summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/AaptAssets.cpp3
-rw-r--r--tools/aapt/tests/AaptConfig_test.cpp6
-rw-r--r--tools/apilint/apilint.py657
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java52
-rw-r--r--tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java193
-rw-r--r--tools/layoutlib/bridge/src/android/text/LineBreaker.java42
-rw-r--r--tools/layoutlib/bridge/src/android/text/LineWidth.java35
-rw-r--r--tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java262
-rw-r--r--tools/layoutlib/bridge/src/android/text/Primitive.java92
-rw-r--r--tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java105
-rw-r--r--tools/layoutlib/bridge/src/android/text/TabStops.java44
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java72
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java30
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java6
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java19
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java19
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java1
-rw-r--r--tools/obbtool/Android.mk4
21 files changed, 1329 insertions, 328 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 2d35129..871e04f 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -345,7 +345,8 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta
return ++currentIndex;
} else {
- if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
+ if ((part.length() == 2 || part.length() == 3)
+ && isAlpha(part) && strcmp("car", part.string())) {
setLanguage(part);
if (++currentIndex == size) {
return size;
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
index e795d81..ef3860c 100644
--- a/tools/aapt/tests/AaptConfig_test.cpp
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -76,3 +76,9 @@ TEST(AaptConfigTest, ParseQualifierAddedInApi13) {
EXPECT_TRUE(TestParse("sw600dp-v8", &config));
EXPECT_EQ(String8("sw600dp-v13"), config.toString());
}
+
+TEST(AaptConfigTest, TestParsingOfCarAttribute) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("car", &config));
+ EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
+}
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 393d2ec..5c6d870 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -48,8 +48,9 @@ def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
class Field():
- def __init__(self, clazz, raw, blame):
+ def __init__(self, clazz, line, raw, blame):
self.clazz = clazz
+ self.line = line
self.raw = raw.strip(" {;")
self.blame = blame
@@ -73,8 +74,9 @@ class Field():
class Method():
- def __init__(self, clazz, raw, blame):
+ def __init__(self, clazz, line, raw, blame):
self.clazz = clazz
+ self.line = line
self.raw = raw.strip(" {;")
self.blame = blame
@@ -110,8 +112,9 @@ class Method():
class Class():
- def __init__(self, pkg, raw, blame):
+ def __init__(self, pkg, line, raw, blame):
self.pkg = pkg
+ self.line = line
self.raw = raw.strip(" {;")
self.blame = blame
self.ctors = []
@@ -129,10 +132,14 @@ class Class():
if "extends" in raw:
self.extends = raw[raw.index("extends")+1]
+ self.extends_path = self.extends.split(".")
else:
self.extends = None
+ self.extends_path = []
self.fullname = self.pkg.name + "." + self.fullname
+ self.fullname_path = self.fullname.split(".")
+
self.name = self.fullname[self.fullname.rindex(".")+1:]
def __repr__(self):
@@ -140,75 +147,110 @@ class Class():
class Package():
- def __init__(self, raw, blame):
+ def __init__(self, line, raw, blame):
+ self.line = line
self.raw = raw.strip(" {;")
self.blame = blame
raw = raw.split()
self.name = raw[raw.index("package")+1]
+ self.name_path = self.name.split(".")
def __repr__(self):
return self.raw
-def parse_api(fn):
+def _parse_stream(f, clazz_cb=None):
+ line = 0
api = {}
pkg = None
clazz = None
blame = None
re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
-
- with open(fn) as f:
- for raw in f.readlines():
- raw = raw.rstrip()
- match = re_blame.match(raw)
- if match is not None:
- blame = match.groups()[0:2]
- raw = match.groups()[2]
- else:
- blame = None
-
- if raw.startswith("package"):
- pkg = Package(raw, blame)
- elif raw.startswith(" ") and raw.endswith("{"):
- clazz = Class(pkg, raw, blame)
+ for raw in f:
+ line += 1
+ raw = raw.rstrip()
+ match = re_blame.match(raw)
+ if match is not None:
+ blame = match.groups()[0:2]
+ raw = match.groups()[2]
+ else:
+ blame = None
+
+ if raw.startswith("package"):
+ pkg = Package(line, raw, blame)
+ elif raw.startswith(" ") and raw.endswith("{"):
+ # When provided with class callback, we treat as incremental
+ # parse and don't build up entire API
+ if clazz and clazz_cb:
+ clazz_cb(clazz)
+ clazz = Class(pkg, line, raw, blame)
+ if not clazz_cb:
api[clazz.fullname] = clazz
- elif raw.startswith(" ctor"):
- clazz.ctors.append(Method(clazz, raw, blame))
- elif raw.startswith(" method"):
- clazz.methods.append(Method(clazz, raw, blame))
- elif raw.startswith(" field"):
- clazz.fields.append(Field(clazz, raw, blame))
+ elif raw.startswith(" ctor"):
+ clazz.ctors.append(Method(clazz, line, raw, blame))
+ elif raw.startswith(" method"):
+ clazz.methods.append(Method(clazz, line, raw, blame))
+ elif raw.startswith(" field"):
+ clazz.fields.append(Field(clazz, line, raw, blame))
+
+ # Handle last trailing class
+ if clazz and clazz_cb:
+ clazz_cb(clazz)
return api
+class Failure():
+ def __init__(self, sig, clazz, detail, error, rule, msg):
+ self.sig = sig
+ self.error = error
+ self.rule = rule
+ self.msg = msg
+
+ if error:
+ self.head = "Error %s" % (rule) if rule else "Error"
+ dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg)
+ else:
+ self.head = "Warning %s" % (rule) if rule else "Warning"
+ dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg)
+
+ self.line = clazz.line
+ blame = clazz.blame
+ if detail is not None:
+ dump += "\n in " + repr(detail)
+ self.line = detail.line
+ blame = detail.blame
+ dump += "\n in " + repr(clazz)
+ dump += "\n in " + repr(clazz.pkg)
+ dump += "\n at line " + repr(self.line)
+ if blame is not None:
+ dump += "\n last modified by %s in %s" % (blame[1], blame[0])
+
+ self.dump = dump
+
+ def __repr__(self):
+ return self.dump
+
+
failures = {}
-def _fail(clazz, detail, msg):
+def _fail(clazz, detail, error, rule, msg):
"""Records an API failure to be processed later."""
global failures
sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
sig = sig.replace(" deprecated ", " ")
- res = msg
- blame = clazz.blame
- if detail is not None:
- res += "\n in " + repr(detail)
- blame = detail.blame
- res += "\n in " + repr(clazz)
- res += "\n in " + repr(clazz.pkg)
- if blame is not None:
- res += "\n last modified by %s in %s" % (blame[1], blame[0])
- failures[sig] = res
+ failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
+
-def warn(clazz, detail, msg):
- _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), format(reset=True), msg))
+def warn(clazz, detail, rule, msg):
+ _fail(clazz, detail, False, rule, msg)
-def error(clazz, detail, msg):
- _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK, bold=True), format(reset=True), msg))
+def error(clazz, detail, rule, msg):
+ _fail(clazz, detail, True, rule, msg)
def verify_constants(clazz):
@@ -218,13 +260,13 @@ def verify_constants(clazz):
for f in clazz.fields:
if "static" in f.split and "final" in f.split:
if re.match("[A-Z0-9_]+", f.name) is None:
- error(clazz, f, "Constant field names should be FOO_NAME")
+ error(clazz, f, "C2", "Constant field names must be FOO_NAME")
def verify_enums(clazz):
"""Enums are bad, mmkay?"""
if "extends java.lang.Enum" in clazz.raw:
- error(clazz, None, "Enums are not allowed")
+ error(clazz, None, "F5", "Enums are not allowed")
def verify_class_names(clazz):
@@ -234,9 +276,9 @@ def verify_class_names(clazz):
if re.match("android\.R\.[a-z]+", clazz.fullname): return
if re.search("[A-Z]{2,}", clazz.name) is not None:
- warn(clazz, None, "Class name style should be Mtp not MTP")
+ warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP")
if re.match("[^A-Z]", clazz.name):
- error(clazz, None, "Class must start with uppercase char")
+ error(clazz, None, "S1", "Class must start with uppercase char")
def verify_method_names(clazz):
@@ -247,9 +289,9 @@ def verify_method_names(clazz):
for m in clazz.methods:
if re.search("[A-Z]{2,}", m.name) is not None:
- warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
+ warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()")
if re.match("[^a-z]", m.name):
- error(clazz, m, "Method name must start with lowercase char")
+ error(clazz, m, "S1", "Method name must start with lowercase char")
def verify_callbacks(clazz):
@@ -259,17 +301,17 @@ def verify_callbacks(clazz):
if clazz.fullname == "android.speech.tts.SynthesisCallback": return
if clazz.name.endswith("Callbacks"):
- error(clazz, None, "Class name must not be plural")
+ error(clazz, None, "L1", "Callback class names should be singular")
if clazz.name.endswith("Observer"):
- warn(clazz, None, "Class should be named FooCallback")
+ warn(clazz, None, "L1", "Class should be named FooCallback")
if clazz.name.endswith("Callback"):
if "interface" in clazz.split:
- error(clazz, None, "Callback must be abstract class to enable extension in future API levels")
+ error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels")
for m in clazz.methods:
if not re.match("on[A-Z][a-z]*", m.name):
- error(clazz, m, "Callback method names must be onFoo() style")
+ error(clazz, m, "L1", "Callback method names must be onFoo() style")
def verify_listeners(clazz):
@@ -281,16 +323,16 @@ def verify_listeners(clazz):
if clazz.name.endswith("Listener"):
if " abstract class " in clazz.raw:
- error(clazz, None, "Listener should be an interface, otherwise renamed Callback")
+ error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
for m in clazz.methods:
if not re.match("on[A-Z][a-z]*", m.name):
- error(clazz, m, "Listener method names must be onFoo() style")
+ error(clazz, m, "L1", "Listener method names must be onFoo() style")
if len(clazz.methods) == 1 and clazz.name.startswith("On"):
m = clazz.methods[0]
if (m.name + "Listener").lower() != clazz.name.lower():
- error(clazz, m, "Single listener method name should match class name")
+ error(clazz, m, "L1", "Single listener method name must match class name")
def verify_actions(clazz):
@@ -308,7 +350,7 @@ def verify_actions(clazz):
if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
if not f.name.startswith("ACTION_"):
- error(clazz, f, "Intent action constant name must be ACTION_FOO")
+ error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO")
else:
if clazz.fullname == "android.content.Intent":
prefix = "android.intent.action"
@@ -320,7 +362,7 @@ def verify_actions(clazz):
prefix = clazz.pkg.name + ".action"
expected = prefix + "." + f.name[7:]
if f.value != expected:
- error(clazz, f, "Inconsistent action value; expected %s" % (expected))
+ error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected))
def verify_extras(clazz):
@@ -340,7 +382,7 @@ def verify_extras(clazz):
if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
if not f.name.startswith("EXTRA_"):
- error(clazz, f, "Intent extra must be EXTRA_FOO")
+ error(clazz, f, "C3", "Intent extra must be EXTRA_FOO")
else:
if clazz.pkg.name == "android.content" and clazz.name == "Intent":
prefix = "android.intent.extra"
@@ -350,7 +392,7 @@ def verify_extras(clazz):
prefix = clazz.pkg.name + ".extra"
expected = prefix + "." + f.name[6:]
if f.value != expected:
- error(clazz, f, "Inconsistent extra value; expected %s" % (expected))
+ error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected))
def verify_equals(clazz):
@@ -359,7 +401,7 @@ def verify_equals(clazz):
eq = "equals" in methods
hc = "hashCode" in methods
if eq != hc:
- error(clazz, None, "Must override both equals and hashCode; missing one")
+ error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
def verify_parcelable(clazz):
@@ -370,17 +412,17 @@ def verify_parcelable(clazz):
describe = [ i for i in clazz.methods if i.name == "describeContents" ]
if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
- error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
+ error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
def verify_protected(clazz):
- """Verify that no protected methods are allowed."""
+ """Verify that no protected methods or fields are allowed."""
for m in clazz.methods:
if "protected" in m.split:
- error(clazz, m, "No protected methods; must be public")
+ error(clazz, m, "M7", "Protected methods not allowed; must be public")
for f in clazz.fields:
if "protected" in f.split:
- error(clazz, f, "No protected fields; must be public")
+ error(clazz, f, "M7", "Protected fields not allowed; must be public")
def verify_fields(clazz):
@@ -410,18 +452,18 @@ def verify_fields(clazz):
elif clazz.fullname.startswith("android.util.Mutable"):
pass
else:
- error(clazz, f, "Bare fields must be marked final; consider adding accessors")
+ error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable")
if not "static" in f.split:
if not re.match("[a-z]([a-zA-Z]+)?", f.name):
- error(clazz, f, "Non-static fields must be named with myField style")
+ error(clazz, f, "S1", "Non-static fields must be named using myField style")
if re.match("[ms][A-Z]", f.name):
- error(clazz, f, "Don't expose your internal objects")
+ error(clazz, f, "F1", "Internal objects must not be exposed")
if re.match("[A-Z_]+", f.name):
if "static" not in f.split or "final" not in f.split:
- error(clazz, f, "Constants must be marked static final")
+ error(clazz, f, "C2", "Constants must be marked static final")
def verify_register(clazz):
@@ -434,34 +476,34 @@ def verify_register(clazz):
if m.name.startswith("register"):
other = "unregister" + m.name[8:]
if other not in methods:
- error(clazz, m, "Missing unregister method")
+ error(clazz, m, "L2", "Missing unregister method")
if m.name.startswith("unregister"):
other = "register" + m.name[10:]
if other not in methods:
- error(clazz, m, "Missing register method")
+ error(clazz, m, "L2", "Missing register method")
if m.name.startswith("add") or m.name.startswith("remove"):
- error(clazz, m, "Callback methods should be named register/unregister")
+ error(clazz, m, "L3", "Callback methods should be named register/unregister")
if "Listener" in m.raw:
if m.name.startswith("add"):
other = "remove" + m.name[3:]
if other not in methods:
- error(clazz, m, "Missing remove method")
+ error(clazz, m, "L2", "Missing remove method")
if m.name.startswith("remove") and not m.name.startswith("removeAll"):
other = "add" + m.name[6:]
if other not in methods:
- error(clazz, m, "Missing add method")
+ error(clazz, m, "L2", "Missing add method")
if m.name.startswith("register") or m.name.startswith("unregister"):
- error(clazz, m, "Listener methods should be named add/remove")
+ error(clazz, m, "L3", "Listener methods should be named add/remove")
def verify_sync(clazz):
"""Verify synchronized methods aren't exposed."""
for m in clazz.methods:
if "synchronized" in m.split:
- error(clazz, m, "Internal lock exposed")
+ error(clazz, m, "M5", "Internal locks must not be exposed")
def verify_intent_builder(clazz):
@@ -473,7 +515,7 @@ def verify_intent_builder(clazz):
if m.name.startswith("create") and m.name.endswith("Intent"):
pass
else:
- error(clazz, m, "Methods creating an Intent should be named createFooIntent()")
+ error(clazz, m, "FW1", "Methods creating an Intent must be named createFooIntent()")
def verify_helper_classes(clazz):
@@ -483,57 +525,45 @@ def verify_helper_classes(clazz):
if "extends android.app.Service" in clazz.raw:
test_methods = True
if not clazz.name.endswith("Service"):
- error(clazz, None, "Inconsistent class name; should be FooService")
+ error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
found = False
for f in clazz.fields:
if f.name == "SERVICE_INTERFACE":
found = True
if f.value != clazz.fullname:
- error(clazz, f, "Inconsistent interface constant; expected %s" % (clazz.fullname))
-
- if not found:
- warn(clazz, None, "Missing SERVICE_INTERFACE constant")
-
- if "abstract" in clazz.split and not clazz.fullname.startswith("android.service."):
- warn(clazz, None, "Services extended by developers should be under android.service")
+ error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
if "extends android.content.ContentProvider" in clazz.raw:
test_methods = True
if not clazz.name.endswith("Provider"):
- error(clazz, None, "Inconsistent class name; should be FooProvider")
+ error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
found = False
for f in clazz.fields:
if f.name == "PROVIDER_INTERFACE":
found = True
if f.value != clazz.fullname:
- error(clazz, f, "Inconsistent interface name; expected %s" % (clazz.fullname))
-
- if not found:
- warn(clazz, None, "Missing PROVIDER_INTERFACE constant")
-
- if "abstract" in clazz.split and not clazz.fullname.startswith("android.provider."):
- warn(clazz, None, "Providers extended by developers should be under android.provider")
+ error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname))
if "extends android.content.BroadcastReceiver" in clazz.raw:
test_methods = True
if not clazz.name.endswith("Receiver"):
- error(clazz, None, "Inconsistent class name; should be FooReceiver")
+ error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
if "extends android.app.Activity" in clazz.raw:
test_methods = True
if not clazz.name.endswith("Activity"):
- error(clazz, None, "Inconsistent class name; should be FooActivity")
+ error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
if test_methods:
for m in clazz.methods:
if "final" in m.split: continue
if not re.match("on[A-Z]", m.name):
if "abstract" in m.split:
- error(clazz, m, "Methods implemented by developers must be named onFoo()")
+ warn(clazz, m, None, "Methods implemented by developers should be named onFoo()")
else:
- warn(clazz, m, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
+ warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
def verify_builder(clazz):
@@ -543,7 +573,7 @@ def verify_builder(clazz):
if not clazz.name.endswith("Builder"): return
if clazz.name != "Builder":
- warn(clazz, None, "Builder should be defined as inner class")
+ warn(clazz, None, None, "Builder should be defined as inner class")
has_build = False
for m in clazz.methods:
@@ -555,26 +585,26 @@ def verify_builder(clazz):
if m.name.startswith("clear"): continue
if m.name.startswith("with"):
- error(clazz, m, "Builder methods names must follow setFoo() style")
+ warn(clazz, m, None, "Builder methods names should use setFoo() style")
if m.name.startswith("set"):
if not m.typ.endswith(clazz.fullname):
- warn(clazz, m, "Methods should return the builder")
+ warn(clazz, m, "M4", "Methods must return the builder object")
if not has_build:
- warn(clazz, None, "Missing build() method")
+ warn(clazz, None, None, "Missing build() method")
def verify_aidl(clazz):
"""Catch people exposing raw AIDL."""
if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
- error(clazz, None, "Exposing raw AIDL interface")
+ error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
def verify_internal(clazz):
"""Catch people exposing internal classes."""
if clazz.pkg.name.startswith("com.android"):
- error(clazz, None, "Exposing internal class")
+ error(clazz, None, None, "Internal classes must not be exposed")
def verify_layering(clazz):
@@ -609,38 +639,56 @@ def verify_layering(clazz):
for f in clazz.fields:
ir = rank(f.typ)
if ir and ir < cr:
- warn(clazz, f, "Field type violates package layering")
+ warn(clazz, f, "FW6", "Field type violates package layering")
for m in clazz.methods:
ir = rank(m.typ)
if ir and ir < cr:
- warn(clazz, m, "Method return type violates package layering")
+ warn(clazz, m, "FW6", "Method return type violates package layering")
for arg in m.args:
ir = rank(arg)
if ir and ir < cr:
- warn(clazz, m, "Method argument type violates package layering")
+ warn(clazz, m, "FW6", "Method argument type violates package layering")
-def verify_boolean(clazz, api):
- """Catches people returning boolean from getFoo() style methods.
- Ignores when matching setFoo() is present."""
+def verify_boolean(clazz):
+ """Verifies that boolean accessors are named correctly.
+ For example, hasFoo() and setHasFoo()."""
- methods = [ m.name for m in clazz.methods ]
+ def is_get(m): return len(m.args) == 0 and m.typ == "boolean"
+ def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean"
+
+ gets = [ m for m in clazz.methods if is_get(m) ]
+ sets = [ m for m in clazz.methods if is_set(m) ]
- builder = clazz.fullname + ".Builder"
- builder_methods = []
- if builder in api:
- builder_methods = [ m.name for m in api[builder].methods ]
+ def error_if_exists(methods, trigger, expected, actual):
+ for m in methods:
+ if m.name == actual:
+ error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected))
for m in clazz.methods:
- if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0:
- setter = "set" + m.name[3:]
- if setter in methods:
- pass
- elif builder is not None and setter in builder_methods:
- pass
- else:
- warn(clazz, m, "Methods returning boolean should be named isFoo, hasFoo, areFoo")
+ if is_get(m):
+ if re.match("is[A-Z]", m.name):
+ target = m.name[2:]
+ expected = "setIs" + target
+ error_if_exists(sets, m.name, expected, "setHas" + target)
+ elif re.match("has[A-Z]", m.name):
+ target = m.name[3:]
+ expected = "setHas" + target
+ error_if_exists(sets, m.name, expected, "setIs" + target)
+ error_if_exists(sets, m.name, expected, "set" + target)
+ elif re.match("get[A-Z]", m.name):
+ target = m.name[3:]
+ expected = "set" + target
+ error_if_exists(sets, m.name, expected, "setIs" + target)
+ error_if_exists(sets, m.name, expected, "setHas" + target)
+
+ if is_set(m):
+ if re.match("set[A-Z]", m.name):
+ target = m.name[3:]
+ expected = "get" + target
+ error_if_exists(sets, m.name, expected, "is" + target)
+ error_if_exists(sets, m.name, expected, "has" + target)
def verify_collections(clazz):
@@ -651,10 +699,10 @@ def verify_collections(clazz):
"java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
for m in clazz.methods:
if m.typ in bad:
- error(clazz, m, "Return type is concrete collection; should be interface")
+ error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface")
for arg in m.args:
if arg in bad:
- error(clazz, m, "Argument is concrete collection; should be interface")
+ error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface")
def verify_flags(clazz):
@@ -669,49 +717,291 @@ def verify_flags(clazz):
scope = f.name[0:f.name.index("FLAG_")]
if val & known[scope]:
- warn(clazz, f, "Found overlapping flag constant value")
+ warn(clazz, f, "C1", "Found overlapping flag constant value")
known[scope] |= val
-def verify_style(api):
- """Find all style issues in the given API level."""
+def verify_exception(clazz):
+ """Verifies that methods don't throw generic exceptions."""
+ for m in clazz.methods:
+ if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw:
+ error(clazz, m, "S1", "Methods must not throw generic exceptions")
+
+
+def verify_google(clazz):
+ """Verifies that APIs never reference Google."""
+
+ if re.search("google", clazz.raw, re.IGNORECASE):
+ error(clazz, None, None, "Must never reference Google")
+
+ test = []
+ test.extend(clazz.ctors)
+ test.extend(clazz.fields)
+ test.extend(clazz.methods)
+
+ for t in test:
+ if re.search("google", t.raw, re.IGNORECASE):
+ error(clazz, t, None, "Must never reference Google")
+
+
+def verify_bitset(clazz):
+ """Verifies that we avoid using heavy BitSet."""
+
+ for f in clazz.fields:
+ if f.typ == "java.util.BitSet":
+ error(clazz, f, None, "Field type must not be heavy BitSet")
+
+ for m in clazz.methods:
+ if m.typ == "java.util.BitSet":
+ error(clazz, m, None, "Return type must not be heavy BitSet")
+ for arg in m.args:
+ if arg == "java.util.BitSet":
+ error(clazz, m, None, "Argument type must not be heavy BitSet")
+
+
+def verify_manager(clazz):
+ """Verifies that FooManager is only obtained from Context."""
+
+ if not clazz.name.endswith("Manager"): return
+
+ for c in clazz.ctors:
+ error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors")
+
+
+def verify_boxed(clazz):
+ """Verifies that methods avoid boxed primitives."""
+
+ boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"]
+
+ for c in clazz.ctors:
+ for arg in c.args:
+ if arg in boxed:
+ error(clazz, c, "M11", "Must avoid boxed primitives")
+
+ for f in clazz.fields:
+ if f.typ in boxed:
+ error(clazz, f, "M11", "Must avoid boxed primitives")
+
+ for m in clazz.methods:
+ if m.typ in boxed:
+ error(clazz, m, "M11", "Must avoid boxed primitives")
+ for arg in m.args:
+ if arg in boxed:
+ error(clazz, m, "M11", "Must avoid boxed primitives")
+
+
+def verify_static_utils(clazz):
+ """Verifies that helper classes can't be constructed."""
+ if clazz.fullname.startswith("android.opengl"): return
+ if re.match("android\.R\.[a-z]+", clazz.fullname): return
+
+ if len(clazz.fields) > 0: return
+ if len(clazz.methods) == 0: return
+
+ for m in clazz.methods:
+ if "static" not in m.split:
+ return
+
+ # At this point, we have no fields, and all methods are static
+ if len(clazz.ctors) > 0:
+ error(clazz, None, None, "Fully-static utility classes must not have constructor")
+
+
+def verify_overload_args(clazz):
+ """Verifies that method overloads add new arguments at the end."""
+ if clazz.fullname.startswith("android.opengl"): return
+
+ overloads = collections.defaultdict(list)
+ for m in clazz.methods:
+ if "deprecated" in m.split: continue
+ overloads[m.name].append(m)
+
+ for name, methods in overloads.items():
+ if len(methods) <= 1: continue
+
+ # Look for arguments common across all overloads
+ def cluster(args):
+ count = collections.defaultdict(int)
+ res = set()
+ for i in range(len(args)):
+ a = args[i]
+ res.add("%s#%d" % (a, count[a]))
+ count[a] += 1
+ return res
+
+ common_args = cluster(methods[0].args)
+ for m in methods:
+ common_args = common_args & cluster(m.args)
+
+ if len(common_args) == 0: continue
+
+ # Require that all common arguments are present at start of signature
+ locked_sig = None
+ for m in methods:
+ sig = m.args[0:len(common_args)]
+ if not common_args.issubset(cluster(sig)):
+ warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args)))
+ elif not locked_sig:
+ locked_sig = sig
+ elif locked_sig != sig:
+ error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig)))
+
+
+def verify_callback_handlers(clazz):
+ """Verifies that methods adding listener/callback have overload
+ for specifying delivery thread."""
+
+ # Ignore UI packages which assume main thread
+ skip = [
+ "animation",
+ "view",
+ "graphics",
+ "transition",
+ "widget",
+ "webkit",
+ ]
+ for s in skip:
+ if s in clazz.pkg.name_path: return
+ if s in clazz.extends_path: return
+
+ # Ignore UI classes which assume main thread
+ if "app" in clazz.pkg.name_path or "app" in clazz.extends_path:
+ for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]:
+ if s in clazz.fullname: return
+ if "content" in clazz.pkg.name_path or "content" in clazz.extends_path:
+ for s in ["Loader"]:
+ if s in clazz.fullname: return
+
+ found = {}
+ by_name = collections.defaultdict(list)
+ for m in clazz.methods:
+ if m.name.startswith("unregister"): continue
+ if m.name.startswith("remove"): continue
+ if re.match("on[A-Z]+", m.name): continue
+
+ by_name[m.name].append(m)
+
+ for a in m.args:
+ if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"):
+ found[m.name] = m
+
+ for f in found.values():
+ takes_handler = False
+ for m in by_name[f.name]:
+ if "android.os.Handler" in m.args:
+ takes_handler = True
+ if not takes_handler:
+ warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler")
+
+
+def verify_context_first(clazz):
+ """Verifies that methods accepting a Context keep it the first argument."""
+ examine = clazz.ctors + clazz.methods
+ for m in examine:
+ if len(m.args) > 1 and m.args[0] != "android.content.Context":
+ if "android.content.Context" in m.args[1:]:
+ error(clazz, m, "M3", "Context is distinct, so it must be the first argument")
+
+
+def verify_listener_last(clazz):
+ """Verifies that methods accepting a Listener or Callback keep them as last arguments."""
+ examine = clazz.ctors + clazz.methods
+ for m in examine:
+ if "Listener" in m.name or "Callback" in m.name: continue
+ found = False
+ for a in m.args:
+ if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
+ found = True
+ elif found and a != "android.os.Handler":
+ warn(clazz, m, "M3", "Listeners should always be at end of argument list")
+
+
+def verify_resource_names(clazz):
+ """Verifies that resource names have consistent case."""
+ if not re.match("android\.R\.[a-z]+", clazz.fullname): return
+
+ # Resources defined by files are foo_bar_baz
+ if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]:
+ for f in clazz.fields:
+ if re.match("[a-z1-9_]+$", f.name): continue
+ error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style")
+
+ # Resources defined inside files are fooBarBaz
+ if clazz.name in ["array","attr","id","bool","fraction","integer"]:
+ for f in clazz.fields:
+ if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue
+ if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue
+ if re.match("state_[a-z_]*$", f.name): continue
+
+ if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue
+ error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style")
+
+ # Styles are FooBar_Baz
+ if clazz.name in ["style"]:
+ for f in clazz.fields:
+ if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue
+ error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style")
+
+
+def examine_clazz(clazz):
+ """Find all style issues in the given class."""
+ if clazz.pkg.name.startswith("java"): return
+ if clazz.pkg.name.startswith("junit"): return
+ if clazz.pkg.name.startswith("org.apache"): return
+ if clazz.pkg.name.startswith("org.xml"): return
+ if clazz.pkg.name.startswith("org.json"): return
+ if clazz.pkg.name.startswith("org.w3c"): return
+
+ verify_constants(clazz)
+ verify_enums(clazz)
+ verify_class_names(clazz)
+ verify_method_names(clazz)
+ verify_callbacks(clazz)
+ verify_listeners(clazz)
+ verify_actions(clazz)
+ verify_extras(clazz)
+ verify_equals(clazz)
+ verify_parcelable(clazz)
+ verify_protected(clazz)
+ verify_fields(clazz)
+ verify_register(clazz)
+ verify_sync(clazz)
+ verify_intent_builder(clazz)
+ verify_helper_classes(clazz)
+ verify_builder(clazz)
+ verify_aidl(clazz)
+ verify_internal(clazz)
+ verify_layering(clazz)
+ verify_boolean(clazz)
+ verify_collections(clazz)
+ verify_flags(clazz)
+ verify_exception(clazz)
+ verify_google(clazz)
+ verify_bitset(clazz)
+ verify_manager(clazz)
+ verify_boxed(clazz)
+ verify_static_utils(clazz)
+ verify_overload_args(clazz)
+ verify_callback_handlers(clazz)
+ verify_context_first(clazz)
+ verify_listener_last(clazz)
+ verify_resource_names(clazz)
+
+
+def examine_stream(stream):
+ """Find all style issues in the given API stream."""
global failures
+ failures = {}
+ _parse_stream(stream, examine_clazz)
+ return failures
+
+def examine_api(api):
+ """Find all style issues in the given parsed API."""
+ global failures
failures = {}
for key in sorted(api.keys()):
- clazz = api[key]
-
- if clazz.pkg.name.startswith("java"): continue
- if clazz.pkg.name.startswith("junit"): continue
- if clazz.pkg.name.startswith("org.apache"): continue
- if clazz.pkg.name.startswith("org.xml"): continue
- if clazz.pkg.name.startswith("org.json"): continue
- if clazz.pkg.name.startswith("org.w3c"): continue
-
- verify_constants(clazz)
- verify_enums(clazz)
- verify_class_names(clazz)
- verify_method_names(clazz)
- verify_callbacks(clazz)
- verify_listeners(clazz)
- verify_actions(clazz)
- verify_extras(clazz)
- verify_equals(clazz)
- verify_parcelable(clazz)
- verify_protected(clazz)
- verify_fields(clazz)
- verify_register(clazz)
- verify_sync(clazz)
- verify_intent_builder(clazz)
- verify_helper_classes(clazz)
- verify_builder(clazz)
- verify_aidl(clazz)
- verify_internal(clazz)
- verify_layering(clazz)
- verify_boolean(clazz, api)
- verify_collections(clazz)
- verify_flags(clazz)
-
+ examine_clazz(api[key])
return failures
@@ -749,49 +1039,52 @@ def verify_compat(cur, prev):
prev_clazz = prev[key]
if not class_exists(cur, prev_clazz):
- error(prev_clazz, None, "Class removed or incompatible change")
+ error(prev_clazz, None, None, "Class removed or incompatible change")
continue
cur_clazz = cur[key]
for test in prev_clazz.ctors:
if not ctor_exists(cur, cur_clazz, test):
- error(prev_clazz, prev_ctor, "Constructor removed or incompatible change")
+ error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change")
methods = all_methods(prev, prev_clazz)
for test in methods:
if not method_exists(cur, cur_clazz, test):
- error(prev_clazz, test, "Method removed or incompatible change")
+ error(prev_clazz, test, None, "Method removed or incompatible change")
for test in prev_clazz.fields:
if not field_exists(cur, cur_clazz, test):
- error(prev_clazz, test, "Field removed or incompatible change")
+ error(prev_clazz, test, None, "Field removed or incompatible change")
return failures
-cur = parse_api(sys.argv[1])
-cur_fail = verify_style(cur)
+if __name__ == "__main__":
+ with open(sys.argv[1]) as f:
+ cur_fail = examine_stream(f)
-if len(sys.argv) > 2:
- prev = parse_api(sys.argv[2])
- prev_fail = verify_style(prev)
+ if len(sys.argv) > 2:
+ with open(sys.argv[2]) as f:
+ prev_fail = examine_stream(f)
- # ignore errors from previous API level
- for p in prev_fail:
- if p in cur_fail:
- del cur_fail[p]
+ # ignore errors from previous API level
+ for p in prev_fail:
+ if p in cur_fail:
+ del cur_fail[p]
- # look for compatibility issues
- compat_fail = verify_compat(cur, prev)
+ """
+ # NOTE: disabled because of memory pressure
+ # look for compatibility issues
+ compat_fail = verify_compat(cur, prev)
- print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
- for f in sorted(compat_fail):
- print compat_fail[f]
- print
+ print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(compat_fail):
+ print compat_fail[f]
+ print
+ """
-
-print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
-for f in sorted(cur_fail):
- print cur_fail[f]
- print
+ print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(cur_fail):
+ print cur_fail[f]
+ print
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 7b07404..8ffd1d8 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -251,7 +251,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static int getFlags(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -264,7 +264,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setFlags(Paint thisPaint, int flags) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -280,7 +280,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static int getHinting(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return Paint.HINTING_ON;
}
@@ -291,7 +291,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setHinting(Paint thisPaint, int mode) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -337,7 +337,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static int getColor(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -348,7 +348,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setColor(Paint thisPaint, int color) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -359,7 +359,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static int getAlpha(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -370,7 +370,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setAlpha(Paint thisPaint, int a) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -381,7 +381,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getStrokeWidth(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 1.f;
}
@@ -392,7 +392,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -403,7 +403,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getStrokeMiter(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 1.f;
}
@@ -414,7 +414,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -441,14 +441,14 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
}
@LayoutlibDelegate
/*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -459,7 +459,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getTextSize(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 1.f;
}
@@ -470,7 +470,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setTextSize(Paint thisPaint, float textSize) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -482,7 +482,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getTextScaleX(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 1.f;
}
@@ -493,7 +493,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -505,7 +505,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getTextSkewX(Paint thisPaint) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 1.f;
}
@@ -516,7 +516,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
@@ -528,7 +528,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float ascent(Paint thisPaint) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -545,7 +545,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float descent(Paint thisPaint) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -562,7 +562,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -573,7 +573,7 @@ public class Paint_Delegate {
@LayoutlibDelegate
/*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -599,7 +599,7 @@ public class Paint_Delegate {
/*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
int count, int bidiFlags) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return 0;
}
@@ -1232,7 +1232,7 @@ public class Paint_Delegate {
private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
// get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.getNativeInstance());
if (delegate == null) {
return;
}
diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
new file mode 100644
index 0000000..24e4b54
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+import com.android.annotations.NonNull;
+
+import android.text.Primitive.PrimitiveType;
+import android.text.StaticLayout.LineBreaks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY;
+
+// Based on the native implementation of GreedyLineBreaker in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+public class GreedyLineBreaker extends LineBreaker {
+
+ public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
+ @NonNull TabStops tabStops) {
+ super(primitives, lineWidth, tabStops);
+ }
+
+ @Override
+ public void computeBreaks(LineBreaks lineBreaks) {
+ BreakInfo breakInfo = new BreakInfo();
+ int lineNum = 0;
+ float width = 0, printedWidth = 0;
+ boolean breakFound = false, goodBreakFound = false;
+ int breakIndex = 0, goodBreakIndex = 0;
+ float breakWidth = 0, goodBreakWidth = 0;
+ int firstTabIndex = Integer.MAX_VALUE;
+
+ float maxWidth = mLineWidth.getLineWidth(lineNum);
+
+ int numPrimitives = mPrimitives.size();
+ // greedily fit as many characters as possible on each line
+ // loop over all primitives, and choose the best break point
+ // (if possible, a break point without splitting a word)
+ // after going over the maximum length
+ for (int i = 0; i < numPrimitives; i++) {
+ Primitive p = mPrimitives.get(i);
+
+ // update the current line width
+ if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
+ width += p.width;
+ if (p.type == PrimitiveType.BOX) {
+ printedWidth = width;
+ }
+ } else if (p.type == PrimitiveType.VARIABLE) {
+ width = mTabStops.width(width);
+ // keep track of first tab character in the region we are examining
+ // so we can determine whether or not a line contains a tab
+ firstTabIndex = Math.min(firstTabIndex, i);
+ }
+
+ // find the best break point for the characters examined so far
+ if (printedWidth > maxWidth) {
+ //noinspection StatementWithEmptyBody
+ if (breakFound || goodBreakFound) {
+ if (goodBreakFound) {
+ // a true line break opportunity existed in the characters examined so far,
+ // so there is no need to split a word
+ i = goodBreakIndex; // no +1 because of i++
+ lineNum++;
+ maxWidth = mLineWidth.getLineWidth(lineNum);
+ breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location);
+ breakInfo.mWidthsList.add(goodBreakWidth);
+ breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex);
+ firstTabIndex = Integer.MAX_VALUE;
+ } else {
+ // must split a word because there is no other option
+ i = breakIndex; // no +1 because of i++
+ lineNum++;
+ maxWidth = mLineWidth.getLineWidth(lineNum);
+ breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location);
+ breakInfo.mWidthsList.add(breakWidth);
+ breakInfo.mFlagsList.add(firstTabIndex < breakIndex);
+ firstTabIndex = Integer.MAX_VALUE;
+ }
+ printedWidth = width = 0;
+ goodBreakFound = breakFound = false;
+ goodBreakWidth = breakWidth = 0;
+ continue;
+ } else {
+ // no choice, keep going... must make progress by putting at least one
+ // character on a line, even if part of that character is cut off --
+ // there is no other option
+ }
+ }
+
+ // update possible break points
+ if (p.type == PrimitiveType.PENALTY &&
+ p.penalty < PENALTY_INFINITY) {
+ // this does not handle penalties with width
+
+ // handle forced line break
+ if (p.penalty == -PENALTY_INFINITY) {
+ lineNum++;
+ maxWidth = mLineWidth.getLineWidth(lineNum);
+ breakInfo.mBreaksList.add(p.location);
+ breakInfo.mWidthsList.add(printedWidth);
+ breakInfo.mFlagsList.add(firstTabIndex < i);
+ firstTabIndex = Integer.MAX_VALUE;
+ printedWidth = width = 0;
+ goodBreakFound = breakFound = false;
+ goodBreakWidth = breakWidth = 0;
+ continue;
+ }
+ if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
+ breakFound = true;
+ breakIndex = i;
+ breakWidth = printedWidth;
+ }
+ if (i > goodBreakIndex && printedWidth <= maxWidth) {
+ goodBreakFound = true;
+ goodBreakIndex = i;
+ goodBreakWidth = printedWidth;
+ }
+ } else if (p.type == PrimitiveType.WORD_BREAK) {
+ // only do this if necessary -- we don't want to break words
+ // when possible, but sometimes it is unavoidable
+ if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
+ breakFound = true;
+ breakIndex = i;
+ breakWidth = printedWidth;
+ }
+ }
+ }
+
+ if (breakFound || goodBreakFound) {
+ // output last break if there are more characters to output
+ if (goodBreakFound) {
+ breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location);
+ breakInfo.mWidthsList.add(goodBreakWidth);
+ breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex);
+ } else {
+ breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location);
+ breakInfo.mWidthsList.add(breakWidth);
+ breakInfo.mFlagsList.add(firstTabIndex < breakIndex);
+ }
+ }
+ breakInfo.copyTo(lineBreaks);
+ }
+
+ private static class BreakInfo {
+ List<Integer> mBreaksList = new ArrayList<Integer>();
+ List<Float> mWidthsList = new ArrayList<Float>();
+ List<Boolean> mFlagsList = new ArrayList<Boolean>();
+
+ public void copyTo(LineBreaks lineBreaks) {
+ if (lineBreaks.breaks.length != mBreaksList.size()) {
+ lineBreaks.breaks = new int[mBreaksList.size()];
+ lineBreaks.widths = new float[mWidthsList.size()];
+ lineBreaks.flags = new boolean[mFlagsList.size()];
+ }
+
+ int i = 0;
+ for (int b : mBreaksList) {
+ lineBreaks.breaks[i] = b;
+ i++;
+ }
+ i = 0;
+ for (float b : mWidthsList) {
+ lineBreaks.widths[i] = b;
+ i++;
+ }
+ i = 0;
+ for (boolean b : mFlagsList) {
+ lineBreaks.flags[i] = b;
+ i++;
+ }
+
+ mBreaksList = null;
+ mWidthsList = null;
+ mFlagsList = null;
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
new file mode 100644
index 0000000..8be3635
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+import android.text.StaticLayout.LineBreaks;
+import com.android.annotations.NonNull;
+
+import java.util.Collections;
+import java.util.List;
+
+// Based on the native implementation of LineBreaker in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+public abstract class LineBreaker {
+
+ protected final @NonNull List<Primitive> mPrimitives;
+ protected final @NonNull LineWidth mLineWidth;
+ protected final @NonNull TabStops mTabStops;
+
+ public LineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
+ @NonNull TabStops tabStops) {
+ mPrimitives = Collections.unmodifiableList(primitives);
+ mLineWidth = lineWidth;
+ mTabStops = tabStops;
+ }
+
+ @NonNull
+ public abstract void computeBreaks(@NonNull LineBreaks breakInfo);
+}
diff --git a/tools/layoutlib/bridge/src/android/text/LineWidth.java b/tools/layoutlib/bridge/src/android/text/LineWidth.java
new file mode 100644
index 0000000..2ea886d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/LineWidth.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+// Based on the native implementation of LineWidth in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+public class LineWidth {
+ private final float mFirstWidth;
+ private final int mFirstWidthLineCount;
+ private float mRestWidth;
+
+ public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) {
+ mFirstWidth = firstWidth;
+ mFirstWidthLineCount = firstWidthLineCount;
+ mRestWidth = restWidth;
+ }
+
+ public float getLineWidth(int line) {
+ return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
new file mode 100644
index 0000000..d5d7798
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+import com.android.annotations.NonNull;
+
+import android.text.Primitive.PrimitiveType;
+import android.text.StaticLayout.LineBreaks;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY;
+
+
+// Based on the native implementation of OptimizingLineBreaker in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+/**
+ * A more complex version of line breaking where we try to prevent the right edge from being too
+ * jagged.
+ */
+public class OptimizingLineBreaker extends LineBreaker {
+
+ public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
+ @NonNull TabStops tabStops) {
+ super(primitives, lineWidth, tabStops);
+ }
+
+ @Override
+ public void computeBreaks(@NonNull LineBreaks breakInfo) {
+ int numBreaks = mPrimitives.size();
+ assert numBreaks > 0;
+ if (numBreaks == 1) {
+ // This can be true only if it's an empty paragraph.
+ Primitive p = mPrimitives.get(0);
+ assert p.type == PrimitiveType.PENALTY;
+ breakInfo.breaks = new int[]{0};
+ breakInfo.widths = new float[]{p.width};
+ breakInfo.flags = new boolean[]{false};
+ return;
+ }
+ Node[] opt = new Node[numBreaks];
+ opt[0] = new Node(-1, 0, 0, 0, false);
+ opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false);
+
+ ArrayList<Integer> active = new ArrayList<Integer>();
+ active.add(0);
+ int lastBreak = 0;
+ for (int i = 0; i < numBreaks; i++) {
+ Primitive p = mPrimitives.get(i);
+ if (p.type == PrimitiveType.PENALTY) {
+ boolean finalBreak = (i + 1 == numBreaks);
+ Node bestBreak = null;
+
+ for (ListIterator<Integer> it = active.listIterator(); it.hasNext();
+ /* incrementing done in loop */) {
+ int pos = it.next();
+ int lines = opt[pos].mPrevCount;
+ float maxWidth = mLineWidth.getLineWidth(lines);
+ // we have to compute metrics every time --
+ // we can't really pre-compute this stuff and just deal with breaks
+ // because of the way tab characters work, this makes it computationally
+ // harder, but this way, we can still optimize while treating tab characters
+ // correctly
+ LineMetrics lineMetrics = computeMetrics(pos, i);
+ if (lineMetrics.mPrintedWidth <= maxWidth) {
+ float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth,
+ finalBreak, p.penalty) + opt[pos].mDemerits;
+ if (bestBreak == null || demerits < bestBreak.mDemerits) {
+ if (bestBreak == null) {
+ bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits,
+ lineMetrics.mPrintedWidth, lineMetrics.mHasTabs);
+ } else {
+ bestBreak.mPrev = pos;
+ bestBreak.mPrevCount = opt[pos].mPrevCount + 1;
+ bestBreak.mDemerits = demerits;
+ bestBreak.mWidth = lineMetrics.mPrintedWidth;
+ bestBreak.mHasTabs = lineMetrics.mHasTabs;
+ }
+ }
+ } else {
+ it.remove();
+ }
+ }
+ if (p.penalty == -PENALTY_INFINITY) {
+ active.clear();
+ }
+ if (bestBreak != null) {
+ opt[i] = bestBreak;
+ active.add(i);
+ lastBreak = i;
+ }
+ if (active.isEmpty()) {
+ // we can't give up!
+ LineMetrics lineMetrics = new LineMetrics();
+ int lines = opt[lastBreak].mPrevCount;
+ float maxWidth = mLineWidth.getLineWidth(lines);
+ int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics);
+ opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/,
+ lineMetrics.mWidth, lineMetrics.mHasTabs);
+ active.add(breakIndex);
+ lastBreak = breakIndex;
+ i = breakIndex; // incremented by i++
+ }
+ }
+ }
+
+ int idx = numBreaks - 1;
+ int count = opt[idx].mPrevCount;
+ resize(breakInfo, count);
+ while (opt[idx].mPrev != -1) {
+ count--;
+ assert count >=0;
+
+ breakInfo.breaks[count] = mPrimitives.get(idx).location;
+ breakInfo.widths[count] = opt[idx].mWidth;
+ breakInfo.flags [count] = opt[idx].mHasTabs;
+ idx = opt[idx].mPrev;
+ }
+ }
+
+ private static void resize(LineBreaks lineBreaks, int size) {
+ if (lineBreaks.breaks.length == size) {
+ return;
+ }
+ int[] breaks = new int[size];
+ float[] widths = new float[size];
+ boolean[] flags = new boolean[size];
+
+ int toCopy = Math.min(size, lineBreaks.breaks.length);
+ System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);
+ System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy);
+ System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy);
+
+ lineBreaks.breaks = breaks;
+ lineBreaks.widths = widths;
+ lineBreaks.flags = flags;
+ }
+
+ @NonNull
+ private LineMetrics computeMetrics(int start, int end) {
+ boolean f = false;
+ float w = 0, pw = 0;
+ for (int i = start; i < end; i++) {
+ Primitive p = mPrimitives.get(i);
+ if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
+ w += p.width;
+ if (p.type == PrimitiveType.BOX) {
+ pw = w;
+ }
+ } else if (p.type == PrimitiveType.VARIABLE) {
+ w = mTabStops.width(w);
+ f = true;
+ }
+ }
+ return new LineMetrics(w, pw, f);
+ }
+
+ private static float computeDemerits(float maxWidth, float width, boolean finalBreak,
+ float penalty) {
+ float deviation = finalBreak ? 0 : maxWidth - width;
+ return (deviation * deviation) + penalty;
+ }
+
+ /**
+ * @return the last break position or -1 if failed.
+ */
+ @SuppressWarnings("ConstantConditions") // method too complex to be analyzed.
+ private int desperateBreak(int start, int limit, float maxWidth,
+ @NonNull LineMetrics lineMetrics) {
+ float w = 0, pw = 0;
+ boolean breakFound = false;
+ int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE;
+ for (int i = start; i < limit; i++) {
+ Primitive p = mPrimitives.get(i);
+
+ if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
+ w += p.width;
+ if (p.type == PrimitiveType.BOX) {
+ pw = w;
+ }
+ } else if (p.type == PrimitiveType.VARIABLE) {
+ w = mTabStops.width(w);
+ firstTabIndex = Math.min(firstTabIndex, i);
+ }
+
+ if (pw > maxWidth && breakFound) {
+ break;
+ }
+
+ // must make progress
+ if (i > start &&
+ (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) {
+ breakFound = true;
+ breakIndex = i;
+ }
+ }
+
+ if (breakFound) {
+ lineMetrics.mWidth = w;
+ lineMetrics.mPrintedWidth = pw;
+ lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex);
+ return breakIndex;
+ } else {
+ return -1;
+ }
+ }
+
+ private static class LineMetrics {
+ /** Actual width of the line. */
+ float mWidth;
+ /** Width of the line minus trailing whitespace. */
+ float mPrintedWidth;
+ boolean mHasTabs;
+
+ public LineMetrics() {
+ }
+
+ public LineMetrics(float width, float printedWidth, boolean hasTabs) {
+ mWidth = width;
+ mPrintedWidth = printedWidth;
+ mHasTabs = hasTabs;
+ }
+ }
+
+ /**
+ * A struct to store the info about a break.
+ */
+ @SuppressWarnings("SpellCheckingInspection") // For the word struct.
+ private static class Node {
+ // -1 for the first node.
+ int mPrev;
+ // number of breaks so far.
+ int mPrevCount;
+ float mDemerits;
+ float mWidth;
+ boolean mHasTabs;
+
+ public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) {
+ mPrev = prev;
+ mPrevCount = prevCount;
+ mDemerits = demerits;
+ mWidth = width;
+ mHasTabs = hasTabs;
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/Primitive.java b/tools/layoutlib/bridge/src/android/text/Primitive.java
new file mode 100644
index 0000000..ce77601
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/Primitive.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+import com.android.annotations.NonNull;
+
+// Based on the native implementation of Primitive in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+public class Primitive {
+ public final @NonNull PrimitiveType type;
+ public final int location;
+ // The following fields don't make sense for all types.
+ // Box and Glue have width only.
+ // Penalty has both width and penalty.
+ // Word_break has penalty only.
+ public final float width;
+ public final float penalty;
+
+ /**
+ * Use {@code PrimitiveType#getNewPrimitive()}
+ */
+ private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) {
+ this.type = type;
+ this.location = location;
+ this.width = width;
+ this.penalty = penalty;
+ }
+
+ public static enum PrimitiveType {
+ /**
+ * Something with a constant width that is to be typeset - like a character.
+ */
+ BOX,
+ /**
+ * Blank space with fixed width.
+ */
+ GLUE,
+ /**
+ * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of
+ * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative
+ * {@code #PENALTY_INFINITY} means a forced break.
+ * <p/>
+ * Currently, it only stores penalty with values 0 or -infinity.
+ */
+ PENALTY,
+ /**
+ * For tabs - variable width space.
+ */
+ VARIABLE,
+ /**
+ * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}.
+ */
+ WORD_BREAK;
+
+ public Primitive getNewPrimitive(int location) {
+ assert this == VARIABLE;
+ return new Primitive(this, location, 0f, 0f);
+ }
+
+ public Primitive getNewPrimitive(int location, float value) {
+ assert this == BOX || this == GLUE || this == WORD_BREAK;
+ if (this == BOX || this == GLUE) {
+ return new Primitive(this, location, value, 0f);
+ } else {
+ return new Primitive(this, location, 0f, value);
+ }
+ }
+
+ public Primitive getNewPrimitive(int location, float width, float penalty) {
+ assert this == PENALTY;
+ return new Primitive(this, location, width, penalty);
+ }
+
+ // forced non-break, negative infinity is forced break.
+ public static final float PENALTY_INFINITY = 1e7f;
+ }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index b0d79a8..929d625 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -1,12 +1,14 @@
package android.text;
+import com.android.annotations.NonNull;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import java.text.CharacterIterator;
-import java.util.Arrays;
-import java.util.Locale;
+import android.text.StaticLayout.LineBreaks;
+import android.text.Primitive.PrimitiveType;
+
+import java.util.ArrayList;
+import java.util.List;
-import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.util.ULocale;
import javax.swing.text.Segment;
@@ -20,36 +22,79 @@ import javax.swing.text.Segment;
*/
public class StaticLayout_Delegate {
- /**
- * Fills the recycle array with positions that are suitable to break the text at. The array
- * must be terminated by '-1'.
- */
+ private static final char CHAR_SPACE = 0x20;
+ private static final char CHAR_TAB = 0x09;
+ private static final char CHAR_NEWLINE = 0x0A;
+ private static final char CHAR_ZWSP = 0x200B; // Zero width space.
+
@LayoutlibDelegate
- /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length,
- int[] recycle) {
- BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale));
- Segment segment = new Segment(text, 0, length);
- iterator.setText(segment);
- if (recycle == null) {
- // Because 42 is the answer to everything.
- recycle = new int[42];
+ /*package*/ static int nComputeLineBreaks(String locale, char[] inputText, float[] widths,
+ int length, float firstWidth, int firstWidthLineCount, float restWidth,
+ int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
+ int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) {
+
+ // compute all possible breakpoints.
+ BreakIterator it = BreakIterator.getLineInstance(new ULocale(locale));
+ it.setText(new Segment(inputText, 0, length));
+ // average word length in english is 5. So, initialize the possible breaks with a guess.
+ List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
+ int loc;
+ it.first();
+ while ((loc = it.next()) != BreakIterator.DONE) {
+ breaks.add(loc);
}
- int breakOpp = iterator.first();
- recycle[0] = breakOpp;
- //noinspection ConstantConditions
- assert BreakIterator.DONE == -1;
- for (int i = 1; breakOpp != BreakIterator.DONE; ++i) {
- if (i >= recycle.length) {
- recycle = doubleSize(recycle);
- }
- assert (i < recycle.length);
- breakOpp = iterator.next();
- recycle[i] = breakOpp;
+
+ LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
+ TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
+ List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks);
+ LineBreaker lineBreaker;
+ if (optimize) {
+ lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator);
+ } else {
+ lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator);
}
- return recycle;
+ lineBreaker.computeBreaks(recycle);
+ return recycle.breaks.length;
}
- private static int[] doubleSize(int[] array) {
- return Arrays.copyOf(array, array.length * 2);
+ /**
+ * Compute metadata each character - things which help in deciding if it's possible to break
+ * at a point or not.
+ */
+ @NonNull
+ private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
+ int length, @NonNull List<Integer> breaks) {
+ // Initialize the list with a guess of the number of primitives:
+ // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
+ List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
+ int breaksSize = breaks.size();
+ int breakIndex = 0;
+ for (int i = 0; i < length; i++) {
+ char c = text[i];
+ if (c == CHAR_SPACE || c == CHAR_ZWSP) {
+ primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
+ } else if (c == CHAR_TAB) {
+ primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
+ } else if (c != CHAR_NEWLINE) {
+ while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
+ breakIndex++;
+ }
+ Primitive p;
+ if (widths[i] != 0) {
+ if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
+ p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
+ } else {
+ p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
+ }
+ primitives.add(p);
+ }
+
+ primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
+ }
+ }
+ // final break at end of everything
+ primitives.add(
+ PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
+ return primitives;
}
}
diff --git a/tools/layoutlib/bridge/src/android/text/TabStops.java b/tools/layoutlib/bridge/src/android/text/TabStops.java
new file mode 100644
index 0000000..cff6b93
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/TabStops.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 android.text;
+
+import com.android.annotations.Nullable;
+
+// Based on the native implementation of TabStops in
+// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
+public class TabStops {
+ @Nullable
+ private int[] mStops;
+ private final int mTabWidth;
+
+ public TabStops(@Nullable int[] stops, int defaultTabWidth) {
+ mTabWidth = defaultTabWidth;
+ mStops = stops;
+ }
+
+ public float width(float widthSoFar) {
+ if (mStops != null) {
+ for (int i : mStops) {
+ if (i > widthSoFar) {
+ return i;
+ }
+ }
+ }
+ // find the next tabStop after widthSoFar.
+ return (int) ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 5176419..32305a0 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -215,6 +215,12 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public void overridePendingAppTransitionClipReveal(int startX, int startY,
+ int startWidth, int startHeight) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException {
// TODO Auto-generated method stub
@@ -293,7 +299,7 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
- public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
+ public void setAppTask(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java b/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java
deleted file mode 100644
index 0100dc5..0000000
--- a/tools/layoutlib/bridge/src/com/android/internal/policy/PolicyManager.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * 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 com.android.internal.policy;
-
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.RenderAction;
-
-import android.content.Context;
-import android.view.BridgeInflater;
-import android.view.FallbackEventHandler;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManagerPolicy;
-
-/**
- * Custom implementation of PolicyManager that does nothing to run in LayoutLib.
- *
- */
-public class PolicyManager {
-
- public static Window makeNewWindow(Context context) {
- // this will likely crash somewhere beyond so we log it.
- Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
- "Call to PolicyManager.makeNewWindow is not supported", null);
- return null;
- }
-
- public static LayoutInflater makeNewLayoutInflater(Context context) {
- return new BridgeInflater(context, RenderAction.getCurrentContext().getProjectCallback());
- }
-
- public static WindowManagerPolicy makeNewWindowManager() {
- // this will likely crash somewhere beyond so we log it.
- Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
- "Call to PolicyManager.makeNewWindowManager is not supported", null);
- return null;
- }
-
- public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
- return new FallbackEventHandler() {
- @Override
- public void setView(View v) {
- }
-
- @Override
- public void preDispatchKeyEvent(KeyEvent event) {
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- return false;
- }
- };
- }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index e0f87fd..feb2590 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -86,11 +86,14 @@ public class BridgeRenderSession extends RenderSession {
}
@Override
- public Result render(long timeout) {
+ public Result render(long timeout, boolean forceMeasure) {
try {
Bridge.prepareThread();
mLastResult = mSession.acquire(timeout);
if (mLastResult.isSuccess()) {
+ if (forceMeasure) {
+ mSession.invalidateRenderingSize();
+ }
mLastResult = mSession.render(false /*freshRender*/);
}
} finally {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 2f62b93..d88a867 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -17,6 +17,7 @@
package com.android.layoutlib.bridge.android;
import android.os.IBinder;
+
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -73,6 +74,7 @@ import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -495,6 +497,34 @@ public final class BridgeContext extends Context {
throw new UnsupportedOperationException("Unsupported Service: " + service);
}
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass.equals(LayoutInflater.class)) {
+ return LAYOUT_INFLATER_SERVICE;
+ }
+
+ if (serviceClass.equals(TextServicesManager.class)) {
+ return TEXT_SERVICES_MANAGER_SERVICE;
+ }
+
+ if (serviceClass.equals(WindowManager.class)) {
+ return WINDOW_SERVICE;
+ }
+
+ if (serviceClass.equals(PowerManager.class)) {
+ return POWER_SERVICE;
+ }
+
+ if (serviceClass.equals(DisplayManager.class)) {
+ return DISPLAY_SERVICE;
+ }
+
+ if (serviceClass.equals(AccessibilityManager.class)) {
+ return ACCESSIBILITY_SERVICE;
+ }
+
+ throw new UnsupportedOperationException("Unsupported Service: " + serviceClass);
+ }
@Override
public final BridgeTypedArray obtainStyledAttributes(int[] attrs) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 25f7078..4289689 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -191,12 +191,6 @@ public final class BridgeWindowSession implements IWindowSession {
}
@Override
- public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
- float dsdx, float dtdx, float dsdy, float dtdy) {
- // pass for now.
- }
-
- @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
index e00ea6a..51a0104 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
@@ -29,6 +29,8 @@ public final class SessionParamsFlags {
public static final SessionParams.Key<String> FLAG_KEY_ROOT_TAG =
new SessionParams.Key<String>("rootTag", String.class);
+ public static final SessionParams.Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING =
+ new SessionParams.Key<Boolean>("disableBitmapCaching", Boolean.class);
// Disallow instances.
private SessionParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 3a0321a..c34f9b5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -35,6 +35,7 @@ import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
+import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
@@ -615,8 +616,22 @@ public class GcSnapshot {
return;
}
- int width = layer.getImage().getWidth();
- int height = layer.getImage().getHeight();
+ int width;
+ int height;
+ Rectangle clipBounds = originalGraphics.getClipBounds();
+ if (clipBounds != null) {
+ if (clipBounds.width == 0 || clipBounds.height == 0) {
+ // Clip is 0 so no need to paint anything.
+ return;
+ }
+ // If we have clipBounds available, use them as they will always be
+ // smaller than the full layer size.
+ width = clipBounds.width;
+ height = clipBounds.height;
+ } else {
+ width = layer.getImage().getWidth();
+ height = layer.getImage().getHeight();
+ }
// Create a temporary image to which the color filter will be applied.
BufferedImage image = new BufferedImage(width, height,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 58acab9..875cc87 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -551,7 +551,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// draw the views
// create the BufferedImage into which the layout will be rendered.
boolean newImage = false;
- if (newRenderSize || mCanvas == null) {
+
+ // When disableBitmapCaching is true, we do not reuse mImage and
+ // we create a new one in every render.
+ // This is useful when mImage is just a wrapper of Graphics2D so
+ // it doesn't get cached.
+ boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
+ SessionParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
+ if (newRenderSize || mCanvas == null || disableBitmapCaching) {
if (params.getImageFactory() != null) {
mImage = params.getImageFactory().getImage(
mMeasuredScreenWidth,
@@ -578,8 +585,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
true /*isMutable*/, hardwareConfig.getDensity());
- // create a Canvas around the Android bitmap
- mCanvas = new Canvas(bitmap);
+ if (mCanvas == null) {
+ // create a Canvas around the Android bitmap
+ mCanvas = new Canvas(bitmap);
+ } else {
+ mCanvas.setBitmap(bitmap);
+ }
mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
}
@@ -1611,7 +1622,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
return null;
}
- private void invalidateRenderingSize() {
+ public void invalidateRenderingSize() {
mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 8f50c5d..f5e8292 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -254,7 +254,6 @@ public final class CreateInfo implements ICreateInfo {
"android.view.SurfaceView", "android.view._Original_SurfaceView",
"android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
"android.webkit.WebView", "android.webkit._Original_WebView",
- "com.android.internal.policy.PolicyManager", "com.android.internal.policy._Original_PolicyManager",
};
/**
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 78d7253..1b1f63e 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -13,7 +13,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
Main.cpp
-LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
#LOCAL_C_INCLUDES +=
@@ -36,7 +36,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := pbkdf2gen
LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
LOCAL_SRC_FILES := pbkdf2gen.cpp
LOCAL_LDLIBS += -ldl
LOCAL_STATIC_LIBRARIES := libcrypto_static