// Copyright 2018 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package res handles understanding and representing information about Android resources. package res import ( "errors" "fmt" "strconv" "strings" rdpb "src/tools/ak/res/proto/res_data_go_proto" ) var ( // ErrWrongType occurs when a type is used in an operation that it does not support. ErrWrongType = errors.New("this type cannot be used in this operation") ) // Type of resource (eg: string, layout, drawable) type Type rdpb.Resource_Type // Enum converts a Type into a enum proto value func (t Type) Enum() (rdpb.Resource_Type, error) { if !t.IsSerializable() { return rdpb.Resource_Type(ValueType), ErrWrongType } return rdpb.Resource_Type(t), nil } // IsSerializable indicates that the Type can be converted to a proto (some types are only for in memory operations). func (t Type) IsSerializable() bool { for _, a := range nonProtoTypes { if t == a { return false } } return true } // NestedClassName is the R.java nested class name for this type (if the type is understood by android). func (t Type) NestedClassName() (string, error) { if !t.IsReal() { return "", ErrWrongType } return typeToString[t], nil } // IsReal indicates that the type is known to the android framework. func (t Type) IsReal() bool { for _, a := range nonProtoTypes { if a == t { return false } } return true } // From frameworks/base/tools/aapt2/Resource.h, except UnknownType and ValueType // TODO(mauriciogg): use proto definitions and remove ValueType and UnknownType. const ( // UnknownType needs to be zero value UnknownType Type = -2 ValueType = -1 // Anim represents Android Anim resource types. Anim = Type(rdpb.Resource_ANIM) // Animator represents Android Animator resource types. Animator = Type(rdpb.Resource_ANIMATOR) // Array represents Android Array resource types. Array = Type(rdpb.Resource_ARRAY) // Attr represents Android Attr resource types. Attr = Type(rdpb.Resource_ATTR) // AttrPrivate represents Android AttrPrivate resource types. AttrPrivate = Type(rdpb.Resource_ATTR_PRIVATE) // Bool represents Android Bool resource types. Bool = Type(rdpb.Resource_BOOL) // Color represents Android Color resource types. Color = Type(rdpb.Resource_COLOR) // ConfigVarying represents Android ConfigVarying resource types, not really a type, but it shows up in some CTS tests ConfigVarying = Type(rdpb.Resource_CONFIG_VARYING) // Dimen represents Android Dimen resource types. Dimen = Type(rdpb.Resource_DIMEN) // Drawable represents Android Drawable resource types. Drawable = Type(rdpb.Resource_DRAWABLE) // Font represents Android Font resource types. Font = Type(rdpb.Resource_FONT) // Fraction represents Android Fraction resource types. Fraction = Type(rdpb.Resource_FRACTION) // ID represents Android Id resource types. ID = Type(rdpb.Resource_ID) // Integer represents Android Integer resource types. Integer = Type(rdpb.Resource_INTEGER) // Interpolator represents Android Interpolator resource types. Interpolator = Type(rdpb.Resource_INTERPOLATOR) // Layout represents Android Layout resource types. Layout = Type(rdpb.Resource_LAYOUT) // Menu represents Android Menu resource types. Menu = Type(rdpb.Resource_MENU) // Mipmap represents Android Mipmap resource types. Mipmap = Type(rdpb.Resource_MIPMAP) // Navigation represents Android Navigation resource types. Navigation = Type(rdpb.Resource_NAVIGATION) // Plurals represents Android Plurals resource types. Plurals = Type(rdpb.Resource_PLURALS) // Raw represents Android Raw resource types. Raw = Type(rdpb.Resource_RAW) // String represents Android String resource types. String = Type(rdpb.Resource_STRING) // Style represents Android Style resource types. Style = Type(rdpb.Resource_STYLE) // Styleable represents Android Styleable resource types. Styleable = Type(rdpb.Resource_STYLEABLE) // Transition represents Android Transition resource types. Transition = Type(rdpb.Resource_TRANSITION) // XML represents Android Xml resource types. XML = Type(rdpb.Resource_XML) ) var ( // A fixed mapping between the string representation of a type and its Type. typeToString = map[Type]string{ Anim: "anim", Animator: "animator", Array: "array", Attr: "attr", AttrPrivate: "^attr-private", Bool: "bool", Color: "color", ConfigVarying: "configVarying", Dimen: "dimen", Drawable: "drawable", Fraction: "fraction", Font: "font", ID: "id", Integer: "integer", Interpolator: "interpolator", Layout: "layout", Menu: "menu", Mipmap: "mipmap", Navigation: "navigation", Plurals: "plurals", Raw: "raw", String: "string", Style: "style", Styleable: "styleable", Transition: "transition", XML: "xml", } stringToType = make(map[string]Type) // AllTypes is a list of all known resource types. AllTypes = make([]Type, 0, len(typeToString)) // These types are not allowed to be serialized into proto format. nonProtoTypes = []Type{ValueType, UnknownType} ) // Kind indicates what type of resource file emits this resource. A resource can be found in // res/values folder (and therefore is a Value - which can be represented as a ResourceValue in // Android) or in folders outside of res/values (such as res/layout) and thus are not ResourceValues // but rather some external resource (such as an image or parsed xml file). type Kind uint8 const ( // Unknown should not be encountered. Unknown Kind = iota // Value can only be encountered in res/values folders. Value // NonValue can not be encountered in res/values folders. NonValue // Both is a Kind of Type which may be inside a res/values folder or in another res/ folder. Both ) var ( kindToString = map[Kind]string{ Unknown: "Unknown", Value: "Value", NonValue: "NonValue", Both: "Both", } // A fixed mapping between Type and Kind. TypesToKind = map[Type]Kind{ Anim: NonValue, Animator: NonValue, Array: Value, Attr: Value, AttrPrivate: Value, Bool: Value, Color: Both, ConfigVarying: Value, Dimen: Value, Drawable: NonValue, Font: NonValue, Fraction: Value, ID: Value, Integer: Value, Interpolator: NonValue, Layout: NonValue, Menu: NonValue, Mipmap: NonValue, Navigation: NonValue, Plurals: Value, Raw: NonValue, String: Value, Style: Value, Styleable: Value, Transition: NonValue, XML: NonValue, } ) // Density represents the dpi value of a resource. type Density uint16 // From frameworks/base/core/java/Android/content/res/Configuration.java const ( // UnspecifiedDensity is a default value indicating no dpi has been specified UnspecifiedDensity Density = 0 // LDPI has a dpi of 120 LDPI Density = 120 // MDPI has a dpi of 160 MDPI Density = 160 // TVDPI has a dpi of 213 TVDPI Density = 213 // HDPI has a dpi of 240 HDPI Density = 240 // XhDPI has a dpi of 320 XhDPI Density = 320 // XxhDPI has a dpi of 480 XxhDPI Density = 480 // XxxhDPI has a dpi of 640 XxxhDPI Density = 640 // AnyDPI indicates a resource which can be any dpi. AnyDPI Density = 0xfffe // NoDPI indicates the resources have no dpi constraints NoDPI Density = 0xffff dpiSuffix = "dpi" ) var ( densityToStr = map[Density]string{ LDPI: "ldpi", MDPI: "mdpi", TVDPI: "tvdpi", HDPI: "hdpi", XhDPI: "xhdpi", XxhDPI: "xxhdpi", XxxhDPI: "xxxhdpi", AnyDPI: "anydpi", NoDPI: "nodpi", } strToDensity = make(map[string]Density) ) // ParseValueOrType converts a string into a value type or well known type func ParseValueOrType(s string) (Type, error) { if s == "values" { return ValueType, nil } return ParseType(s) } // ParseType converts a string into a well known type func ParseType(s string) (Type, error) { if t, ok := stringToType[s]; ok { return t, nil } return UnknownType, fmt.Errorf("%s: unknown type", s) } // String for Type structs corresponds to the string format known to Android. func (t Type) String() string { if s, ok := typeToString[t]; ok { return s } return fmt.Sprintf("Type(%d)", t) } // Kind indicates the resource kind of this type. func (t Type) Kind() Kind { if t == ValueType { return Value } if t, ok := TypesToKind[t]; ok { return t } return Unknown } // ParseDensity converts a string representation of a density into a Density. func ParseDensity(s string) (Density, error) { if d, ok := strToDensity[s]; ok { return d, nil } if strings.HasSuffix(s, dpiSuffix) { parsed, err := strconv.ParseUint(s[0:len(s)-len(dpiSuffix)], 10, 16) if err != nil { return 0, fmt.Errorf("%s: unparsable: %v", s, err) } return Density(parsed), nil } return UnspecifiedDensity, nil } func init() { for k, v := range typeToString { AllTypes = append(AllTypes, k) stringToType[v] = k } for k, v := range densityToStr { strToDensity[v] = k } }