sdk.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package binres
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "compress/gzip"
  6. "fmt"
  7. "io"
  8. "os"
  9. "path/filepath"
  10. "golang.org/x/mobile/internal/sdkpath"
  11. )
  12. // MinSDK is the targeted sdk version for support by package binres.
  13. const MinSDK = 16
  14. func apiResources() ([]byte, error) {
  15. apiResPath, err := apiResourcesPath()
  16. if err != nil {
  17. return nil, err
  18. }
  19. zr, err := zip.OpenReader(apiResPath)
  20. if err != nil {
  21. if os.IsNotExist(err) {
  22. return nil, fmt.Errorf(`%v; consider installing with "android update sdk --all --no-ui --filter android-%d"`, err, MinSDK)
  23. }
  24. return nil, err
  25. }
  26. defer zr.Close()
  27. buf := new(bytes.Buffer)
  28. for _, f := range zr.File {
  29. if f.Name == "resources.arsc" {
  30. rc, err := f.Open()
  31. if err != nil {
  32. return nil, err
  33. }
  34. _, err = io.Copy(buf, rc)
  35. if err != nil {
  36. return nil, err
  37. }
  38. rc.Close()
  39. break
  40. }
  41. }
  42. if buf.Len() == 0 {
  43. return nil, fmt.Errorf("failed to read resources.arsc")
  44. }
  45. return buf.Bytes(), nil
  46. }
  47. func apiResourcesPath() (string, error) {
  48. platformDir, err := sdkpath.AndroidAPIPath(MinSDK)
  49. if err != nil {
  50. return "", err
  51. }
  52. return filepath.Join(platformDir, "android.jar"), nil
  53. }
  54. // PackResources produces a stripped down gzip version of the resources.arsc from api jar.
  55. func PackResources() ([]byte, error) {
  56. tbl, err := OpenSDKTable()
  57. if err != nil {
  58. return nil, err
  59. }
  60. tbl.pool.strings = []string{} // should not be needed
  61. pkg := tbl.pkgs[0]
  62. // drop language string entries
  63. for _, typ := range pkg.specs[3].types {
  64. if typ.config.locale.language != 0 {
  65. for j, nt := range typ.entries {
  66. if nt == nil { // NoEntry
  67. continue
  68. }
  69. pkg.keyPool.strings[nt.key] = ""
  70. typ.indices[j] = NoEntry
  71. typ.entries[j] = nil
  72. }
  73. }
  74. }
  75. // drop strings from pool for specs to be dropped
  76. for _, spec := range pkg.specs[4:] {
  77. for _, typ := range spec.types {
  78. for _, nt := range typ.entries {
  79. if nt == nil { // NoEntry
  80. continue
  81. }
  82. // don't drop if there's a collision
  83. var collision bool
  84. for _, xspec := range pkg.specs[:4] {
  85. for _, xtyp := range xspec.types {
  86. for _, xnt := range xtyp.entries {
  87. if xnt == nil {
  88. continue
  89. }
  90. if collision = nt.key == xnt.key; collision {
  91. break
  92. }
  93. }
  94. }
  95. }
  96. if !collision {
  97. pkg.keyPool.strings[nt.key] = ""
  98. }
  99. }
  100. }
  101. }
  102. // entries are densely packed but probably safe to drop nil entries off the end
  103. for _, spec := range pkg.specs[:4] {
  104. for _, typ := range spec.types {
  105. var last int
  106. for i, nt := range typ.entries {
  107. if nt != nil {
  108. last = i
  109. }
  110. }
  111. typ.entries = typ.entries[:last+1]
  112. typ.indices = typ.indices[:last+1]
  113. }
  114. }
  115. // keeping 0:attr, 1:id, 2:style, 3:string
  116. pkg.typePool.strings = pkg.typePool.strings[:4]
  117. pkg.specs = pkg.specs[:4]
  118. bin, err := tbl.MarshalBinary()
  119. if err != nil {
  120. return nil, err
  121. }
  122. buf := new(bytes.Buffer)
  123. zw := gzip.NewWriter(buf)
  124. if _, err := zw.Write(bin); err != nil {
  125. return nil, err
  126. }
  127. if err := zw.Flush(); err != nil {
  128. return nil, err
  129. }
  130. if err := zw.Close(); err != nil {
  131. return nil, err
  132. }
  133. return buf.Bytes(), nil
  134. }