[{"content":"Version: 1.6\nSweet Juice is an open-source Go mobile app development framework for building native mobile apps using Go and native Android/iOS components, powered by gomobile.\nFeatures Fast and optimised compile times. Deep Native integrations. Supports cross-platform builds for Android apps on Linux, Windows and macOS. Supports cross-platform builds for iOS apps on Linux, Windows and macOS. GET STARTED WITH TUTORIALS WITH THE BUTTON BELOW\n","permalink":"https://sweetjuice.pages.dev/v1/docs/2.-intro/","summary":"\u003cp\u003e\u003ccode\u003eVersion: 1.6\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSweet Juice\u003c/strong\u003e is an open-source Go mobile app development framework for building native mobile apps using Go and native Android/iOS components, powered by \u003ccode\u003egomobile\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"features\"\u003eFeatures\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eFast and optimised compile times.\u003c/li\u003e\n\u003cli\u003eDeep Native integrations.\u003c/li\u003e\n\u003cli\u003eSupports cross-platform builds for Android apps on \u003cstrong\u003eLinux\u003c/strong\u003e, \u003cstrong\u003eWindows\u003c/strong\u003e and \u003cstrong\u003emacOS\u003c/strong\u003e.\u003c/li\u003e\n\u003cli\u003eSupports cross-platform builds for iOS apps on \u003cstrong\u003eLinux\u003c/strong\u003e, \u003cstrong\u003eWindows\u003c/strong\u003e and \u003cstrong\u003emacOS\u003c/strong\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cfigure\u003e\n    \u003cimg loading=\"lazy\" src=\"/images/first-app.jpg\"\n         alt=\"Preview1\" width=\"200\" height=\"400\"/\u003e \n\u003c/figure\u003e\n\n\u003cfigure\u003e\n    \u003cimg loading=\"lazy\" src=\"/images/first-app.jpg\"\n         alt=\"Preview1\" width=\"200\" height=\"400\"/\u003e \n\u003c/figure\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eGET STARTED WITH TUTORIALS WITH THE BUTTON BELOW\u003c/strong\u003e\u003c/p\u003e","title":"Overview"},{"content":"This document contains the installation, bootstrapping, build and uninstall steps used during development.\nPrerequisites Go 1.24+ Android SDK and Android NDK installed (Android Studio recommended) xtool.sh (For cross-platform iOS builds) git, curl NB: For less mess, do not alter the default location for Android SDK.\nInstallation You can use Go to install Sweet Juice CLI tool, juice.\nUsing Go: go install github.com/sweet-juice/sweetjuice/cmd/juice@latest Create a new project juice --new my-new-app This scaffolds a fresh project from the local AppTemplate included in the Sweet Juice module.\nSweet Juice app structure .plugins/ — Go-side implementations of native plugins native/android/ — Android Studio project using the generated AAR native/ios/ — iOS project using the generated XFramework lib/views/ — Go view definitions using native widgets Run App Run on a connected device: To run on device, ensure Android ADB is configured for Android, or iTunes or libimobiledevice is configured for iOS.\n# For Android (via ADB) juice --run android # For iOS (via xtool) juice --run ios Build release binaries: # Android juice --build android debug juice --build android release juice --build android bundle # iOS juice --build ios debug juice --build ios release To Uninstall Sweet Juice CLI: rm $(go env GOPATH)/bin/juice Notes \u0026amp; troubleshooting Ensure ANDROID_HOME or equivalent environment variables are set or Android Studio is installed in default locations. For iOS, ensures xtool and Xcode Command Line Tools are installed. If bindings seem stale, run juice --refresh \u0026lt;platform\u0026gt; to force a rebuild. ","permalink":"https://sweetjuice.pages.dev/v1/docs/3.-install/","summary":"\u003cp\u003eThis document contains the installation, bootstrapping, build and uninstall steps used during development.\u003c/p\u003e\n\u003ch2 id=\"prerequisites\"\u003ePrerequisites\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGo 1.24+\u003c/li\u003e\n\u003cli\u003eAndroid SDK and Android NDK installed (Android Studio recommended)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003extool.sh\u003c/code\u003e (For cross-platform iOS builds)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003egit\u003c/code\u003e, \u003ccode\u003ecurl\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eNB\u003c/strong\u003e: For less mess, do not alter the default location for Android SDK.\u003c/p\u003e\n\u003ch2 id=\"installation\"\u003eInstallation\u003c/h2\u003e\n\u003cp\u003eYou can use \u003ccode\u003eGo\u003c/code\u003e to install \u003ccode\u003eSweet Juice\u003c/code\u003e CLI tool, \u003ccode\u003ejuice\u003c/code\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUsing \u003ccode\u003eGo\u003c/code\u003e:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ego install github.com/sweet-juice/sweetjuice/cmd/juice@latest\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"create-a-new-project\"\u003eCreate a new project\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejuice --new my-new-app\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis scaffolds a fresh project from the local \u003ccode\u003eAppTemplate\u003c/code\u003e included in the Sweet Juice module.\u003c/p\u003e","title":"Getting started"},{"content":"1. Scaffold juice --new MyApp cd MyApp 2. Define state lib/state/app_state.go\npackage state type MainAppState struct { SelectedTab string } func NewMainAppState() *MainAppState { return \u0026amp;MainAppState{SelectedTab: \u0026#34;feed\u0026#34;} } func (s *MainAppState) SetSelectedTab(tab string) { s.SelectedTab = tab } 3. Build the UI lib/views/home_view.go\npackage views import ( \u0026#34;myapp/lib/state\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/plugins/mu3\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/ui\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/ui/style\u0026#34; ) type HomeView struct { State *state.MainAppState } func (v *HomeView) Render() ui.Node { return ui.Root( ui.VStack( mu3.TopAppBar(\u0026#34;Feed\u0026#34;, \u0026#34;menu\u0026#34;), ui.Spacer().Height(12), mu3.SearchBar(\u0026#34;Search...\u0026#34;, false), ui.Spacer().Height(16), mu3.Tabs([]string{\u0026#34;For You\u0026#34;, \u0026#34;Saved\u0026#34;, \u0026#34;Archived\u0026#34;}, nil, v.State.SelectedTab). OnChanged(v.State.SetSelectedTab), ui.Spacer().Height(16), v.contentForTab(), ui.Spacer().Height(24), mu3.NavigationBar([]map[string]string{ {\u0026#34;label\u0026#34;: \u0026#34;Home\u0026#34;, \u0026#34;icon\u0026#34;: \u0026#34;home\u0026#34;}, {\u0026#34;label\u0026#34;: \u0026#34;Explore\u0026#34;, \u0026#34;icon\u0026#34;: \u0026#34;explore\u0026#34;}, {\u0026#34;label\u0026#34;: \u0026#34;Profile\u0026#34;, \u0026#34;icon\u0026#34;: \u0026#34;person\u0026#34;}, }), ), \u0026#34;#FFFBFE\u0026#34;, ) } func (v *HomeView) contentForTab() ui.Node { switch v.State.SelectedTab { case \u0026#34;saved\u0026#34;: return ui.VStack( mu3.Card(\u0026#34;Saved\u0026#34;, \u0026#34;Item 1\u0026#34;).OnClick(func() { println(\u0026#34;saved\u0026#34;) }), ui.Spacer().Height(12), mu3.Card(\u0026#34;Saved\u0026#34;, \u0026#34;Item 2\u0026#34;).OnClick(func() { println(\u0026#34;saved\u0026#34;) }), ) default: return ui.VStack( mu3.Card(\u0026#34;Feed\u0026#34;, \u0026#34;Welcome to Sweet Juice\u0026#34;).OnClick(func() { println(\u0026#34;feed\u0026#34;) }), ui.Spacer().Height(12), mu3.Card(\u0026#34;Feed\u0026#34;, \u0026#34;Material You is active\u0026#34;).OnClick(func() { println(\u0026#34;feed\u0026#34;) }), ) } } 4. Bootstrap start.go\npackage sweetjuice import ( \u0026#34;myapp/lib/state\u0026#34; \u0026#34;myapp/lib/views\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/app\u0026#34; ) func StartApplication() string { mainState := state.NewMainAppState() root := \u0026amp;views.HomeView{State: mainState} app.Run(root) return `{\u0026#34;status\u0026#34;:\u0026#34;started\u0026#34;}` } 5. Run juice --run android Styling components Any mu3 widget accepts .Style() with a style struct:\nmu3.Card(\u0026#34;Title\u0026#34;, \u0026#34;Subtitle\u0026#34;). Style(style.View{ CornerRadius: 8, Padding: 16, }). OnClick(func() { println(\u0026#34;clicked\u0026#34;) }) Available style structs: style.View, style.Text, style.Button, style.TextButton, style.OutlinedButton, style.TonalButton, style.ElevatedButton, style.IconButton, style.FabStyle, style.SegmentedButton, style.ButtonGroup, style.Image, style.Video, style.TopAppBar, style.BottomAppBar, style.NavigationBar, style.NavigationRail, style.SearchBar, style.Tabs, style.Toolbar.\nEvents mu3.Card(\u0026#34;Tap me\u0026#34;). OnClick(func() { println(\u0026#34;clicked\u0026#34;) }). OnLongPress(func() { println(\u0026#34;long pressed\u0026#34;) }) ","permalink":"https://sweetjuice.pages.dev/v1/guide/5.-first_app/","summary":"\u003ch2 id=\"1-scaffold\"\u003e1. Scaffold\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejuice --new MyApp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd MyApp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"2-define-state\"\u003e2. Define state\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003elib/state/app_state.go\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estate\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eMainAppState\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eSelectedTab\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNewMainAppState\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eMainAppState\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eMainAppState\u003c/span\u003e{\u003cspan style=\"color:#a6e22e\"\u003eSelectedTab\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;feed\u0026#34;\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003es\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eMainAppState\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003eSetSelectedTab\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etab\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003es\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSelectedTab\u003c/span\u003e = \u003cspan style=\"color:#a6e22e\"\u003etab\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"3-build-the-ui\"\u003e3. Build the UI\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003elib/views/home_view.go\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eviews\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;myapp/lib/state\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/plugins/mu3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/ui\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/ui/style\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eHomeView\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eState\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003estate\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eMainAppState\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eHomeView\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003eRender\u003c/span\u003e() \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNode\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRoot\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eVStack\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eTopAppBar\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feed\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;menu\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e12\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSearchBar\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Search...\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eTabs\u003c/span\u003e([]\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;For You\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Saved\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Archived\u0026#34;\u003c/span\u003e}, \u003cspan style=\"color:#66d9ef\"\u003enil\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eState\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSelectedTab\u003c/span\u003e).\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003eOnChanged\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eState\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSetSelectedTab\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003econtentForTab\u003c/span\u003e(),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e24\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNavigationBar\u003c/span\u003e([]\u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e[\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e]\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;label\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Home\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;home\u0026#34;\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;label\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Explore\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;explore\u0026#34;\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                {\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;label\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Profile\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;person\u0026#34;\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;#FFFBFE\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eHomeView\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003econtentForTab\u003c/span\u003e() \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNode\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eswitch\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ev\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eState\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSelectedTab\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;saved\u0026#34;\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eVStack\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCard\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Saved\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Item 1\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eOnClick\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() { println(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;saved\u0026#34;\u003c/span\u003e) }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e12\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCard\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Saved\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Item 2\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eOnClick\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() { println(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;saved\u0026#34;\u003c/span\u003e) }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edefault\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eVStack\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCard\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feed\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Welcome to Sweet Juice\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eOnClick\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() { println(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;feed\u0026#34;\u003c/span\u003e) }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eui\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSpacer\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003eHeight\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e12\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003emu3\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCard\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feed\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Material You is active\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eOnClick\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() { println(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;feed\u0026#34;\u003c/span\u003e) }),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"4-bootstrap\"\u003e4. Bootstrap\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003estart.go\u003c/code\u003e\u003c/p\u003e","title":"Build Your First App"},{"content":"Sweet Juice plugins are first-party bridges between Go and native Android system APIs.\nPlugin guides Permissions Plugin — Check and request runtime permissions. Notification Plugin — Post and cancel system notifications. Calls Plugin — Read device call history. SMS Plugin — Read SMS messages from inbox, sent, and drafts. WorkManager Plugin — Schedule background tasks. Special Permissions Plugin — Accessibility, battery exemption, notification access, and more. Notification Listener Plugin — React to notifications posted or removed by any app. DataDir Plugin — Read/write app internal storage and access standard directories. More plugins are documented as they are stabilized and stress-tested.\nUsing a plugin from Go Import the plugin package and call its methods. Each plugin bridges to native code through core.CallNativePlatform.\nimport \u0026#34;github.com/sweet-juice/sweetjuice/plugins/permission\u0026#34; status, err := permission.RequestMultiple([]string{ \u0026#34;android.permission.READ_CALL_LOG\u0026#34;, \u0026#34;android.permission.READ_SMS\u0026#34;, }) AndroidManifest requirements Calls — READ_CALL_LOG SMS — READ_SMS Notifications — POST_NOTIFICATIONS Camera — CAMERA Biometric — USE_BIOMETRIC, USE_FINGERPRINT, USE_FACE_AUTHENTICATION All files access — MANAGE_EXTERNAL_STORAGE Foreground service — FOREGROUND_SERVICE, FOREGROUND_SERVICE_SPECIAL_USE See Android Manifest Guide for details.\n","permalink":"https://sweetjuice.pages.dev/v1/guide/6.-plugins/","summary":"\u003cp\u003eSweet Juice plugins are first-party bridges between Go and native Android system APIs.\u003c/p\u003e\n\u003ch2 id=\"plugin-guides\"\u003ePlugin guides\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/permissions\"\u003ePermissions Plugin\u003c/a\u003e — Check and request runtime permissions.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/notification\"\u003eNotification Plugin\u003c/a\u003e — Post and cancel system notifications.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/calls\"\u003eCalls Plugin\u003c/a\u003e — Read device call history.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/sms\"\u003eSMS Plugin\u003c/a\u003e — Read SMS messages from inbox, sent, and drafts.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/workmanager\"\u003eWorkManager Plugin\u003c/a\u003e — Schedule background tasks.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/special\"\u003eSpecial Permissions Plugin\u003c/a\u003e — Accessibility, battery exemption, notification access, and more.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/notification_listener\"\u003eNotification Listener Plugin\u003c/a\u003e — React to notifications posted or removed by any app.\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/v1/plugins/datadir\"\u003eDataDir Plugin\u003c/a\u003e — Read/write app internal storage and access standard directories.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMore plugins are documented as they are stabilized and stress-tested.\u003c/p\u003e","title":"Plugins"},{"content":"A Sweet Juice plugin has two parts:\nGo side — exposes a friendly API and calls core.CallNativePlatform to reach native code. Native side — implements SweetJuicePlugin, registers widget factories, and handles actions. Go-side plugin package myplugin import ( \u0026#34;encoding/json\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/core\u0026#34; ) type Plugin struct { app *core.Application } func NewPlugin() *Plugin { return \u0026amp;Plugin{} } func (p *Plugin) Init(app *core.Application) error { p.app = app // Register native callbacks if needed app.RegisterNativeMethod(\u0026#34;myplugin:result\u0026#34;, func(args []json.RawMessage) (interface{}, error) { // handle result from native side return map[string]string{\u0026#34;status\u0026#34;: \u0026#34;ok\u0026#34;}, nil }) return nil } func (p *Plugin) DoSomething(input string) (string, error) { payload, _ := json.Marshal(map[string]string{\u0026#34;input\u0026#34;: input}) return core.CallNativePlatform(\u0026#34;myplugin:doSomething\u0026#34;, payload), nil } Native-side plugin (Android) package com.sweetjuice.pkg.myplugin; import android.content.Context; import com.sweetjuice.plugin.SweetJuicePlugin; import org.json.JSONObject; import org.json.JSONException; public class MyPlugin implements SweetJuicePlugin { private Context mContext; @Override public String getDomain() { return \u0026#34;myplugin\u0026#34;; } @Override public void onAttach(Context context) { this.mContext = context; } @Override public String handleAction(String action, String jsonArgsPayload) { try { JSONObject args = new JSONObject(jsonArgsPayload); if (\u0026#34;doSomething\u0026#34;.equals(action)) { String input = args.optString(\u0026#34;input\u0026#34;, \u0026#34;\u0026#34;); // do native work return \u0026#34;{\\\u0026#34;result\\\u0026#34;:\\\u0026#34;\u0026#34; + input + \u0026#34;\\\u0026#34;}\u0026#34;; } return \u0026#34;{\\\u0026#34;error\\\u0026#34;:\\\u0026#34;Unknown action\\\u0026#34;}\u0026#34;; } catch (JSONException e) { return \u0026#34;{\\\u0026#34;error\\\u0026#34;:\\\u0026#34;\u0026#34; + e.getMessage() + \u0026#34;\\\u0026#34;}\u0026#34;; } } } Register the plugin Add the plugin to SweetJuiceApplication:\nregisterPlugin(new com.sweetjuice.pkg.myplugin.MyPlugin()); Widget factories If your plugin renders UI, implement SweetJuiceWidgetFactory:\npublic class MyWidgetFactory implements SweetJuiceWidgetFactory { @Override public String getType() { return \u0026#34;myplugin:widget\u0026#34;; } @Override public View createView(Context ctx, JSONObject node, ViewGroup parent) { return new TextView(ctx); } @Override public void updateView(View view, JSONObject node) { // update view from node props } } Return the factory from getWidgetFactories() in your plugin.\n","permalink":"https://sweetjuice.pages.dev/v1/guide/7.-write_plugins/","summary":"\u003cp\u003eA Sweet Juice plugin has two parts:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eGo side\u003c/strong\u003e — exposes a friendly API and calls \u003ccode\u003ecore.CallNativePlatform\u003c/code\u003e to reach native code.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNative side\u003c/strong\u003e — implements \u003ccode\u003eSweetJuicePlugin\u003c/code\u003e, registers widget factories, and handles actions.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"go-side-plugin\"\u003eGo-side plugin\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emyplugin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;encoding/json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/core\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePlugin\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eapp\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ecore\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eApplication\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNewPlugin\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlugin\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlugin\u003c/span\u003e{}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlugin\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003eInit\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eapp\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ecore\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eApplication\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eerror\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ep\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eapp\u003c/span\u003e = \u003cspan style=\"color:#a6e22e\"\u003eapp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Register native callbacks if needed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eapp\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRegisterNativeMethod\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;myplugin:result\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eargs\u003c/span\u003e []\u003cspan style=\"color:#a6e22e\"\u003ejson\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRawMessage\u003c/span\u003e) (\u003cspan style=\"color:#66d9ef\"\u003einterface\u003c/span\u003e{}, \u003cspan style=\"color:#66d9ef\"\u003eerror\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// handle result from native side\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e[\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e]\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;status\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ok\u0026#34;\u003c/span\u003e}, \u003cspan style=\"color:#66d9ef\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlugin\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003eDoSomething\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003einput\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e) (\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eerror\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003epayload\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejson\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eMarshal\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e[\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e]\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;input\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#a6e22e\"\u003einput\u003c/span\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecore\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCallNativePlatform\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;myplugin:doSomething\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003epayload\u003c/span\u003e), \u003cspan style=\"color:#66d9ef\"\u003enil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"native-side-plugin-android\"\u003eNative-side plugin (Android)\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e com.sweetjuice.pkg.myplugin;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e android.content.Context;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e com.sweetjuice.plugin.SweetJuicePlugin;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e org.json.JSONObject;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e org.json.JSONException;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eMyPlugin\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eimplements\u003c/span\u003e SweetJuicePlugin {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e Context mContext;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e String \u003cspan style=\"color:#a6e22e\"\u003egetDomain\u003c/span\u003e() { \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;myplugin\u0026#34;\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eonAttach\u003c/span\u003e(Context context) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emContext\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e context;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e String \u003cspan style=\"color:#a6e22e\"\u003ehandleAction\u003c/span\u003e(String action, String jsonArgsPayload) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            JSONObject args \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JSONObject(jsonArgsPayload);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;doSomething\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eequals\u003c/span\u003e(action)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                String input \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e args.\u003cspan style=\"color:#a6e22e\"\u003eoptString\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;input\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#75715e\"\u003e// do native work\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{\\\u0026#34;result\\\u0026#34;:\\\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e input \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\\u0026#34;}\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{\\\u0026#34;error\\\u0026#34;:\\\u0026#34;Unknown action\\\u0026#34;}\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (JSONException e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{\\\u0026#34;error\\\u0026#34;:\\\u0026#34;\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e e.\u003cspan style=\"color:#a6e22e\"\u003egetMessage\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\\u0026#34;}\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"register-the-plugin\"\u003eRegister the plugin\u003c/h2\u003e\n\u003cp\u003eAdd the plugin to \u003ccode\u003eSweetJuiceApplication\u003c/code\u003e:\u003c/p\u003e","title":"Writing Plugins"},{"content":"The Notification plugin lets your app post and cancel system notifications on Android.\nSetup The notification plugin is already registered in the default AppTemplate.\nAndroid Manifest \u0026lt;uses-permission android:name=\u0026#34;android.permission.POST_NOTIFICATIONS\u0026#34; /\u0026gt; API notification.NewPlugin() *NotificationPlugin Creates a new notification plugin instance.\n(*NotificationPlugin).Post(n Notification) (string, error) Posts a system notification.\nReturns: A result string from the native layer, typically {\u0026quot;status\u0026quot;:\u0026quot;posted\u0026quot;} or similar.\n(*NotificationPlugin).Cancel(id int) string Cancels a previously posted notification by its ID.\nTypes Notification type Notification struct { ID int `json:\u0026#34;id\u0026#34;` Title string `json:\u0026#34;title\u0026#34;` Body string `json:\u0026#34;body\u0026#34;` ChannelID string `json:\u0026#34;channel_id\u0026#34;` ChannelName string `json:\u0026#34;channel_name\u0026#34;` Importance Importance `json:\u0026#34;importance\u0026#34;` } Field Description ID Notification ID. If 0, a unique ID is generated automatically Title Notification title text Body Notification body text ChannelID Android notification channel ID. Defaults to default_channel ChannelName Human-readable channel name. Defaults to General Notifications Importance Notification priority level Importance type Importance string const ( ImportanceDefault Importance = \u0026#34;DEFAULT\u0026#34; ImportanceHigh Importance = \u0026#34;HIGH\u0026#34; ImportanceLow Importance = \u0026#34;LOW\u0026#34; ImportanceMin Importance = \u0026#34;MIN\u0026#34; ) Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/notification\u0026#34; plugin := notification.NewPlugin() _, err := plugin.Post(notification.Notification{ Title: \u0026#34;Sweet Juice\u0026#34;, Body: \u0026#34;Background sync running...\u0026#34;, ChannelID: \u0026#34;default_channel\u0026#34;, ChannelName: \u0026#34;General Notifications\u0026#34;, Importance: notification.ImportanceDefault, }) // Cancel a notification by ID plugin.Cancel(12345) Notes On Android 13+ (API 33), the user must grant POST_NOTIFICATIONS runtime permission before notifications can be posted. The notification plugin uses a default channel if none is specified. Custom channels should be created natively if you need different behavior. ","permalink":"https://sweetjuice.pages.dev/v1/plugins/8.-notification/","summary":"\u003cp\u003eThe \u003cstrong\u003eNotification\u003c/strong\u003e plugin lets your app post and cancel system notifications on Android.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe notification plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"android-manifest\"\u003eAndroid Manifest\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;uses-permission\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.POST_NOTIFICATIONS\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"notificationnewplugin-notificationplugin\"\u003e\u003ccode\u003enotification.NewPlugin() *NotificationPlugin\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eCreates a new notification plugin instance.\u003c/p\u003e\n\u003ch3 id=\"notificationpluginpostn-notification-string-error\"\u003e\u003ccode\u003e(*NotificationPlugin).Post(n Notification) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003ePosts a system notification.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReturns:\u003c/strong\u003e A result string from the native layer, typically \u003ccode\u003e{\u0026quot;status\u0026quot;:\u0026quot;posted\u0026quot;}\u003c/code\u003e or similar.\u003c/p\u003e\n\u003ch3 id=\"notificationplugincancelid-int-string\"\u003e\u003ccode\u003e(*NotificationPlugin).Cancel(id int) string\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eCancels a previously posted notification by its ID.\u003c/p\u003e\n\u003ch2 id=\"types\"\u003eTypes\u003c/h2\u003e\n\u003ch3 id=\"notification\"\u003e\u003ccode\u003eNotification\u003c/code\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNotification\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eID\u003c/span\u003e          \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e        \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;id\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eTitle\u003c/span\u003e       \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e     \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;title\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eBody\u003c/span\u003e        \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e     \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;body\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eChannelID\u003c/span\u003e   \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e     \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;channel_id\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eChannelName\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e     \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;channel_name\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e  \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;importance\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eField\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eID\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNotification ID. If \u003ccode\u003e0\u003c/code\u003e, a unique ID is generated automatically\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eTitle\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNotification title text\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eBody\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNotification body text\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eChannelID\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid notification channel ID. Defaults to \u003ccode\u003edefault_channel\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eChannelName\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eHuman-readable channel name. Defaults to \u003ccode\u003eGeneral Notifications\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eImportance\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNotification priority level\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"importance\"\u003e\u003ccode\u003eImportance\u003c/code\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportanceDefault\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;DEFAULT\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportanceHigh\u003c/span\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;HIGH\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportanceLow\u003c/span\u003e     \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;LOW\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportanceMin\u003c/span\u003e     \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;MIN\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"example\"\u003eExample\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/plugins/notification\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eplugin\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enotification\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNewPlugin\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e_\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eerr\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplugin\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePost\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enotification\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNotification\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eTitle\u003c/span\u003e:       \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Sweet Juice\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eBody\u003c/span\u003e:        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Background sync running...\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eChannelID\u003c/span\u003e:   \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;default_channel\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eChannelName\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;General Notifications\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eImportance\u003c/span\u003e:  \u003cspan style=\"color:#a6e22e\"\u003enotification\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eImportanceDefault\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Cancel a notification by ID\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eplugin\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCancel\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e12345\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"notes\"\u003eNotes\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eOn Android 13+ (\u003ccode\u003eAPI 33\u003c/code\u003e), the user must grant \u003ccode\u003ePOST_NOTIFICATIONS\u003c/code\u003e runtime permission before notifications can be posted.\u003c/li\u003e\n\u003cli\u003eThe notification plugin uses a default channel if none is specified. Custom channels should be created natively if you need different behavior.\u003c/li\u003e\n\u003c/ul\u003e","title":"Notification Plugin"},{"content":"Android plugins live under:\nplugins/\u0026lt;name\u0026gt;/android/com/sweetjuice/pkg/\u0026lt;name\u0026gt;/ And are synced into the template at:\nAppTemplate/native/android/app/src/main/java/com/sweetjuice/pkg/\u0026lt;name\u0026gt;/ Plugin contract Every plugin must implement SweetJuicePlugin:\npublic class MyPlugin implements SweetJuicePlugin { @Override public String getDomain() { return \u0026#34;mydomain\u0026#34;; } @Override public void onAttach(Context context) { } @Override public String handleAction(String action, String jsonArgsPayload) { // route action to handler return \u0026#34;{}\u0026#34;; } } Action routing handleAction receives the action name and a JSON payload. Use it to route to specific handlers:\n@Override public String handleAction(String action, String jsonArgsPayload) { try { JSONObject args = new JSONObject(jsonArgsPayload); switch (action) { case \u0026#34;check\u0026#34;: return check(args); case \u0026#34;request\u0026#34;: return request(args); default: return errorJson(\u0026#34;Unknown action\u0026#34;); } } catch (JSONException e) { return errorJson(e.getMessage()); } } Returning results Return JSON strings. The Go side receives the raw string and unmarshals it:\nprivate String check(JSONObject args) throws JSONException { String permission = args.optString(\u0026#34;permission\u0026#34;, \u0026#34;\u0026#34;); // ... JSONObject result = new JSONObject(); result.put(\u0026#34;status\u0026#34;, \u0026#34;granted\u0026#34;); return result.toString(); } ","permalink":"https://sweetjuice.pages.dev/v1/guide/8.-write_android_plugins/","summary":"\u003cp\u003eAndroid plugins live under:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eplugins/\u0026lt;name\u0026gt;/android/com/sweetjuice/pkg/\u0026lt;name\u0026gt;/\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd are synced into the template at:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eAppTemplate/native/android/app/src/main/java/com/sweetjuice/pkg/\u0026lt;name\u0026gt;/\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"plugin-contract\"\u003ePlugin contract\u003c/h2\u003e\n\u003cp\u003eEvery plugin must implement \u003ccode\u003eSweetJuicePlugin\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eMyPlugin\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eimplements\u003c/span\u003e SweetJuicePlugin {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e String \u003cspan style=\"color:#a6e22e\"\u003egetDomain\u003c/span\u003e() { \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;mydomain\u0026#34;\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eonAttach\u003c/span\u003e(Context context) { }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e String \u003cspan style=\"color:#a6e22e\"\u003ehandleAction\u003c/span\u003e(String action, String jsonArgsPayload) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// route action to handler\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{}\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"action-routing\"\u003eAction routing\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003ehandleAction\u003c/code\u003e receives the action name and a JSON payload. Use it to route to specific handlers:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e String \u003cspan style=\"color:#a6e22e\"\u003ehandleAction\u003c/span\u003e(String action, String jsonArgsPayload) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        JSONObject args \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JSONObject(jsonArgsPayload);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eswitch\u003c/span\u003e (action) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;check\u0026#34;\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e check(args);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;request\u0026#34;\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e request(args);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003edefault\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e errorJson(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Unknown action\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    } \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (JSONException e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e errorJson(e.\u003cspan style=\"color:#a6e22e\"\u003egetMessage\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"returning-results\"\u003eReturning results\u003c/h2\u003e\n\u003cp\u003eReturn JSON strings. The Go side receives the raw string and unmarshals it:\u003c/p\u003e","title":"Writing Android Plugins"},{"content":"The Calls plugin provides read-only access to the device call log.\nSetup The calls plugin is already registered in the default AppTemplate.\nAndroid Manifest \u0026lt;uses-permission android:name=\u0026#34;android.permission.READ_CALL_LOG\u0026#34; /\u0026gt; API calls.GetRecent(limit int) (CallLog, error) Fetches the most recent call log entries.\nReturns: CallLog containing Calls and Count\ncalls.GetLast(limit int) (CallLog, error) An alias for GetRecent(limit).\ncalls.GetAll() (CallLog, error) Fetches the entire call log.\nReturns: CallLog or error\nTypes CallLog type CallLog struct { Calls []CallRecord `json:\u0026#34;calls\u0026#34;` Count int `json:\u0026#34;count\u0026#34;` } CallRecord Field Type Description ID int64 Stable row ID Number string Phone number or URI Type string \u0026quot;incoming\u0026quot;, \u0026quot;outgoing\u0026quot;, \u0026quot;missed\u0026quot;, etc. Date int64 Epoch millis Duration int64 Duration in seconds CachedName string Contact name if available GeoLocation string Reverse-geocoded location if available Events calls:changed Emitted when the native side detects a call-log change.\nExample import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/calls\u0026#34; plugin := calls.NewPlugin() log, err := plugin.GetAll() ","permalink":"https://sweetjuice.pages.dev/v1/plugins/9.-calls/","summary":"\u003cp\u003eThe \u003cstrong\u003eCalls\u003c/strong\u003e plugin provides read-only access to the device call log.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe calls plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"android-manifest\"\u003eAndroid Manifest\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;uses-permission\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.READ_CALL_LOG\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"callsgetrecentlimit-int-calllog-error\"\u003e\u003ccode\u003ecalls.GetRecent(limit int) (CallLog, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eFetches the most recent call log entries.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReturns:\u003c/strong\u003e \u003ccode\u003eCallLog\u003c/code\u003e containing \u003ccode\u003eCalls\u003c/code\u003e and \u003ccode\u003eCount\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"callsgetlastlimit-int-calllog-error\"\u003e\u003ccode\u003ecalls.GetLast(limit int) (CallLog, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eAn alias for \u003ccode\u003eGetRecent(limit)\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"callsgetall-calllog-error\"\u003e\u003ccode\u003ecalls.GetAll() (CallLog, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eFetches the entire call log.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReturns:\u003c/strong\u003e \u003ccode\u003eCallLog\u003c/code\u003e or error\u003c/p\u003e\n\u003ch2 id=\"types\"\u003eTypes\u003c/h2\u003e\n\u003ch3 id=\"calllog\"\u003e\u003ccode\u003eCallLog\u003c/code\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eCallLog\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eCalls\u003c/span\u003e []\u003cspan style=\"color:#a6e22e\"\u003eCallRecord\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;calls\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eCount\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e          \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;count\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"callrecord\"\u003e\u003ccode\u003eCallRecord\u003c/code\u003e\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eField\u003c/th\u003e\n          \u003cth\u003eType\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eID\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eint64\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eStable row ID\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eNumber\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estring\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePhone number or URI\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eType\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estring\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026quot;incoming\u0026quot;\u003c/code\u003e, \u003ccode\u003e\u0026quot;outgoing\u0026quot;\u003c/code\u003e, \u003ccode\u003e\u0026quot;missed\u0026quot;\u003c/code\u003e, etc.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eDate\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eint64\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEpoch millis\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eDuration\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eint64\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDuration in seconds\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCachedName\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estring\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eContact name if available\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eGeoLocation\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estring\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eReverse-geocoded location if available\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"events\"\u003eEvents\u003c/h2\u003e\n\u003ch3 id=\"callschanged\"\u003e\u003ccode\u003ecalls:changed\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eEmitted when the native side detects a call-log change.\u003c/p\u003e","title":"Calls Plugin"},{"content":"The Permissions plugin provides a thin, reliable bridge between Go and the Android runtime permission system.\nSetup The permissions plugin is already registered in the default AppTemplate.\nAPI permission.Check(permission string) (string, error) Returns the current status of a single permission.\npermission.Request(permission string) (string, error) Shows the system permission dialog.\npermission.RequestMultiple(permissions []string) (string, error) Requests multiple permissions sequentially.\nEvents permissions:changed Emitted each time a permission request completes.\n{ \u0026#34;permission\u0026#34;: \u0026#34;android.permission.READ_CALL_LOG\u0026#34;, \u0026#34;granted\u0026#34;: true } Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/permission\u0026#34; status, _ := permission.RequestMultiple([]string{ \u0026#34;android.permission.READ_CALL_LOG\u0026#34;, \u0026#34;android.permission.READ_SMS\u0026#34;, }) ","permalink":"https://sweetjuice.pages.dev/v1/plugins/10.-permissions/","summary":"\u003cp\u003eThe \u003cstrong\u003ePermissions\u003c/strong\u003e plugin provides a thin, reliable bridge between Go and the Android runtime permission system.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe permissions plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"permissioncheckpermission-string-string-error\"\u003e\u003ccode\u003epermission.Check(permission string) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eReturns the current status of a single permission.\u003c/p\u003e\n\u003ch3 id=\"permissionrequestpermission-string-string-error\"\u003e\u003ccode\u003epermission.Request(permission string) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eShows the system permission dialog.\u003c/p\u003e\n\u003ch3 id=\"permissionrequestmultiplepermissions-string-string-error\"\u003e\u003ccode\u003epermission.RequestMultiple(permissions []string) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eRequests multiple permissions sequentially.\u003c/p\u003e\n\u003ch2 id=\"events\"\u003eEvents\u003c/h2\u003e\n\u003ch3 id=\"permissionschanged\"\u003e\u003ccode\u003epermissions:changed\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eEmitted each time a permission request completes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;permission\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.READ_CALL_LOG\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;granted\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"example\"\u003eExample\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/plugins/permission\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003estatus\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003epermission\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRequestMultiple\u003c/span\u003e([]\u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.READ_CALL_LOG\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.READ_SMS\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Permissions Plugin"},{"content":"The SMS plugin provides read-only access to device SMS messages across inbox, sent, and draft folders.\nSetup The SMS plugin is already registered in the default AppTemplate.\nAndroid Manifest \u0026lt;uses-permission android:name=\u0026#34;android.permission.READ_SMS\u0026#34; /\u0026gt; Note: On Android 4.4+ (KITKAT), this plugin can only read SMS messages if your app is the default SMS app.\nAPI sms.GetRecent(limit int) (SmsFolder, error) Fetches the most recent SMS messages across all folders.\nReturns: SmsFolder or error\nsms.GetLast(limit int) (SmsFolder, error) An alias for GetRecent(limit).\nsms.GetInbox() (SmsFolder, error) Fetches messages from the inbox.\nReturns: SmsFolder or error\nsms.GetSent() (SmsFolder, error) Fetches messages from the sent folder.\nReturns: SmsFolder or error\nsms.GetDrafts() (SmsFolder, error) Fetches messages from the drafts folder.\nReturns: SmsFolder or error\nsms.GetAll() (SmsFolder, error) Fetches all messages from inbox, sent, and draft folders.\nReturns: SmsFolder or error\nTypes SmsFolder type SmsFolder struct { Messages []SmsMessage `json:\u0026#34;messages\u0026#34;` Count int `json:\u0026#34;count\u0026#34;` Folder string `json:\u0026#34;folder\u0026#34;` } SmsMessage Field Type Description ID int64 Stable row ID Address string Sender or recipient address Body string Message text Type string \u0026quot;inbox\u0026quot;, \u0026quot;sent\u0026quot;, or \u0026quot;draft\u0026quot; Timestamp int64 Epoch millis Read bool Whether the message has been read Events sms:changed Emitted when the native side detects an SMS change.\nExample import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/sms\u0026#34; plugin := sms.NewPlugin() folder, err := plugin.GetAll() ","permalink":"https://sweetjuice.pages.dev/v1/plugins/11.-sms/","summary":"\u003cp\u003eThe \u003cstrong\u003eSMS\u003c/strong\u003e plugin provides read-only access to device SMS messages across inbox, sent, and draft folders.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe SMS plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"android-manifest\"\u003eAndroid Manifest\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;uses-permission\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.READ_SMS\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e On Android 4.4+ (\u003ccode\u003eKITKAT\u003c/code\u003e), this plugin can only read SMS messages if your app is the \u003cstrong\u003edefault SMS app\u003c/strong\u003e.\u003c/p\u003e\u003c/blockquote\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"smsgetrecentlimit-int-smsfolder-error\"\u003e\u003ccode\u003esms.GetRecent(limit int) (SmsFolder, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eFetches the most recent SMS messages across all folders.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReturns:\u003c/strong\u003e \u003ccode\u003eSmsFolder\u003c/code\u003e or error\u003c/p\u003e","title":"SMS Plugin"},{"content":"The WorkManager plugin manages background tasks via the Android WorkManager API, supporting constraint-based one-time and periodic scheduling.\nSetup The WorkManager plugin is already registered in the default AppTemplate.\nAndroid Manifest \u0026lt;uses-permission android:name=\u0026#34;android.permission.FOREGROUND_SERVICE\u0026#34; /\u0026gt; \u0026lt;uses-permission android:name=\u0026#34;android.permission.FOREGROUND_SERVICE_SPECIAL_USE\u0026#34; /\u0026gt; API workmanager.NewPlugin() *WorkManagerPlugin Creates a new WorkManager plugin instance.\n(*WorkManagerPlugin).RegisterTask(name string, fn TaskFunc) Registers a task function that can be executed by WorkManager.\ntype TaskFunc func() error (*WorkManagerPlugin).EnqueueOneTime(taskKey string, constraints *Constraints) (string, error) Schedules a one-time background task.\n(*WorkManagerPlugin).EnqueuePeriodic(taskKey string, intervalMinutes int, constraints *Constraints, replaceExisting bool, runImmediate bool) (string, error) Schedules a repeating background task using enqueueUniquePeriodicWork.\nArgument Type Description taskKey string Unique task identifier used as the WorkManager unique name intervalMinutes int Repeat interval in minutes (minimum 15) constraints *Constraints Execution constraints replaceExisting bool If true, replaces any existing periodic work with the same taskKey (REPLACE). If false, keeps the existing work (KEEP) runImmediate bool If true, also enqueues a one-time execution of the task immediately, in addition to the periodic schedule (*WorkManagerPlugin).IsEnqueued(taskKey string) (bool, error) Checks if a task is currently scheduled.\n(*WorkManagerPlugin).CancelAll() string Cancels all scheduled tasks.\nworkmanager.DefaultConstraints() Constraints Returns default constraints (network not required).\nConstraints type Constraints struct { NetworkType string // CONNECTED, UNMETERED, NOT_ROAMING, NOT_REQUIRED RequiresCharging bool RequiresDeviceIdle bool RequiresBatteryNotLow bool RequiresStorageNotLow bool } Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/workmanager\u0026#34; // Register a background task workmanager.NewPlugin().RegisterTask(\u0026#34;sync_data\u0026#34;, func() error { // Your background logic here return nil }) // Schedule it to run every 15 minutes, keeping any existing work workmanager.NewPlugin().EnqueuePeriodic(\u0026#34;sync_data\u0026#34;, 15, nil, false, false) // Schedule it to run every 15 minutes, replacing existing work, and run immediately workmanager.NewPlugin().EnqueuePeriodic(\u0026#34;sync_data\u0026#34;, 15, nil, true, true) Events workmanager:execute Fired when a background task executes. Payload includes task key and input data.\nworkmanager:result Fired when a task completes. Payload:\n{ \u0026#34;task_key\u0026#34;: \u0026#34;sync_data\u0026#34;, \u0026#34;result\u0026#34;: \u0026#34;success\u0026#34;, \u0026#34;error\u0026#34;: \u0026#34;\u0026#34; } Notes Android only. iOS uses BGTaskScheduler instead. Tasks must be registered before they can be enqueued. Tasks run even if the app is killed (system-managed). ","permalink":"https://sweetjuice.pages.dev/v1/plugins/12.-workmanager/","summary":"\u003cp\u003eThe \u003cstrong\u003eWorkManager\u003c/strong\u003e plugin manages background tasks via the Android WorkManager API, supporting constraint-based one-time and periodic scheduling.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe WorkManager plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"android-manifest\"\u003eAndroid Manifest\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;uses-permission\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.FOREGROUND_SERVICE\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;uses-permission\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.FOREGROUND_SERVICE_SPECIAL_USE\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"workmanagernewplugin-workmanagerplugin\"\u003e\u003ccode\u003eworkmanager.NewPlugin() *WorkManagerPlugin\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eCreates a new WorkManager plugin instance.\u003c/p\u003e\n\u003ch3 id=\"workmanagerpluginregistertaskname-string-fn-taskfunc\"\u003e\u003ccode\u003e(*WorkManagerPlugin).RegisterTask(name string, fn TaskFunc)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eRegisters a task function that can be executed by WorkManager.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTaskFunc\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() \u003cspan style=\"color:#66d9ef\"\u003eerror\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"workmanagerpluginenqueueonetimetaskkey-string-constraints-constraints-string-error\"\u003e\u003ccode\u003e(*WorkManagerPlugin).EnqueueOneTime(taskKey string, constraints *Constraints) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eSchedules a one-time background task.\u003c/p\u003e\n\u003ch3 id=\"workmanagerpluginenqueueperiodictaskkey-string-intervalminutes-int-constraints-constraints-replaceexisting-bool-runimmediate-bool-string-error\"\u003e\u003ccode\u003e(*WorkManagerPlugin).EnqueuePeriodic(taskKey string, intervalMinutes int, constraints *Constraints, replaceExisting bool, runImmediate bool) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eSchedules a repeating background task using \u003ccode\u003eenqueueUniquePeriodicWork\u003c/code\u003e.\u003c/p\u003e","title":"WorkManager Plugin"},{"content":"The Special Permissions plugin handles Android permissions that cannot be requested through normal runtime dialogs. These require the user to manually enable them in system settings.\nSetup The special permissions plugin is already registered in the default AppTemplate.\nAPI special.Request(perm SpecialType) (string, error) Opens the system settings screen for the given permission type.\nspecial.Check(perm SpecialType) (bool, error) Checks whether the given special permission is currently granted.\nSpecial Permission Types Type Settings Screen Android Version Accessibility Settings.ACTION_ACCESSIBILITY_SETTINGS All AllFilesAccess Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION 11+ BatteryExemption Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZERS 6.0+ NotificationAccess Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS All Convenience Helpers Function Description RequestAccessibility() (string, error) Opens Accessibility settings RequestAllFilesAccess() (string, error) Opens All Files Access settings (Android 11+) RequestBatteryExemption() (string, error) Opens battery optimization exemption dialog RequestNotificationAccess() (string, error) Opens notification access settings RequestAppSettings() (string, error) Opens app-specific system settings CheckAccessibility() (bool, error) Checks if Accessibility is granted CheckAllFilesAccess() (bool, error) Checks if All Files Access is granted CheckBatteryExemption() (bool, error) Checks if battery exemption is granted CheckNotificationAccess() (bool, error) Checks if notification access is granted Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/special\u0026#34; // Request battery exemption special.RequestBatteryExemption() // Request notification access special.RequestNotificationAccess() // Check if notification access is granted granted, _ := special.CheckNotificationAccess() Android Notes These permissions cannot be requested via requestPermissions(). The user must manually enable them in the system settings screen. AllFilesAccess requires Android 11 (API 30) or higher. NotificationAccess requires a NotificationListenerService declared in the app manifest with the BIND_NOTIFICATION_LISTENER_SERVICE permission. ","permalink":"https://sweetjuice.pages.dev/v1/plugins/13.-special/","summary":"\u003cp\u003eThe \u003cstrong\u003eSpecial Permissions\u003c/strong\u003e plugin handles Android permissions that cannot be requested through normal runtime dialogs. These require the user to manually enable them in system settings.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe special permissions plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"specialrequestperm-specialtype-string-error\"\u003e\u003ccode\u003especial.Request(perm SpecialType) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eOpens the system settings screen for the given permission type.\u003c/p\u003e\n\u003ch3 id=\"specialcheckperm-specialtype-bool-error\"\u003e\u003ccode\u003especial.Check(perm SpecialType) (bool, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eChecks whether the given special permission is currently granted.\u003c/p\u003e\n\u003ch2 id=\"special-permission-types\"\u003eSpecial Permission Types\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eType\u003c/th\u003e\n          \u003cth\u003eSettings Screen\u003c/th\u003e\n          \u003cth\u003eAndroid Version\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eAccessibility\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eSettings.ACTION_ACCESSIBILITY_SETTINGS\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eAllFilesAccess\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eSettings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e11+\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eBatteryExemption\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eSettings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZERS\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e6.0+\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eNotificationAccess\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eSettings.ACTION_NOTIFICATION_LISTENER_SETTINGS\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"convenience-helpers\"\u003eConvenience Helpers\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFunction\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eRequestAccessibility() (string, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOpens Accessibility settings\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eRequestAllFilesAccess() (string, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOpens All Files Access settings (Android 11+)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eRequestBatteryExemption() (string, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOpens battery optimization exemption dialog\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eRequestNotificationAccess() (string, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOpens notification access settings\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eRequestAppSettings() (string, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOpens app-specific system settings\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCheckAccessibility() (bool, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eChecks if Accessibility is granted\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCheckAllFilesAccess() (bool, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eChecks if All Files Access is granted\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCheckBatteryExemption() (bool, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eChecks if battery exemption is granted\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eCheckNotificationAccess() (bool, error)\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eChecks if notification access is granted\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"example\"\u003eExample\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/plugins/special\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Request battery exemption\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003especial\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRequestBatteryExemption\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Request notification access\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003especial\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eRequestNotificationAccess\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Check if notification access is granted\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003egranted\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003especial\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eCheckNotificationAccess\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"android-notes\"\u003eAndroid Notes\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eThese permissions \u003cstrong\u003ecannot\u003c/strong\u003e be requested via \u003ccode\u003erequestPermissions()\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eThe user must manually enable them in the system settings screen.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eAllFilesAccess\u003c/code\u003e requires Android 11 (API 30) or higher.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eNotificationAccess\u003c/code\u003e requires a \u003ccode\u003eNotificationListenerService\u003c/code\u003e declared in the app manifest with the \u003ccode\u003eBIND_NOTIFICATION_LISTENER_SERVICE\u003c/code\u003e permission.\u003c/li\u003e\n\u003c/ul\u003e","title":"Special Permissions Plugin"},{"content":"The Notification Listener plugin receives callbacks when notifications are posted or removed on the device. It lets your Go code react to incoming notifications from any app.\nSetup The notification listener plugin is already registered in the default AppTemplate.\nAndroid Manifest The app must declare a NotificationListenerService and the user must grant notification access in system settings.\n\u0026lt;service android:name=\u0026#34;com.sweetjuice.pkg.notifications.SweetJuiceNotificationListener\u0026#34; android:label=\u0026#34;@string/app_name\u0026#34; android:permission=\u0026#34;android.permission.BIND_NOTIFICATION_LISTENER_SERVICE\u0026#34; android:exported=\u0026#34;false\u0026#34;\u0026gt; \u0026lt;intent-filter\u0026gt; \u0026lt;action android:name=\u0026#34;android.service.notification.NotificationListenerService\u0026#34; /\u0026gt; \u0026lt;/intent-filter\u0026gt; \u0026lt;/service\u0026gt; Use the Special Permissions plugin to open the notification access settings screen:\nimport \u0026#34;github.com/sweet-juice/sweetjuice/plugins/special\u0026#34; special.RequestNotificationAccess() API notification_listener.NewPlugin() *NotificationListenerPlugin Creates a new notification listener plugin instance.\n(*NotificationListenerPlugin).OnPosted(key string, handler NotificationHandler) Registers a handler that is called when a notification is posted.\n(*NotificationListenerPlugin).OnRemoved(key string, handler NotificationHandler) Registers a handler that is called when a notification is removed.\n(*NotificationListenerPlugin).OnGranted(key string, handler GrantedHandler) Registers a handler that is called when notification access is granted by the user.\ntype GrantedHandler func() Types Notification type Notification struct { PackageName string `json:\u0026#34;package_name\u0026#34;` ID int `json:\u0026#34;id\u0026#34;` Title string `json:\u0026#34;title,omitempty\u0026#34;` Text string `json:\u0026#34;text,omitempty\u0026#34;` IsOngoing bool `json:\u0026#34;is_ongoing\u0026#34;` Timestamp int64 `json:\u0026#34;timestamp\u0026#34;` } NotificationHandler type NotificationHandler func(Notification) error GrantedHandler type GrantedHandler func() Example import ( \u0026#34;fmt\u0026#34; \u0026#34;github.com/sweet-juice/sweetjuice/plugins/notification_listener\u0026#34; ) plugin := notification_listener.NewPlugin() plugin.OnPosted(\u0026#34;default\u0026#34;, func(n notification_listener.Notification) error { fmt.Printf(\u0026#34;Notification from %s: %s - %s\\n\u0026#34;, n.PackageName, n.Title, n.Text) return nil }) plugin.OnRemoved(\u0026#34;default\u0026#34;, func(n notification_listener.Notification) error { fmt.Printf(\u0026#34;Notification removed from %s\\n\u0026#34;, n.PackageName) return nil }) plugin.OnGranted(\u0026#34;default\u0026#34;, func() { fmt.Println(\u0026#34;Notification access granted\u0026#34;) }) Events notification-listener:posted Fired when a notification is posted. Payload:\n{ \u0026#34;package_name\u0026#34;: \u0026#34;com.example.app\u0026#34;, \u0026#34;id\u0026#34;: 42, \u0026#34;title\u0026#34;: \u0026#34;Hello\u0026#34;, \u0026#34;text\u0026#34;: \u0026#34;World\u0026#34;, \u0026#34;is_ongoing\u0026#34;: false, \u0026#34;timestamp\u0026#34;: 1722345678901 } notification-listener:removed Fired when a notification is removed. Payload has the same shape as posted.\nnotification-listener:granted Fired when notification access is granted. No payload required.\nAndroid Notes The user must grant Notification access in system settings before the listener receives events. The listener runs even when the app UI is not visible. Title and Text are extracted from Notification.EXTRA_TITLE and Notification.EXTRA_TEXT when available. Some notifications may not expose text if the posting app suppresses it. ","permalink":"https://sweetjuice.pages.dev/v1/plugins/14.-notification_listener/","summary":"\u003cp\u003eThe \u003cstrong\u003eNotification Listener\u003c/strong\u003e plugin receives callbacks when notifications are posted or removed on the device. It lets your Go code react to incoming notifications from any app.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe notification listener plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"android-manifest\"\u003eAndroid Manifest\u003c/h3\u003e\n\u003cp\u003eThe app must declare a \u003ccode\u003eNotificationListenerService\u003c/code\u003e and the user must grant notification access in system settings.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;com.sweetjuice.pkg.notifications.SweetJuiceNotificationListener\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eandroid:label=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@string/app_name\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eandroid:permission=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.permission.BIND_NOTIFICATION_LISTENER_SERVICE\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eandroid:exported=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;intent-filter\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;action\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eandroid:name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;android.service.notification.NotificationListenerService\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/intent-filter\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/service\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUse the \u003cstrong\u003eSpecial Permissions\u003c/strong\u003e plugin to open the notification access settings screen:\u003c/p\u003e","title":"Notification Listener Plugin"},{"content":"The DataDir plugin provides access to standard Android application directories and supports reading and writing files within internal storage.\nSetup The datadir plugin is already registered in the default AppTemplate.\nAPI datadir.NewPlugin() *DataDirPlugin Creates a new datadir plugin instance.\n(*DataDirPlugin).GetDirs() (AppDirs, error) Returns the application directory paths.\ntype AppDirs struct { Files string // Internal files directory Cache string // Internal cache directory ExternalFiles string // External files directory (if available) ExternalCache string // External cache directory (if available) } (*DataDirPlugin).ReadFile(path string) (string, error) Reads the contents of a file from the internal files directory.\nArgument Type Description path string Relative path inside internal files directory Returns: File content as string, or error\n(*DataDirPlugin).WriteFile(path string, content string) error Writes content to a file in the internal files directory. Creates parent directories if needed.\nArgument Type Description path string Relative path inside internal files directory content string Text content to write Returns: error\n(*DataDirPlugin).FileExists(path string) (bool, error) Checks whether a file exists in the internal files directory.\nArgument Type Description path string Relative path inside internal files directory Returns: (exists bool, err error)\n(*DataDirPlugin).DeleteFile(path string) error Deletes a file from the internal files directory.\nArgument Type Description path string Relative path inside internal files directory Returns: error\nExample import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/datadir\u0026#34; plugin := datadir.NewPlugin() // Get directory paths dirs, _ := plugin.GetDirs() fmt.Println(\u0026#34;Files dir:\u0026#34;, dirs.Files) // Write a file plugin.WriteFile(\u0026#34;notes.txt\u0026#34;, \u0026#34;Hello Sweet Juice\u0026#34;) // Read it back content, _ := plugin.ReadFile(\u0026#34;notes.txt\u0026#34;) fmt.Println(\u0026#34;File content:\u0026#34;, content) // Check existence exists, _ := plugin.FileExists(\u0026#34;notes.txt\u0026#34;) fmt.Println(\u0026#34;Exists:\u0026#34;, exists) // Delete it plugin.DeleteFile(\u0026#34;notes.txt\u0026#34;) Notes All file operations use the app\u0026rsquo;s private internal files directory (getFilesDir()). External directories are available via GetDirs(), but file read/write methods operate on internal storage only. Parent directories are created automatically when writing files. No extra permissions are required for internal storage operations. ","permalink":"https://sweetjuice.pages.dev/v1/plugins/15.-datadir/","summary":"\u003cp\u003eThe \u003cstrong\u003eDataDir\u003c/strong\u003e plugin provides access to standard Android application directories and supports reading and writing files within internal storage.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe datadir plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"datadirnewplugin-datadirplugin\"\u003e\u003ccode\u003edatadir.NewPlugin() *DataDirPlugin\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eCreates a new datadir plugin instance.\u003c/p\u003e\n\u003ch3 id=\"datadirplugingetdirs-appdirs-error\"\u003e\u003ccode\u003e(*DataDirPlugin).GetDirs() (AppDirs, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eReturns the application directory paths.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eAppDirs\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eFiles\u003c/span\u003e         \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Internal files directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eCache\u003c/span\u003e         \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Internal cache directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eExternalFiles\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// External files directory (if available)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eExternalCache\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// External cache directory (if available)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"datadirpluginreadfilepath-string-string-error\"\u003e\u003ccode\u003e(*DataDirPlugin).ReadFile(path string) (string, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eReads the contents of a file from the internal files directory.\u003c/p\u003e","title":"DataDir Plugin"},{"content":"The System plugin provides comprehensive OS and device information.\nSetup The system plugin is already registered in the default AppTemplate.\nAPI system.NewPlugin() *SystemPlugin Creates a new System plugin instance.\n(*SystemPlugin).GetInfo() (SystemInfo, error) Returns OS and device information from the native layer.\nReturns: SystemInfo struct or error.\nTypes SystemInfo type SystemInfo struct { SystemName string `json:\u0026#34;system_name\u0026#34;` SystemVersion string `json:\u0026#34;system_version\u0026#34;` Model string `json:\u0026#34;model\u0026#34;` SdkInt int `json:\u0026#34;sdk_int\u0026#34;` Release string `json:\u0026#34;release\u0026#34;` Codename string `json:\u0026#34;codename\u0026#34;` Manufacturer string `json:\u0026#34;manufacturer\u0026#34;` Brand string `json:\u0026#34;brand\u0026#34;` Board string `json:\u0026#34;board\u0026#34;` Device string `json:\u0026#34;device\u0026#34;` Product string `json:\u0026#34;product\u0026#34;` Hardware string `json:\u0026#34;hardware\u0026#34;` BaseOS string `json:\u0026#34;base_os\u0026#34;` SecurityPatch string `json:\u0026#34;security_patch\u0026#34;` Name string `json:\u0026#34;name\u0026#34;` LocalizedModel string `json:\u0026#34;localized_model\u0026#34;` IdentifierForVendor string `json:\u0026#34;identifier_for_vendor\u0026#34;` IsPhysicalDevice bool `json:\u0026#34;is_physical_device\u0026#34;` } Field Platform Description system_name All \u0026quot;Android\u0026quot; or \u0026quot;iOS\u0026quot; system_version All Human-readable OS version model All Device model name manufacturer Android Build.MANUFACTURER brand Android Build.BRAND release Android Build.VERSION.RELEASE sdk_int Android Build.VERSION.SDK_INT security_patch Android Security patch level is_physical_device All Whether the app is running on a real device Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/system\u0026#34; plugin := system.NewPlugin() info, err := plugin.GetInfo() if err == nil { fmt.Printf(\u0026#34;Device: %s %s\\n\u0026#34;, info.Manufacturer, info.Model) fmt.Printf(\u0026#34;OS: %s %s\\n\u0026#34;, info.SystemName, info.SystemVersion) } ","permalink":"https://sweetjuice.pages.dev/v1/plugins/16.-system/","summary":"\u003cp\u003eThe \u003cstrong\u003eSystem\u003c/strong\u003e plugin provides comprehensive OS and device information.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe system plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"systemnewplugin-systemplugin\"\u003e\u003ccode\u003esystem.NewPlugin() *SystemPlugin\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eCreates a new System plugin instance.\u003c/p\u003e\n\u003ch3 id=\"systemplugingetinfo-systeminfo-error\"\u003e\u003ccode\u003e(*SystemPlugin).GetInfo() (SystemInfo, error)\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eReturns OS and device information from the native layer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReturns:\u003c/strong\u003e \u003ccode\u003eSystemInfo\u003c/code\u003e struct or error.\u003c/p\u003e\n\u003ch2 id=\"types\"\u003eTypes\u003c/h2\u003e\n\u003ch3 id=\"systeminfo\"\u003e\u003ccode\u003eSystemInfo\u003c/code\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSystemInfo\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eSystemName\u003c/span\u003e          \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;system_name\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eSystemVersion\u003c/span\u003e       \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;system_version\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eModel\u003c/span\u003e               \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;model\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eSdkInt\u003c/span\u003e              \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e    \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;sdk_int\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eRelease\u003c/span\u003e             \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;release\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eCodename\u003c/span\u003e            \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;codename\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eManufacturer\u003c/span\u003e        \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;manufacturer\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eBrand\u003c/span\u003e               \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;brand\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eBoard\u003c/span\u003e               \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;board\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDevice\u003c/span\u003e              \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;device\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eProduct\u003c/span\u003e             \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;product\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eHardware\u003c/span\u003e            \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;hardware\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eBaseOS\u003c/span\u003e              \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;base_os\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eSecurityPatch\u003c/span\u003e       \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;security_patch\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eName\u003c/span\u003e                \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;name\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eLocalizedModel\u003c/span\u003e      \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;localized_model\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIdentifierForVendor\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;identifier_for_vendor\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIsPhysicalDevice\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e   \u003cspan style=\"color:#e6db74\"\u003e`json:\u0026#34;is_physical_device\u0026#34;`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eField\u003c/th\u003e\n          \u003cth\u003ePlatform\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003esystem_name\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026quot;Android\u0026quot;\u003c/code\u003e or \u003ccode\u003e\u0026quot;iOS\u0026quot;\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003esystem_version\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n          \u003ctd\u003eHuman-readable OS version\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003emodel\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n          \u003ctd\u003eDevice model name\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003emanufacturer\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eBuild.MANUFACTURER\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ebrand\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eBuild.BRAND\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003erelease\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eBuild.VERSION.RELEASE\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003esdk_int\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eBuild.VERSION.SDK_INT\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003esecurity_patch\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAndroid\u003c/td\u003e\n          \u003ctd\u003eSecurity patch level\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eis_physical_device\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAll\u003c/td\u003e\n          \u003ctd\u003eWhether the app is running on a real device\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"example\"\u003eExample\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/sweet-juice/sweetjuice/plugins/system\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eplugin\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esystem\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eNewPlugin\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eerr\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplugin\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eGetInfo\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eerr\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enil\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Device: %s %s\\n\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eManufacturer\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eModel\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;OS: %s %s\\n\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSystemName\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSystemVersion\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"System Plugin"},{"content":"The Broadcast plugin provides a bridge to the Android system\u0026rsquo;s Intent-based broadcast infrastructure.\nSetup The broadcast plugin is already registered in the default AppTemplate.\nAPI broadcast.On(action string, callback func(interface{})) Registers a handler for a specific Android Intent action.\nWhen the OS fires a matching intent, the plugin captures it and forwards the extras as a Go map to your callback.\nbroadcast.Post(action string, extras map[string]interface{}) Sends a system-wide broadcast (Intent) on the native side.\nArgument Type Description action string The Intent action string extras map[string]interface{} Data to include in the intent extras System Events Auto-start on Boot To wake up your app when the phone starts, ensure your AndroidManifest.xml includes the following:\n\u0026lt;uses-permission android:name=\u0026#34;android.permission.RECEIVE_BOOT_COMPLETED\u0026#34; /\u0026gt; \u0026lt;receiver android:name=\u0026#34;com.sweetjuice.pkg.broadcast.BootReceiver\u0026#34; android:exported=\u0026#34;false\u0026#34;\u0026gt; \u0026lt;intent-filter\u0026gt; \u0026lt;action android:name=\u0026#34;android.intent.action.BOOT_COMPLETED\u0026#34; /\u0026gt; \u0026lt;/intent-filter\u0026gt; \u0026lt;/receiver\u0026gt; In your Go code, you can then handle the event:\nbroadcast.On(\u0026#34;android.intent.action.BOOT_COMPLETED\u0026#34;, func(data interface{}) { // Start background services }) Example import \u0026#34;github.com/sweet-juice/sweetjuice/plugins/broadcast\u0026#34; // Send a signal broadcast.Post(\u0026#34;com.myapp.ENGINE_STARTED\u0026#34;, map[string]interface{}{ \u0026#34;uptime\u0026#34;: 0, }) // Listen for system battery changes broadcast.On(\u0026#34;android.intent.action.BATTERY_LOW\u0026#34;, func(data interface{}) { // Throttle intensive tasks }) ","permalink":"https://sweetjuice.pages.dev/v1/plugins/17.-broadcast/","summary":"\u003cp\u003eThe \u003cstrong\u003eBroadcast\u003c/strong\u003e plugin provides a bridge to the Android system\u0026rsquo;s Intent-based broadcast infrastructure.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eThe broadcast plugin is already registered in the default \u003ccode\u003eAppTemplate\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"api\"\u003eAPI\u003c/h2\u003e\n\u003ch3 id=\"broadcastonaction-string-callback-funcinterface\"\u003e\u003ccode\u003ebroadcast.On(action string, callback func(interface{}))\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eRegisters a handler for a specific Android Intent action.\u003c/p\u003e\n\u003cp\u003eWhen the OS fires a matching intent, the plugin captures it and forwards the \u003ccode\u003eextras\u003c/code\u003e as a Go map to your callback.\u003c/p\u003e\n\u003ch3 id=\"broadcastpostaction-string-extras-mapstringinterface\"\u003e\u003ccode\u003ebroadcast.Post(action string, extras map[string]interface{})\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eSends a system-wide broadcast (Intent) on the native side.\u003c/p\u003e","title":"Broadcast Plugin"}]