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