| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- package binres
- import (
- "archive/zip"
- "bytes"
- "compress/gzip"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "golang.org/x/mobile/internal/sdkpath"
- )
- // MinSDK is the targeted sdk version for support by package binres.
- const MinSDK = 16
- func apiResources() ([]byte, error) {
- apiResPath, err := apiResourcesPath()
- if err != nil {
- return nil, err
- }
- zr, err := zip.OpenReader(apiResPath)
- if err != nil {
- if os.IsNotExist(err) {
- return nil, fmt.Errorf(`%v; consider installing with "android update sdk --all --no-ui --filter android-%d"`, err, MinSDK)
- }
- return nil, err
- }
- defer zr.Close()
- buf := new(bytes.Buffer)
- for _, f := range zr.File {
- if f.Name == "resources.arsc" {
- rc, err := f.Open()
- if err != nil {
- return nil, err
- }
- _, err = io.Copy(buf, rc)
- if err != nil {
- return nil, err
- }
- rc.Close()
- break
- }
- }
- if buf.Len() == 0 {
- return nil, fmt.Errorf("failed to read resources.arsc")
- }
- return buf.Bytes(), nil
- }
- func apiResourcesPath() (string, error) {
- platformDir, err := sdkpath.AndroidAPIPath(MinSDK)
- if err != nil {
- return "", err
- }
- return filepath.Join(platformDir, "android.jar"), nil
- }
- // PackResources produces a stripped down gzip version of the resources.arsc from api jar.
- func PackResources() ([]byte, error) {
- tbl, err := OpenSDKTable()
- if err != nil {
- return nil, err
- }
- tbl.pool.strings = []string{} // should not be needed
- pkg := tbl.pkgs[0]
- // drop language string entries
- for _, typ := range pkg.specs[3].types {
- if typ.config.locale.language != 0 {
- for j, nt := range typ.entries {
- if nt == nil { // NoEntry
- continue
- }
- pkg.keyPool.strings[nt.key] = ""
- typ.indices[j] = NoEntry
- typ.entries[j] = nil
- }
- }
- }
- // drop strings from pool for specs to be dropped
- for _, spec := range pkg.specs[4:] {
- for _, typ := range spec.types {
- for _, nt := range typ.entries {
- if nt == nil { // NoEntry
- continue
- }
- // don't drop if there's a collision
- var collision bool
- for _, xspec := range pkg.specs[:4] {
- for _, xtyp := range xspec.types {
- for _, xnt := range xtyp.entries {
- if xnt == nil {
- continue
- }
- if collision = nt.key == xnt.key; collision {
- break
- }
- }
- }
- }
- if !collision {
- pkg.keyPool.strings[nt.key] = ""
- }
- }
- }
- }
- // entries are densely packed but probably safe to drop nil entries off the end
- for _, spec := range pkg.specs[:4] {
- for _, typ := range spec.types {
- var last int
- for i, nt := range typ.entries {
- if nt != nil {
- last = i
- }
- }
- typ.entries = typ.entries[:last+1]
- typ.indices = typ.indices[:last+1]
- }
- }
- // keeping 0:attr, 1:id, 2:style, 3:string
- pkg.typePool.strings = pkg.typePool.strings[:4]
- pkg.specs = pkg.specs[:4]
- bin, err := tbl.MarshalBinary()
- if err != nil {
- return nil, err
- }
- buf := new(bytes.Buffer)
- zw := gzip.NewWriter(buf)
- if _, err := zw.Write(bin); err != nil {
- return nil, err
- }
- if err := zw.Flush(); err != nil {
- return nil, err
- }
- if err := zw.Close(); err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
- }
|