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]) }