88 "errors"
99 "fmt"
1010 "io"
11+ "io/fs"
1112 "io/ioutil"
1213 "os"
1314 "os/exec"
@@ -49,6 +50,7 @@ type manifestData struct {
4950 Features []string
5051 IconSnip string
5152 AppName string
53+ HasService bool
5254}
5355
5456const (
@@ -68,6 +70,7 @@ const (
6870 <item name="android:statusBarColor">#40000000</item>
6971 </style>
7072</resources>`
73+ foregroundPermission = "android.permission.FOREGROUND_SERVICE"
7174)
7275
7376func init () {
@@ -146,9 +149,6 @@ func buildAndroid(tmpDir string, bi *buildInfo) error {
146149 return err
147150 }
148151
149- if err := compileAndroid (tmpDir , tools , bi ); err != nil {
150- return err
151- }
152152 switch * buildMode {
153153 case "archive" :
154154 return archiveAndroid (tmpDir , bi , perms )
@@ -248,6 +248,18 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
248248 if err != nil {
249249 return err
250250 }
251+
252+ // walk tmpDir to find generated R.java that is generated from Manifest.xml
253+ err = filepath .Walk (tmpDir , func (path string , info fs.FileInfo , err error ) error {
254+ if ! info .IsDir () && filepath .Ext (info .Name ()) == ".java" {
255+ javaFiles = append (javaFiles , path )
256+ }
257+ return nil
258+ })
259+ if err != nil {
260+ return err
261+ }
262+
251263 if len (javaFiles ) == 0 {
252264 return fmt .Errorf ("the gioui.org/app package contains no .java files (gioui.org module too old?)" )
253265 }
@@ -337,22 +349,6 @@ func archiveAndroid(tmpDir string, bi *buildInfo, perms []string) (err error) {
337349}
338350
339351func exeAndroid (tmpDir string , tools * androidTools , bi * buildInfo , extraJars , perms []string , isBundle bool ) (err error ) {
340- classes := filepath .Join (tmpDir , "classes" )
341- var classFiles []string
342- err = filepath .Walk (classes , func (path string , f os.FileInfo , err error ) error {
343- if err != nil {
344- return err
345- }
346- if filepath .Ext (path ) == ".class" {
347- classFiles = append (classFiles , path )
348- }
349- return nil
350- })
351- classFiles = append (classFiles , extraJars ... )
352- dexDir := filepath .Join (tmpDir , "apk" )
353- if err := os .MkdirAll (dexDir , 0755 ); err != nil {
354- return err
355- }
356352 // https://developer.android.com/distribute/best-practices/develop/target-sdk
357353 targetSDK := 31
358354 if bi .minsdk > targetSDK {
@@ -362,22 +358,6 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
362358 if bi .minsdk > minSDK {
363359 minSDK = bi .minsdk
364360 }
365- if len (classFiles ) > 0 {
366- d8 := exec .Command (
367- filepath .Join (tools .buildtools , "d8" ),
368- "--lib" , tools .androidjar ,
369- "--output" , dexDir ,
370- "--min-api" , strconv .Itoa (minSDK ),
371- )
372- d8 .Args = append (d8 .Args , classFiles ... )
373- if _ , err := runCmd (d8 ); err != nil {
374- major , minor , ok := determineJDKVersion ()
375- if ok && (major != 1 || minor != 8 ) {
376- return fmt .Errorf ("unsupported JDK version %d.%d, expected 1.8\n d8 error: %v" , major , minor , err )
377- }
378- return err
379- }
380- }
381361
382362 // Compile resources.
383363 resDir := filepath .Join (tmpDir , "res" )
@@ -446,6 +426,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
446426 Features : features ,
447427 IconSnip : iconSnip ,
448428 AppName : appName ,
429+ HasService : stringsContains (permissions , foregroundPermission ),
449430 }
450431 tmpl , err := template .New ("test" ).Parse (
451432 `<?xml version="1.0" encoding="utf-8"?>
@@ -468,6 +449,19 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
468449 <category android:name="android.intent.category.LAUNCHER" />
469450 </intent-filter>
470451 </activity>
452+ {{if .HasService}}
453+ <service android:name="org.gioui.GioForegroundService"
454+ android:stopWithTask="true">
455+ <meta-data android:name="org.gioui.ForegroundChannelID"
456+ android:value="gio_foreground" />
457+ <meta-data android:name="org.gioui.ForegroundChannelName"
458+ android:value="GioForeground" />
459+ <meta-data android:name="org.gioui.ForegroundChannelDesc"
460+ android:value="" />
461+ <meta-data android:name="org.gioui.ForegroundNotificationID"
462+ android:value="0x42424242" />
463+ </service>
464+ {{end}}
471465 </application>
472466</manifest>` )
473467 var manifestBuffer bytes.Buffer
@@ -486,6 +480,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
486480 "--manifest" , manifest ,
487481 "-I" , tools .androidjar ,
488482 "-o" , linkAPK ,
483+ "--java" , tmpDir ,
489484 }
490485 if isBundle {
491486 args = append (args , "--proto-format" )
@@ -496,6 +491,43 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
496491 return err
497492 }
498493
494+ if err := compileAndroid (tmpDir , tools , bi ); err != nil {
495+ return err
496+ }
497+
498+ classes := filepath .Join (tmpDir , "classes" )
499+ var classFiles []string
500+ err = filepath .Walk (classes , func (path string , f os.FileInfo , err error ) error {
501+ if err != nil {
502+ return err
503+ }
504+ if filepath .Ext (path ) == ".class" {
505+ classFiles = append (classFiles , path )
506+ }
507+ return nil
508+ })
509+ classFiles = append (classFiles , extraJars ... )
510+ dexDir := filepath .Join (tmpDir , "apk" )
511+ if err := os .MkdirAll (dexDir , 0755 ); err != nil {
512+ return err
513+ }
514+ if len (classFiles ) > 0 {
515+ d8 := exec .Command (
516+ filepath .Join (tools .buildtools , "d8" ),
517+ "--lib" , tools .androidjar ,
518+ "--output" , dexDir ,
519+ "--min-api" , strconv .Itoa (minSDK ),
520+ )
521+ d8 .Args = append (d8 .Args , classFiles ... )
522+ if _ , err := runCmd (d8 ); err != nil {
523+ major , minor , ok := determineJDKVersion ()
524+ if ok && (major != 1 || minor != 8 ) {
525+ return fmt .Errorf ("unsupported JDK version %d.%d, expected 1.8\n d8 error: %v" , major , minor , err )
526+ }
527+ return err
528+ }
529+ }
530+
499531 // The Go standard library archive/zip doesn't support appending to zip
500532 // files. Copy files from `link.apk` (generated by aapt2) along with classes.dex and
501533 // the Go libraries to a new `app.zip` file.
@@ -865,6 +897,15 @@ func getPermissions(ps []string) ([]string, []string) {
865897 return permissions , features
866898}
867899
900+ func stringsContains (strings []string , str string ) bool {
901+ for _ , s := range strings {
902+ if str == s {
903+ return true
904+ }
905+ }
906+ return false
907+ }
908+
868909func latestPlatform (sdk string ) (string , error ) {
869910 allPlats , err := filepath .Glob (filepath .Join (sdk , "platforms" , "android-*" ))
870911 if err != nil {
0 commit comments