1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093 |
- package android_binary
- import (
- "encoding/binary"
- "fmt"
- "io"
- "strconv"
- "strings"
- "unsafe"
- )
- // ResID is ID for resources.
- type ResID uint32
- // TableFile is a resource table file.
- type TableFile struct {
- stringPool *ResStringPool
- tablePackages map[uint32]*TablePackage
- }
- // ResTableHeader is a header of TableFile.
- type ResTableHeader struct {
- Header ResChunkHeader
- PackageCount uint32
- }
- // ResTablePackage is a header of table packages.
- type ResTablePackage struct {
- Header ResChunkHeader
- ID uint32
- Name [128]uint16
- TypeStrings uint32
- LastPublicType uint32
- KeyStrings uint32
- LastPublicKey uint32
- }
- // TablePackage is a table package.
- type TablePackage struct {
- Header ResTablePackage
- TypeStrings *ResStringPool
- KeyStrings *ResStringPool
- TableTypes []*TableType
- }
- // ResTableType is a type of a table.
- type ResTableType struct {
- Header ResChunkHeader
- ID uint8
- Res0 uint8
- Res1 uint16
- EntryCount uint32
- EntriesStart uint32
- Config ResTableConfig
- }
- // ScreenLayout describes screen layout.
- type ScreenLayout uint8
- // ScreenLayout bits
- const (
- MaskScreenSize ScreenLayout = 0x0f
- ScreenSizeAny ScreenLayout = 0x01
- ScreenSizeSmall ScreenLayout = 0x02
- ScreenSizeNormal ScreenLayout = 0x03
- ScreenSizeLarge ScreenLayout = 0x04
- ScreenSizeXLarge ScreenLayout = 0x05
- MaskScreenLong ScreenLayout = 0x30
- ShiftScreenLong = 4
- ScreenLongAny ScreenLayout = 0x00
- ScreenLongNo ScreenLayout = 0x10
- ScreenLongYes ScreenLayout = 0x20
- MaskLayoutDir ScreenLayout = 0xC0
- ShiftLayoutDir = 6
- LayoutDirAny ScreenLayout = 0x00
- LayoutDirLTR ScreenLayout = 0x40
- LayoutDirRTL ScreenLayout = 0x80
- )
- // UIMode describes UI mode.
- type UIMode uint8
- // UIMode bits
- const (
- MaskUIModeType UIMode = 0x0f
- UIModeTypeAny UIMode = 0x01
- UIModeTypeNormal UIMode = 0x02
- UIModeTypeDesk UIMode = 0x03
- UIModeTypeCar UIMode = 0x04
- MaskUIModeNight UIMode = 0x30
- ShiftUIModeNight = 4
- UIModeNightAny UIMode = 0x00
- UIModeNightNo UIMode = 0x10
- UIModeNightYes UIMode = 0x20
- )
- // InputFlags are input flags.
- type InputFlags uint8
- // input flags
- const (
- MaskKeysHidden InputFlags = 0x03
- KeysHiddenAny InputFlags = 0x00
- KeysHiddenNo InputFlags = 0x01
- KeysHiddenYes InputFlags = 0x02
- KeysHiddenSoft InputFlags = 0x03
- MaskNavHidden InputFlags = 0x0c
- NavHiddenAny InputFlags = 0x00
- NavHiddenNo InputFlags = 0x04
- NavHiddenYes InputFlags = 0x08
- )
- // ResTableConfig is a configuration of a table.
- type ResTableConfig struct {
- Size uint32
- // imsi
- Mcc uint16
- Mnc uint16
- // locale
- Language [2]uint8
- Country [2]uint8
- // screen type
- Orientation uint8
- Touchscreen uint8
- Density uint16
- // inout
- Keyboard uint8
- Navigation uint8
- InputFlags InputFlags
- InputPad0 uint8
- // screen size
- ScreenWidth uint16
- ScreenHeight uint16
- // version
- SDKVersion uint16
- MinorVersion uint16
- // screen config
- ScreenLayout ScreenLayout
- UIMode UIMode
- SmallestScreenWidthDp uint16
- // screen size dp
- ScreenWidthDp uint16
- ScreenHeightDp uint16
- }
- // TableType is a collection of resource entries for a particular resource data type.
- type TableType struct {
- Header *ResTableType
- Entries []TableEntry
- }
- // ResTableEntry is the beginning of information about an entry in the resource table.
- type ResTableEntry struct {
- Size uint16
- Flags uint16
- Key ResStringPoolRef
- }
- // TableEntry is a entry in a resource table.
- type TableEntry struct {
- Key *ResTableEntry
- Value *ResValue
- Flags uint32
- }
- // ResTableTypeSpec is specification of the resources defined by a particular type.
- type ResTableTypeSpec struct {
- Header ResChunkHeader
- ID uint8
- Res0 uint8
- Res1 uint16
- EntryCount uint32
- }
- // IsResID returns whether s is ResId.
- func IsResID(s string) bool {
- return strings.HasPrefix(s, "@0x")
- }
- // ParseResID parses ResId.
- func ParseResID(s string) (ResID, error) {
- if !IsResID(s) {
- return 0, fmt.Errorf("androidbinary: %s is not ResID", s)
- }
- id, err := strconv.ParseUint(s[3:], 16, 32)
- if err != nil {
- return 0, err
- }
- return ResID(id), nil
- }
- func (id ResID) String() string {
- return fmt.Sprintf("@0x%08X", uint32(id))
- }
- // Package returns the package index of id.
- func (id ResID) Package() uint32 {
- return uint32(id) >> 24
- }
- // Type returns the type index of id.
- func (id ResID) Type() int {
- return (int(id) >> 16) & 0xFF
- }
- // Entry returns the entry index of id.
- func (id ResID) Entry() int {
- return int(id) & 0xFFFF
- }
- // NewTableFile returns new TableFile.
- func NewTableFile(r io.ReaderAt) (*TableFile, error) {
- f := new(TableFile)
- sr := io.NewSectionReader(r, 0, 1<<63-1)
- header := new(ResTableHeader)
- binary.Read(sr, binary.LittleEndian, header)
- f.tablePackages = make(map[uint32]*TablePackage)
- offset := int64(header.Header.HeaderSize)
- for offset < int64(header.Header.Size) {
- chunkHeader, err := f.readChunk(sr, offset)
- if err != nil {
- return nil, err
- }
- offset += int64(chunkHeader.Size)
- }
- return f, nil
- }
- func (f *TableFile) findPackage(id uint32) *TablePackage {
- if f == nil {
- return nil
- }
- return f.tablePackages[id]
- }
- func (p *TablePackage) findEntry(typeIndex, entryIndex int, config *ResTableConfig) TableEntry {
- var best *TableType
- for _, t := range p.TableTypes {
- switch {
- case int(t.Header.ID) != typeIndex:
- // nothing to do
- case !t.Header.Config.Match(config):
- // nothing to do
- case entryIndex >= len(t.Entries):
- // nothing to do
- case t.Entries[entryIndex].Value == nil:
- // nothing to do
- case best == nil || t.Header.Config.IsBetterThan(&best.Header.Config, config):
- best = t
- }
- }
- if best == nil || entryIndex >= len(best.Entries) {
- return TableEntry{}
- }
- return best.Entries[entryIndex]
- }
- // GetResource returns a resource referenced by id.
- func (f *TableFile) GetResource(id ResID, config *ResTableConfig) (any, error) {
- p := f.findPackage(id.Package())
- if p == nil {
- return nil, fmt.Errorf("androidbinary: package 0x%02X not found", id.Package())
- }
- e := p.findEntry(id.Type(), id.Entry(), config)
- v := e.Value
- if v == nil {
- return nil, fmt.Errorf("androidbinary: entry 0x%04X not found", id.Entry())
- }
- switch v.DataType {
- case TypeNull:
- return nil, nil
- case TypeString:
- return f.GetString(ResStringPoolRef(v.Data)), nil
- case TypeIntDec:
- return v.Data, nil
- case TypeIntHex:
- return v.Data, nil
- case TypeIntBoolean:
- return v.Data != 0, nil
- }
- return v.Data, nil
- }
- // GetString returns a string referenced by ref.
- func (f *TableFile) GetString(ref ResStringPoolRef) string {
- return f.stringPool.GetString(ref)
- }
- func (f *TableFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) {
- sr := io.NewSectionReader(r, offset, 1<<63-1-offset)
- chunkHeader := &ResChunkHeader{}
- if _, err := sr.Seek(0, io.SeekStart); err != nil {
- return nil, err
- }
- if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
- return nil, err
- }
- var err error
- if _, err := sr.Seek(0, io.SeekStart); err != nil {
- return nil, err
- }
- switch chunkHeader.Type {
- case ResStringPoolChunkType:
- f.stringPool, err = readStringPool(sr)
- case ResTablePackageType:
- var tablePackage *TablePackage
- tablePackage, err = readTablePackage(sr)
- f.tablePackages[tablePackage.Header.ID] = tablePackage
- }
- if err != nil {
- return nil, err
- }
- return chunkHeader, nil
- }
- func readTablePackage(sr *io.SectionReader) (*TablePackage, error) {
- tablePackage := new(TablePackage)
- header := new(ResTablePackage)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return nil, err
- }
- tablePackage.Header = *header
- srTypes := io.NewSectionReader(sr, int64(header.TypeStrings), int64(header.Header.Size-header.TypeStrings))
- if typeStrings, err := readStringPool(srTypes); err == nil {
- tablePackage.TypeStrings = typeStrings
- } else {
- return nil, err
- }
- srKeys := io.NewSectionReader(sr, int64(header.KeyStrings), int64(header.Header.Size-header.KeyStrings))
- if keyStrings, err := readStringPool(srKeys); err == nil {
- tablePackage.KeyStrings = keyStrings
- } else {
- return nil, err
- }
- offset := int64(header.Header.HeaderSize)
- for offset < int64(header.Header.Size) {
- chunkHeader := &ResChunkHeader{}
- if _, err := sr.Seek(offset, io.SeekStart); err != nil {
- return nil, err
- }
- if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
- return nil, err
- }
- var err error
- chunkReader := io.NewSectionReader(sr, offset, int64(chunkHeader.Size))
- if _, err := sr.Seek(offset, io.SeekStart); err != nil {
- return nil, err
- }
- switch chunkHeader.Type {
- case ResTableTypeType:
- var tableType *TableType
- tableType, err = readTableType(chunkHeader, chunkReader)
- tablePackage.TableTypes = append(tablePackage.TableTypes, tableType)
- case ResTableTypeSpecType:
- _, err = readTableTypeSpec(chunkReader)
- }
- if err != nil {
- return nil, err
- }
- offset += int64(chunkHeader.Size)
- }
- return tablePackage, nil
- }
- func readTableType(chunkHeader *ResChunkHeader, sr *io.SectionReader) (*TableType, error) {
- // TableType header may be omitted
- header := new(ResTableType)
- if _, err := sr.Seek(0, io.SeekStart); err != nil {
- return nil, err
- }
- buf, err := newZeroFilledReader(sr, int64(chunkHeader.HeaderSize), int64(unsafe.Sizeof(*header)))
- if err != nil {
- return nil, err
- }
- if err := binary.Read(buf, binary.LittleEndian, header); err != nil {
- return nil, err
- }
- entryIndexes := make([]uint32, header.EntryCount)
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return nil, err
- }
- if err := binary.Read(sr, binary.LittleEndian, entryIndexes); err != nil {
- return nil, err
- }
- entries := make([]TableEntry, header.EntryCount)
- for i, index := range entryIndexes {
- if index == 0xFFFFFFFF {
- continue
- }
- if _, err := sr.Seek(int64(header.EntriesStart+index), io.SeekStart); err != nil {
- return nil, err
- }
- var key ResTableEntry
- binary.Read(sr, binary.LittleEndian, &key)
- entries[i].Key = &key
- var val ResValue
- binary.Read(sr, binary.LittleEndian, &val)
- entries[i].Value = &val
- }
- return &TableType{
- header,
- entries,
- }, nil
- }
- func readTableTypeSpec(sr *io.SectionReader) ([]uint32, error) {
- header := new(ResTableTypeSpec)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return nil, err
- }
- flags := make([]uint32, header.EntryCount)
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return nil, err
- }
- if err := binary.Read(sr, binary.LittleEndian, flags); err != nil {
- return nil, err
- }
- return flags, nil
- }
- // IsMoreSpecificThan returns true if c is more specific than o.
- func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool {
- // nil ResTableConfig is never more specific than any ResTableConfig
- if c == nil {
- return false
- }
- if o == nil {
- return false
- }
- // imsi
- if c.Mcc != o.Mcc {
- if c.Mcc == 0 {
- return false
- }
- if o.Mnc == 0 {
- return true
- }
- }
- if c.Mnc != o.Mnc {
- if c.Mnc == 0 {
- return false
- }
- if o.Mnc == 0 {
- return true
- }
- }
- // locale
- if diff := c.IsLocaleMoreSpecificThan(o); diff < 0 {
- return false
- } else if diff > 0 {
- return true
- }
- // screen layout
- if c.ScreenLayout != 0 || o.ScreenLayout != 0 {
- if ((c.ScreenLayout ^ o.ScreenLayout) & MaskLayoutDir) != 0 {
- if (c.ScreenLayout & MaskLayoutDir) == 0 {
- return false
- }
- if (o.ScreenLayout & MaskLayoutDir) == 0 {
- return true
- }
- }
- }
- // smallest screen width dp
- if c.SmallestScreenWidthDp != 0 || o.SmallestScreenWidthDp != 0 {
- if c.SmallestScreenWidthDp != o.SmallestScreenWidthDp {
- if c.SmallestScreenWidthDp == 0 {
- return false
- }
- if o.SmallestScreenWidthDp == 0 {
- return true
- }
- }
- }
- // screen size dp
- if c.ScreenWidthDp != 0 || o.ScreenWidthDp != 0 ||
- c.ScreenHeightDp != 0 || o.ScreenHeightDp != 0 {
- if c.ScreenWidthDp != o.ScreenWidthDp {
- if c.ScreenWidthDp == 0 {
- return false
- }
- if o.ScreenWidthDp == 0 {
- return true
- }
- }
- if c.ScreenHeightDp != o.ScreenHeightDp {
- if c.ScreenHeightDp == 0 {
- return false
- }
- if o.ScreenHeightDp == 0 {
- return true
- }
- }
- }
- // screen layout
- if c.ScreenLayout != 0 || o.ScreenLayout != 0 {
- if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenSize) != 0 {
- if (c.ScreenLayout & MaskScreenSize) == 0 {
- return false
- }
- if (o.ScreenLayout & MaskScreenSize) == 0 {
- return true
- }
- }
- if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenLong) != 0 {
- if (c.ScreenLayout & MaskScreenLong) == 0 {
- return false
- }
- if (o.ScreenLayout & MaskScreenLong) == 0 {
- return true
- }
- }
- }
- // orientation
- if c.Orientation != o.Orientation {
- if c.Orientation == 0 {
- return false
- }
- if o.Orientation == 0 {
- return true
- }
- }
- // uimode
- if c.UIMode != 0 || o.UIMode != 0 {
- diff := c.UIMode ^ o.UIMode
- if (diff & MaskUIModeType) != 0 {
- if (c.UIMode & MaskUIModeType) == 0 {
- return false
- }
- if (o.UIMode & MaskUIModeType) == 0 {
- return true
- }
- }
- if (diff & MaskUIModeNight) != 0 {
- if (c.UIMode & MaskUIModeNight) == 0 {
- return false
- }
- if (o.UIMode & MaskUIModeNight) == 0 {
- return true
- }
- }
- }
- // touchscreen
- if c.Touchscreen != o.Touchscreen {
- if c.Touchscreen == 0 {
- return false
- }
- if o.Touchscreen == 0 {
- return true
- }
- }
- // input
- if c.InputFlags != 0 || o.InputFlags != 0 {
- myKeysHidden := c.InputFlags & MaskKeysHidden
- oKeysHidden := o.InputFlags & MaskKeysHidden
- if (myKeysHidden ^ oKeysHidden) != 0 {
- if myKeysHidden == 0 {
- return false
- }
- if oKeysHidden == 0 {
- return true
- }
- }
- myNavHidden := c.InputFlags & MaskNavHidden
- oNavHidden := o.InputFlags & MaskNavHidden
- if (myNavHidden ^ oNavHidden) != 0 {
- if myNavHidden == 0 {
- return false
- }
- if oNavHidden == 0 {
- return true
- }
- }
- }
- if c.Keyboard != o.Keyboard {
- if c.Keyboard == 0 {
- return false
- }
- if o.Keyboard == 0 {
- return true
- }
- }
- if c.Navigation != o.Navigation {
- if c.Navigation == 0 {
- return false
- }
- if o.Navigation == 0 {
- return true
- }
- }
- // screen size
- if c.ScreenWidth != 0 || o.ScreenWidth != 0 ||
- c.ScreenHeight != 0 || o.ScreenHeight != 0 {
- if c.ScreenWidth != o.ScreenWidth {
- if c.ScreenWidth == 0 {
- return false
- }
- if o.ScreenWidth == 0 {
- return true
- }
- }
- if c.ScreenHeight != o.ScreenHeight {
- if c.ScreenHeight == 0 {
- return false
- }
- if o.ScreenHeight == 0 {
- return true
- }
- }
- }
- //version
- if c.SDKVersion != o.SDKVersion {
- if c.SDKVersion == 0 {
- return false
- }
- if o.SDKVersion == 0 {
- return true
- }
- }
- if c.MinorVersion != o.MinorVersion {
- if c.MinorVersion == 0 {
- return false
- }
- if o.MinorVersion == 0 {
- return true
- }
- }
- return false
- }
- // IsBetterThan returns true if c is better than o for the r configuration.
- func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool {
- if r == nil {
- return c.IsMoreSpecificThan(o)
- }
- // nil ResTableConfig is never better than any ResTableConfig
- if c == nil {
- return false
- }
- if o == nil {
- return false
- }
- // imsi
- if c.Mcc != 0 || c.Mnc != 0 || o.Mcc != 0 || o.Mnc != 0 {
- if c.Mcc != o.Mcc && r.Mcc != 0 {
- return c.Mcc != 0
- }
- if c.Mnc != o.Mnc && r.Mnc != 0 {
- return c.Mnc != 0
- }
- }
- // locale
- if c.IsLocaleBetterThan(o, r) {
- return true
- }
- // screen layout
- if c.ScreenLayout != 0 || o.ScreenLayout != 0 {
- myLayoutdir := c.ScreenLayout & MaskLayoutDir
- oLayoutdir := o.ScreenLayout & MaskLayoutDir
- if (myLayoutdir^oLayoutdir) != 0 && (r.ScreenLayout&MaskLayoutDir) != 0 {
- return myLayoutdir > oLayoutdir
- }
- }
- // smallest screen width dp
- if c.SmallestScreenWidthDp != 0 || o.SmallestScreenWidthDp != 0 {
- if c.SmallestScreenWidthDp != o.SmallestScreenWidthDp {
- return c.SmallestScreenWidthDp > o.SmallestScreenWidthDp
- }
- }
- // screen size dp
- if c.ScreenWidthDp != 0 || c.ScreenHeightDp != 0 || o.ScreenWidthDp != 0 || o.ScreenHeightDp != 0 {
- myDelta := 0
- otherDelta := 0
- if r.ScreenWidthDp != 0 {
- myDelta += int(r.ScreenWidthDp) - int(c.ScreenWidthDp)
- otherDelta += int(r.ScreenWidthDp) - int(o.ScreenWidthDp)
- }
- if r.ScreenHeightDp != 0 {
- myDelta += int(r.ScreenHeightDp) - int(c.ScreenHeightDp)
- otherDelta += int(r.ScreenHeightDp) - int(o.ScreenHeightDp)
- }
- if myDelta != otherDelta {
- return myDelta < otherDelta
- }
- }
- // screen layout
- if c.ScreenLayout != 0 || o.ScreenLayout != 0 {
- mySL := c.ScreenLayout & MaskScreenSize
- oSL := o.ScreenLayout & MaskScreenSize
- if (mySL^oSL) != 0 && (r.ScreenLayout&MaskScreenSize) != 0 {
- fixedMySL := mySL
- fixedOSL := oSL
- if (r.ScreenLayout & MaskScreenSize) >= ScreenSizeNormal {
- if fixedMySL == 0 {
- fixedMySL = ScreenSizeNormal
- }
- if fixedOSL == 0 {
- fixedOSL = ScreenSizeNormal
- }
- }
- if fixedMySL == fixedOSL {
- return mySL != 0
- }
- return fixedMySL > fixedOSL
- }
- if ((c.ScreenLayout^o.ScreenLayout)&MaskScreenLong) != 0 &&
- (r.ScreenLayout&MaskScreenLong) != 0 {
- return (c.ScreenLayout & MaskScreenLong) != 0
- }
- }
- // orientation
- if c.Orientation != o.Orientation && r.Orientation != 0 {
- return c.Orientation != 0
- }
- // uimode
- if c.UIMode != 0 || o.UIMode != 0 {
- diff := c.UIMode ^ o.UIMode
- if (diff&MaskUIModeType) != 0 && (r.UIMode&MaskUIModeType) != 0 {
- return (c.UIMode & MaskUIModeType) != 0
- }
- if (diff&MaskUIModeNight) != 0 && (r.UIMode&MaskUIModeNight) != 0 {
- return (c.UIMode & MaskUIModeNight) != 0
- }
- }
- // screen type
- if c.Density != o.Density {
- h := int(c.Density)
- if h == 0 {
- h = 160
- }
- l := int(o.Density)
- if l == 0 {
- l = 160
- }
- blmBigger := true
- if l > h {
- h, l = l, h
- blmBigger = false
- }
- reqValue := int(r.Density)
- if reqValue == 0 {
- reqValue = 160
- }
- if reqValue >= h {
- return blmBigger
- }
- if l >= reqValue {
- return !blmBigger
- }
- if (2*l-reqValue)*h > reqValue*reqValue {
- return !blmBigger
- }
- return blmBigger
- }
- if c.Touchscreen != o.Touchscreen && r.Touchscreen != 0 {
- return c.Touchscreen != 0
- }
- // input
- if c.InputFlags != 0 || o.InputFlags != 0 {
- myKeysHidden := c.InputFlags & MaskKeysHidden
- oKeysHidden := o.InputFlags & MaskKeysHidden
- reqKeysHidden := r.InputFlags & MaskKeysHidden
- if myKeysHidden != oKeysHidden && reqKeysHidden != 0 {
- switch {
- case myKeysHidden == 0:
- return false
- case oKeysHidden == 0:
- return true
- case reqKeysHidden == myKeysHidden:
- return true
- case reqKeysHidden == oKeysHidden:
- return false
- }
- }
- myNavHidden := c.InputFlags & MaskNavHidden
- oNavHidden := o.InputFlags & MaskNavHidden
- reqNavHidden := r.InputFlags & MaskNavHidden
- if myNavHidden != oNavHidden && reqNavHidden != 0 {
- switch {
- case myNavHidden == 0:
- return false
- case oNavHidden == 0:
- return true
- }
- }
- }
- if c.Keyboard != o.Keyboard && r.Keyboard != 0 {
- return c.Keyboard != 0
- }
- if c.Navigation != o.Navigation && r.Navigation != 0 {
- return c.Navigation != 0
- }
- // screen size
- if c.ScreenWidth != 0 || c.ScreenHeight != 0 || o.ScreenWidth != 0 || o.ScreenHeight != 0 {
- myDelta := 0
- otherDelta := 0
- if r.ScreenWidth != 0 {
- myDelta += int(r.ScreenWidth) - int(c.ScreenWidth)
- otherDelta += int(r.ScreenWidth) - int(o.ScreenWidth)
- }
- if r.ScreenHeight != 0 {
- myDelta += int(r.ScreenHeight) - int(c.ScreenHeight)
- otherDelta += int(r.ScreenHeight) - int(o.ScreenHeight)
- }
- if myDelta != otherDelta {
- return myDelta < otherDelta
- }
- }
- // version
- if c.SDKVersion != 0 || o.MinorVersion != 0 {
- if c.SDKVersion != o.SDKVersion && r.SDKVersion != 0 {
- return c.SDKVersion > o.SDKVersion
- }
- if c.MinorVersion != o.MinorVersion && r.MinorVersion != 0 {
- return c.MinorVersion != 0
- }
- }
- return false
- }
- // IsLocaleMoreSpecificThan a positive integer if this config is more specific than o,
- // a negative integer if |o| is more specific
- // and 0 if they're equally specific.
- func (c *ResTableConfig) IsLocaleMoreSpecificThan(o *ResTableConfig) int {
- if (c.Language != [2]uint8{} || c.Country != [2]uint8{}) || (o.Language != [2]uint8{} || o.Country != [2]uint8{}) {
- if c.Language != o.Language {
- if c.Language == [2]uint8{} {
- return -1
- }
- if o.Language == [2]uint8{} {
- return 1
- }
- }
- if c.Country != o.Country {
- if c.Country == [2]uint8{} {
- return -1
- }
- if o.Country == [2]uint8{} {
- return 1
- }
- }
- }
- return 0
- }
- // IsLocaleBetterThan returns true if c is a better locale match than o for the r configuration.
- func (c *ResTableConfig) IsLocaleBetterThan(o *ResTableConfig, r *ResTableConfig) bool {
- if r.Language == [2]uint8{} && r.Country == [2]uint8{} {
- // The request doesn't have a locale, so no resource is better
- // than the other.
- return false
- }
- if c.Language == [2]uint8{} && c.Country == [2]uint8{} && o.Language == [2]uint8{} && o.Country == [2]uint8{} {
- // The locales parts of both resources are empty, so no one is better
- // than the other.
- return false
- }
- if c.Language != o.Language {
- // The languages of the two resources are not the same.
- // the US English resource have traditionally lived for most apps.
- if r.Language == [2]uint8{'e', 'n'} {
- if r.Country == [2]uint8{'U', 'S'} {
- if c.Language != [2]uint8{} {
- return c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'}
- }
- return !(c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'})
- }
- }
- return c.Language != [2]uint8{}
- }
- if c.Country != o.Country {
- return c.Country != [2]uint8{}
- }
- return false
- }
- // Match returns true if c can be considered a match for the parameters in settings.
- func (c *ResTableConfig) Match(settings *ResTableConfig) bool {
- // nil ResTableConfig always matches.
- if settings == nil {
- return true
- } else if c == nil {
- return *settings == ResTableConfig{}
- }
- // match imsi
- if settings.Mcc == 0 {
- if c.Mcc != 0 {
- return false
- }
- } else {
- if c.Mcc != 0 && c.Mcc != settings.Mcc {
- return false
- }
- }
- if settings.Mnc == 0 {
- if c.Mnc != 0 {
- return false
- }
- } else {
- if c.Mnc != 0 && c.Mnc != settings.Mnc {
- return false
- }
- }
- // match locale
- if c.Language != [2]uint8{0, 0} {
- // Don't consider country and variants when deciding matches.
- // If two configs differ only in their country and variant,
- // they can be weeded out in the isMoreSpecificThan test.
- if c.Language != settings.Language {
- return false
- }
- if c.Country != [2]uint8{0, 0} {
- if c.Country != settings.Country {
- return false
- }
- }
- }
- // screen layout
- layoutDir := c.ScreenLayout & MaskLayoutDir
- setLayoutDir := settings.ScreenLayout & MaskLayoutDir
- if layoutDir != 0 && layoutDir != setLayoutDir {
- return false
- }
- screenSize := c.ScreenLayout & MaskScreenSize
- setScreenSize := settings.ScreenLayout & MaskScreenSize
- if screenSize != 0 && screenSize > setScreenSize {
- return false
- }
- screenLong := c.ScreenLayout & MaskScreenLong
- setScreenLong := settings.ScreenLayout & MaskScreenLong
- if screenLong != 0 && screenLong != setScreenLong {
- return false
- }
- // ui mode
- uiModeType := c.UIMode & MaskUIModeType
- setUIModeType := settings.UIMode & MaskUIModeType
- if uiModeType != 0 && uiModeType != setUIModeType {
- return false
- }
- uiModeNight := c.UIMode & MaskUIModeNight
- setUIModeNight := settings.UIMode & MaskUIModeNight
- if uiModeNight != 0 && uiModeNight != setUIModeNight {
- return false
- }
- // smallest screen width dp
- if c.SmallestScreenWidthDp != 0 &&
- c.SmallestScreenWidthDp > settings.SmallestScreenWidthDp {
- return false
- }
- // screen size dp
- if c.ScreenWidthDp != 0 &&
- c.ScreenWidthDp > settings.ScreenWidthDp {
- return false
- }
- if c.ScreenHeightDp != 0 &&
- c.ScreenHeightDp > settings.ScreenHeightDp {
- return false
- }
- // screen type
- if c.Orientation != 0 && c.Orientation != settings.Orientation {
- return false
- }
- if c.Touchscreen != 0 && c.Touchscreen != settings.Touchscreen {
- return false
- }
- // input
- if c.InputFlags != 0 {
- myKeysHidden := c.InputFlags & MaskKeysHidden
- oKeysHidden := settings.InputFlags & MaskKeysHidden
- if myKeysHidden != 0 && myKeysHidden != oKeysHidden {
- if myKeysHidden != KeysHiddenNo || oKeysHidden != KeysHiddenSoft {
- return false
- }
- }
- myNavHidden := c.InputFlags & MaskNavHidden
- oNavHidden := settings.InputFlags & MaskNavHidden
- if myNavHidden != 0 && myNavHidden != oNavHidden {
- return false
- }
- }
- if c.Keyboard != 0 && c.Keyboard != settings.Keyboard {
- return false
- }
- if c.Navigation != 0 && c.Navigation != settings.Navigation {
- return false
- }
- // screen size
- if c.ScreenWidth != 0 &&
- c.ScreenWidth > settings.ScreenWidth {
- return false
- }
- if c.ScreenHeight != 0 &&
- c.ScreenHeight > settings.ScreenHeight {
- return false
- }
- // version
- if settings.SDKVersion != 0 && c.SDKVersion != 0 &&
- c.SDKVersion > settings.SDKVersion {
- return false
- }
- if settings.MinorVersion != 0 && c.MinorVersion != 0 &&
- c.MinorVersion != settings.MinorVersion {
- return false
- }
- return true
- }
- // Locale returns the locale of the configuration.
- func (c *ResTableConfig) Locale() string {
- if c.Language[0] == 0 {
- return ""
- }
- if c.Country[0] == 0 {
- return fmt.Sprintf("%c%c", c.Language[0], c.Language[1])
- }
- return fmt.Sprintf("%c%c-%c%c", c.Language[0], c.Language[1], c.Country[0], c.Country[1])
- }
|