forked from fyne-io/fyne
/
sdk.go
154 lines (140 loc) · 3.41 KB
/
sdk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package binres
import (
"archive/zip"
"bytes"
"compress/gzip"
"fmt"
"io"
"os"
"path"
)
// MinSDK is the targeted sdk version for support by package binres.
const MinSDK = 15
// Requires environment variable ANDROID_HOME to be set.
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
}
err = rc.Close()
if err != nil {
return nil, err
}
break
}
}
if buf.Len() == 0 {
return nil, fmt.Errorf("failed to read resources.arsc")
}
return buf.Bytes(), nil
}
func apiResourcesPath() (string, error) {
// TODO(elias.naur): use the logic from gomobile's androidAPIPath and use the any installed version of the
// Android SDK instead. Currently, the binres_test.go tests fail on anything newer than android-15.
sdkdir := os.Getenv("ANDROID_HOME")
if sdkdir == "" {
return "", fmt.Errorf("ANDROID_HOME env var not set")
}
platform := fmt.Sprintf("android-%v", MinSDK)
return path.Join(sdkdir, "platforms", platform, "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
}