mirror of
https://github.com/alesimula/Murine-launcher.git
synced 2026-10-28 09:43:42 +00:00
ICON - TEST - legacy icon + gradle script to preserve legacy icon
This commit is contained in:
1 parent
10ea49884a
commit
a3f94f2cf8
7 files changed
+914
-17
No files matched your search
@@ -39,6 +39,8 @@ def frameworkStubs16 = resolveJitpackStub('16-r2')
|
||||
def frameworkStubs17 = resolveJitpackStub('17-r2')
|
||||
|
||||
apply plugin: 'com.google.protobuf'
|
||||
// Keep the launcher icons' anydpi-v26 versioned so pre-Oreo lookups ("force legacy icons") get the legacy icon
|
||||
apply from: 'gradle/legacy-icon.gradle'
|
||||
|
||||
android {
|
||||
def (sdkMajor, sdkMinor) = COMPILE_SDK.tokenize('.')
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* Keeps the launcher icons' "-v26" qualifier in the packaged resources, so launchers that
|
||||
* resolve icons as if the platform were pre-Oreo (e.g. a "force legacy icons" option) get the
|
||||
* app's legacy icon or vector freeform icon instead of its adaptive icon.
|
||||
*
|
||||
* aapt2 (link and optimize) strips every version qualifier <= minSdk, so with minSdk >= 26
|
||||
* res/mipmap-anydpi-v26/ is packaged as plain "anydpi", which wins at any sdk. There is no flag
|
||||
* to turn that off, so after every task that writes linked resources this puts sdkVersion 26
|
||||
* back, only on:
|
||||
* - the resources referenced by android:icon / android:roundIcon of <application>,
|
||||
* <activity> and <activity-alias> in the packaged manifest (plus the resources those
|
||||
* reference, if they are aliases),
|
||||
* - and only on their "anydpi" values without an sdk version that are <adaptive-icon> files,
|
||||
* - and only if the resource has another value to fall back to (e.g. a plain default).
|
||||
* Nothing else in the table changes, and nothing at all happens when the manifest's minSdk is
|
||||
* below 26 (aapt2 keeps -v26 then). Adaptive icons need API 26 anyway, so real devices are
|
||||
* unaffected. Both formats are handled: resources.arsc (APKs) and resources.pb (app bundles;
|
||||
* bundletool converts it without collapsing versions again).
|
||||
*
|
||||
* USAGE
|
||||
*
|
||||
* 1. Apply it in the app module's build script, after the Android plugin:
|
||||
*
|
||||
* apply from: 'gradle/legacy-icon.gradle' // build.gradle
|
||||
* apply(from = "gradle/legacy-icon.gradle") // build.gradle.kts
|
||||
*
|
||||
* The path is relative to the module directory (use rootProject.file(...) if the script
|
||||
* lives elsewhere). Nothing else to configure: icons are read from the manifest.
|
||||
*
|
||||
* 2. Keep the adaptive icon where it normally is and point the manifest at it:
|
||||
*
|
||||
* res/mipmap-anydpi-v26/ic_launcher.xml // <adaptive-icon>
|
||||
* <application android:icon="@mipmap/ic_launcher" ...>
|
||||
*
|
||||
* android:roundIcon and <activity>/<activity-alias> icons are handled the same way.
|
||||
*
|
||||
* 3. Give the icon a legacy (non-adaptive) value without a version qualifier. Either PNGs
|
||||
* (res/mipmap-mdpi/ic_launcher.png, ...), or a single vector: put the vector in
|
||||
* res/drawable/ic_launcher_legacy.xml and alias it from any values file (e.g. res/values/ic_launcher.xml):
|
||||
*
|
||||
* <resources xmlns:tools="http://schemas.android.com/tools">
|
||||
* <item name="ic_launcher" type="mipmap"
|
||||
* tools:ignore="ReferenceType">@drawable/ic_launcher_legacy</item>
|
||||
* </resources>
|
||||
*
|
||||
* The alias keeps the vector out of res/mipmap/ (where the IDE flags <vector>), and
|
||||
* tools:ignore silences the ReferenceType lint error (a mipmap aliasing a drawable), which
|
||||
* otherwise fails release builds. Without a legacy value the icon is left untouched.
|
||||
*
|
||||
* If minSdk < 26 it is a no-op as it's not needed.
|
||||
* Hooks AGP's process*Resources, convertShrunkResourcesToBinary*, optimize*Resources and minify*WithR8 tasks.
|
||||
*/
|
||||
|
||||
class LegacyIconResPatcher {
|
||||
static final int RES_STRING_POOL_TYPE = 0x0001
|
||||
static final int RES_TABLE_TYPE = 0x0002
|
||||
static final int RES_XML_TYPE = 0x0003
|
||||
static final int RES_XML_START_ELEMENT_TYPE = 0x0102
|
||||
static final int RES_XML_RESOURCE_MAP_TYPE = 0x0180
|
||||
static final int RES_TABLE_PACKAGE_TYPE = 0x0200
|
||||
static final int RES_TABLE_TYPE_TYPE = 0x0201
|
||||
static final int RES_TABLE_TYPE_SPEC_TYPE = 0x0202
|
||||
static final int ATTR_ICON = 0x01010002 // android:icon
|
||||
static final int ATTR_ROUND_ICON = 0x0101052c // android:roundIcon
|
||||
static final int ATTR_MIN_SDK = 0x0101020c // android:minSdkVersion
|
||||
static final String ADAPTIVE_ICON = 'adaptive-icon'
|
||||
static final Set<String> ICON_ELEMENTS = ['application', 'activity', 'activity-alias'] as Set
|
||||
static final int TYPE_REFERENCE = 0x01
|
||||
static final int TYPE_STRING = 0x03
|
||||
static final int TYPE_DYNAMIC_REFERENCE = 0x07
|
||||
static final int TYPE_INT_DEC = 0x10
|
||||
static final int TYPE_INT_HEX = 0x11
|
||||
static final int PREVIEW_SDK = 10000 // minSdkVersion given as a codename
|
||||
static final int DENSITY_ANY = 0xfffe
|
||||
static final int CONFIG_VERSION = 0x0400
|
||||
static final int NO_ENTRY = -1
|
||||
static final int ADAPTIVE_ICON_SDK = 26
|
||||
static final int MAX_ALIAS_DEPTH = 8
|
||||
|
||||
/** Patches the resource table inside an .ap_ file in place. Returns true if it changed. */
|
||||
static boolean patchApFile(File ap) {
|
||||
String name = null
|
||||
byte[] patched = null
|
||||
new java.util.zip.ZipFile(ap).withCloseable { zip ->
|
||||
def manifestEntry = zip.getEntry('AndroidManifest.xml')
|
||||
if (manifestEntry == null) return
|
||||
byte[] manifest = zip.getInputStream(manifestEntry).bytes
|
||||
Closure<byte[]> readFile = { path ->
|
||||
def e = zip.getEntry(path as String)
|
||||
e == null ? null : zip.getInputStream(e).bytes
|
||||
}
|
||||
if (zip.getEntry('resources.arsc') != null) {
|
||||
name = 'resources.arsc'
|
||||
Map info = binaryManifestInfo(manifest)
|
||||
if ((info.minSdk as int) < ADAPTIVE_ICON_SDK) return
|
||||
patched = patchArsc(readFile.call(name), info.icons as Set<Integer>, readFile)
|
||||
} else if (zip.getEntry('resources.pb') != null) {
|
||||
name = 'resources.pb'
|
||||
Map info = protoManifestInfo(manifest)
|
||||
if ((info.minSdk as int) < ADAPTIVE_ICON_SDK) return
|
||||
patched = patchProto(readFile.call(name), info.icons as Set<Integer>, readFile)
|
||||
}
|
||||
}
|
||||
if (patched == null) return false
|
||||
replaceZipEntry(ap, name, patched)
|
||||
return true
|
||||
}
|
||||
|
||||
// ---- Zip: replace one entry, copy every other one byte for byte ----
|
||||
// Recompressing the other entries would make their bytes depend on the JDK's zlib, and the
|
||||
// packager copies compressed entries from the .ap_ into the APK as they are.
|
||||
|
||||
static void replaceZipEntry(File file, String name, byte[] data) {
|
||||
byte[] zip = file.bytes
|
||||
def buf = le(zip)
|
||||
int eocd = -1
|
||||
for (int p = zip.length - 22; p >= Math.max(0, zip.length - 22 - 0xffff); p--) {
|
||||
if (buf.getInt(p) == 0x06054b50) { eocd = p; break }
|
||||
}
|
||||
if (eocd < 0) throw new IllegalStateException("${file.name}: no zip end record")
|
||||
int count = u16(buf, eocd + 10)
|
||||
long cdOffset = buf.getInt(eocd + 16) & 0xffffffffL
|
||||
if (count == 0xffff || cdOffset == 0xffffffffL) throw new IllegalStateException("${file.name}: zip64 is not supported")
|
||||
|
||||
// Central directory records: {record offset, record length, local header offset}
|
||||
List<int[]> records = []
|
||||
int target = -1
|
||||
for (int i = 0, p = (int) cdOffset; i < count; i++) {
|
||||
if (buf.getInt(p) != 0x02014b50) throw new IllegalStateException("${file.name}: bad central directory")
|
||||
int nameLen = u16(buf, p + 28)
|
||||
int length = 46 + nameLen + u16(buf, p + 30) + u16(buf, p + 32)
|
||||
if (new String(zip, p + 46, nameLen, 'UTF-8') == name) target = i
|
||||
records.add([p, length, buf.getInt(p + 42)] as int[])
|
||||
p += length
|
||||
}
|
||||
if (target < 0) throw new IllegalStateException("${file.name}: no $name")
|
||||
int method = u16(buf, records[target][0] + 10)
|
||||
byte[] stored = data
|
||||
if (method == java.util.zip.ZipEntry.DEFLATED) {
|
||||
def deflater = new java.util.zip.Deflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, true)
|
||||
try {
|
||||
deflater.setInput(data)
|
||||
deflater.finish()
|
||||
def out = new ByteArrayOutputStream()
|
||||
byte[] chunk = new byte[65536]
|
||||
while (!deflater.finished()) out.write(chunk, 0, deflater.deflate(chunk))
|
||||
stored = out.toByteArray()
|
||||
} finally {
|
||||
deflater.end()
|
||||
}
|
||||
} else if (method != java.util.zip.ZipEntry.STORED) {
|
||||
throw new IllegalStateException("${file.name}: unsupported compression for $name")
|
||||
}
|
||||
def crc = new java.util.zip.CRC32()
|
||||
crc.update(data)
|
||||
|
||||
// Local entries in file order; each spans up to the next one (or the central directory).
|
||||
List<int[]> locals = records.collect { it } .sort { it[2] }
|
||||
def out = new ByteArrayOutputStream()
|
||||
Map<Integer, Integer> moved = [:]
|
||||
for (int i = 0; i < locals.size(); i++) {
|
||||
int start = locals[i][2]
|
||||
int end = i + 1 < locals.size() ? locals[i + 1][2] : (int) cdOffset
|
||||
moved.put(start, out.size())
|
||||
if (locals[i].is(records[target])) {
|
||||
int headerLen = 30 + u16(buf, start + 26) + u16(buf, start + 28)
|
||||
byte[] header = Arrays.copyOfRange(zip, start, start + headerLen)
|
||||
def h = le(header)
|
||||
h.putShort(6, (short) (u16(h, 6) & ~0x08)) // sizes are in the header now
|
||||
h.putInt(14, (int) crc.value)
|
||||
h.putInt(18, stored.length)
|
||||
h.putInt(22, data.length)
|
||||
out.write(header)
|
||||
out.write(stored)
|
||||
} else {
|
||||
out.write(zip, start, end - start)
|
||||
}
|
||||
}
|
||||
int newCdOffset = out.size()
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
int[] r = records[i]
|
||||
byte[] record = Arrays.copyOfRange(zip, r[0], r[0] + r[1])
|
||||
def c = le(record)
|
||||
c.putInt(42, moved.get(r[2]))
|
||||
if (i == target) {
|
||||
c.putShort(8, (short) (u16(c, 8) & ~0x08))
|
||||
c.putInt(16, (int) crc.value)
|
||||
c.putInt(20, stored.length)
|
||||
c.putInt(24, data.length)
|
||||
}
|
||||
out.write(record)
|
||||
}
|
||||
byte[] end = Arrays.copyOfRange(zip, eocd, zip.length)
|
||||
le(end).putInt(12, out.size() - newCdOffset)
|
||||
le(end).putInt(16, newCdOffset)
|
||||
out.write(end)
|
||||
|
||||
File tmp = new File(file.absoluteFile.parentFile, file.name + '.tmp')
|
||||
tmp.bytes = out.toByteArray()
|
||||
java.nio.file.Files.move(tmp.toPath(), file.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
|
||||
// ---- Binary format: AndroidManifest.xml (AXML) and resources.arsc ----
|
||||
// frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h
|
||||
|
||||
/**
|
||||
* [icons: resource ids of the icons declared in a binary manifest, minSdk: its minSdkVersion
|
||||
* (1 if missing, PREVIEW_SDK for a codename)].
|
||||
*/
|
||||
static Map binaryManifestInfo(byte[] xml) {
|
||||
Set<Integer> icons = new HashSet<>()
|
||||
int minSdk = 1
|
||||
def buf = le(xml)
|
||||
if (xml.length < 8 || u16(buf, 0) != RES_XML_TYPE) return [icons: icons, minSdk: minSdk]
|
||||
int pool = -1
|
||||
int[] resMap = new int[0]
|
||||
int pos = u16(buf, 2)
|
||||
while (pos + 8 <= xml.length) {
|
||||
int type = u16(buf, pos)
|
||||
int size = buf.getInt(pos + 4)
|
||||
if (size <= 0) break
|
||||
if (type == RES_STRING_POOL_TYPE) {
|
||||
pool = pos
|
||||
} else if (type == RES_XML_RESOURCE_MAP_TYPE) {
|
||||
int start = pos + u16(buf, pos + 2)
|
||||
resMap = new int[(pos + size - start).intdiv(4)]
|
||||
for (int i = 0; i < resMap.length; i++) resMap[i] = buf.getInt(start + 4 * i)
|
||||
} else if (type == RES_XML_START_ELEMENT_TYPE && pool >= 0) {
|
||||
// ResXMLTree_attrExt: ns, name, attributeStart, attributeSize, attributeCount
|
||||
int ext = pos + u16(buf, pos + 2)
|
||||
String element = stringAt(buf, pool, buf.getInt(ext + 4))
|
||||
boolean iconElement = ICON_ELEMENTS.contains(element)
|
||||
int attrs = ext + u16(buf, ext + 8)
|
||||
int attrSize = u16(buf, ext + 10)
|
||||
int count = u16(buf, ext + 12)
|
||||
for (int i = 0; i < count; i++) {
|
||||
// ResXMLTree_attribute: ns, name, rawValue, Res_value(size, res0, dataType, data)
|
||||
int a = attrs + i * attrSize
|
||||
int nameIdx = buf.getInt(a + 4)
|
||||
int attrId = nameIdx >= 0 && nameIdx < resMap.length ? resMap[nameIdx] : 0
|
||||
int dataType = buf.get(a + 15) & 0xff
|
||||
int data = buf.getInt(a + 16)
|
||||
if (iconElement && (attrId == ATTR_ICON || attrId == ATTR_ROUND_ICON)
|
||||
&& (dataType == TYPE_REFERENCE || dataType == TYPE_DYNAMIC_REFERENCE)) {
|
||||
icons.add(data)
|
||||
} else if (element == 'uses-sdk' && attrId == ATTR_MIN_SDK) {
|
||||
minSdk = dataType == TYPE_INT_DEC || dataType == TYPE_INT_HEX ? data : PREVIEW_SDK
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += size
|
||||
}
|
||||
return [icons: icons, minSdk: minSdk]
|
||||
}
|
||||
|
||||
/** Name of the root element of a binary (AXML) or proto XML file, or null. */
|
||||
static String rootElement(byte[] file) {
|
||||
if (file == null || file.length < 8) return null
|
||||
def buf = le(file)
|
||||
if (u16(buf, 0) == RES_XML_TYPE) {
|
||||
int pool = -1
|
||||
for (int pos = u16(buf, 2); pos + 8 <= file.length; ) {
|
||||
int size = buf.getInt(pos + 4)
|
||||
if (size <= 0) break
|
||||
int type = u16(buf, pos)
|
||||
if (type == RES_STRING_POOL_TYPE) pool = pos
|
||||
if (type == RES_XML_START_ELEMENT_TYPE && pool >= 0) {
|
||||
return stringAt(buf, pool, buf.getInt(pos + u16(buf, pos + 2) + 4))
|
||||
}
|
||||
pos += size
|
||||
}
|
||||
return null
|
||||
}
|
||||
try {
|
||||
byte[] element = messageAt(file, 1)
|
||||
return element == null ? null : stringField(element, 3)
|
||||
} catch (RuntimeException ignored) {
|
||||
return null // not proto XML (e.g. a PNG)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the patched resources.arsc, or null if nothing changed. */
|
||||
static byte[] patchArsc(byte[] data, Set<Integer> icons, Closure<byte[]> readFile) {
|
||||
if (icons.isEmpty()) return null
|
||||
def buf = le(data)
|
||||
if (data.length < 12 || u16(buf, 0) != RES_TABLE_TYPE) return null
|
||||
int headerSize = u16(buf, 2)
|
||||
// File values are indexes into the table's global string pool, its first chunk.
|
||||
int pool = u16(buf, headerSize) == RES_STRING_POOL_TYPE ? headerSize : -1
|
||||
Closure<Boolean> isAdaptiveIcon = { index ->
|
||||
String path = pool < 0 ? null : stringAt(buf, pool, index as int)
|
||||
path != null && rootElement(readFile.call(path)) == ADAPTIVE_ICON
|
||||
}
|
||||
def out = new ByteArrayOutputStream()
|
||||
out.write(data, 0, headerSize)
|
||||
boolean changed = false
|
||||
int pos = headerSize
|
||||
while (pos + 8 <= data.length) {
|
||||
int size = buf.getInt(pos + 4)
|
||||
if (size <= 0) break
|
||||
byte[] chunk = Arrays.copyOfRange(data, pos, pos + size)
|
||||
if (u16(buf, pos) == RES_TABLE_PACKAGE_TYPE) {
|
||||
byte[] patched = patchPackage(chunk, icons, isAdaptiveIcon)
|
||||
if (patched != null) { chunk = patched; changed = true }
|
||||
}
|
||||
out.write(chunk)
|
||||
pos += size
|
||||
}
|
||||
if (!changed) return null
|
||||
if (pos < data.length) out.write(data, pos, data.length - pos)
|
||||
byte[] result = out.toByteArray()
|
||||
le(result).putInt(4, result.length)
|
||||
return result
|
||||
}
|
||||
|
||||
private static byte[] patchPackage(byte[] pkg, Set<Integer> icons, Closure<Boolean> isAdaptiveIcon) {
|
||||
def buf = le(pkg)
|
||||
int pkgId = buf.getInt(8)
|
||||
int headerSize = u16(buf, 2)
|
||||
List<int[]> chunks = [] // {offset, size}
|
||||
for (int pos = headerSize; pos + 8 <= pkg.length; ) {
|
||||
int size = buf.getInt(pos + 4)
|
||||
if (size <= 0) break
|
||||
chunks.add([pos, size] as int[])
|
||||
pos += size
|
||||
}
|
||||
|
||||
// The icons, plus whatever they alias to.
|
||||
Set<Integer> targets = new HashSet<>(icons)
|
||||
for (int depth = 0; depth < MAX_ALIAS_DEPTH; depth++) {
|
||||
Set<Integer> found = new HashSet<>()
|
||||
for (int[] c : chunks) {
|
||||
if (u16(buf, c[0]) != RES_TABLE_TYPE_TYPE) continue
|
||||
for (int[] e : entries(buf, c[0])) {
|
||||
if (!targets.contains(resId(pkgId, buf, c[0], e[0]))) continue
|
||||
int ref = referenceOf(buf, e[1])
|
||||
if (ref != 0) found.add(ref)
|
||||
}
|
||||
}
|
||||
if (!targets.addAll(found)) break
|
||||
}
|
||||
|
||||
// Values that are not candidates: a target needs one of these to fall back to.
|
||||
Set<Integer> hasFallback = new HashSet<>()
|
||||
for (int[] c : chunks) {
|
||||
if (u16(buf, c[0]) != RES_TABLE_TYPE_TYPE || isUnversionedAnyDpi(buf, c[0])) continue
|
||||
for (int[] e : entries(buf, c[0])) hasFallback.add(resId(pkgId, buf, c[0], e[0]))
|
||||
}
|
||||
|
||||
def out = new ByteArrayOutputStream()
|
||||
out.write(pkg, 0, headerSize)
|
||||
Map<Integer, List<Integer>> versioned = [:] // type id -> entry indices
|
||||
Map<Integer, Integer> addedChunks = [:] // type id -> extra type chunks
|
||||
for (int[] c : chunks) {
|
||||
int pos = c[0]
|
||||
if (u16(buf, pos) != RES_TABLE_TYPE_TYPE || !isUnversionedAnyDpi(buf, pos)) {
|
||||
out.write(pkg, pos, c[1])
|
||||
continue
|
||||
}
|
||||
List<int[]> hit = []
|
||||
List<int[]> rest = []
|
||||
for (int[] e : entries(buf, pos)) {
|
||||
int id = resId(pkgId, buf, pos, e[0])
|
||||
boolean icon = targets.contains(id) && hasFallback.contains(id)
|
||||
int[] value = valueOf(buf, e[1])
|
||||
if (icon && value != null && value[0] == TYPE_STRING && isAdaptiveIcon.call(value[1])) hit.add(e)
|
||||
else rest.add(e)
|
||||
}
|
||||
if (hit.isEmpty()) {
|
||||
out.write(pkg, pos, c[1])
|
||||
continue
|
||||
}
|
||||
int typeId = pkg[pos + 8] & 0xff
|
||||
if (!versioned.containsKey(typeId)) versioned.put(typeId, [])
|
||||
for (int[] e : hit) versioned.get(typeId).add(e[0])
|
||||
if (rest.isEmpty()) {
|
||||
// Everything in this chunk is an icon: just version it in place.
|
||||
byte[] chunk = Arrays.copyOfRange(pkg, pos, pos + c[1])
|
||||
le(chunk).putShort(20 + 24, (short) ADAPTIVE_ICON_SDK)
|
||||
out.write(chunk)
|
||||
} else {
|
||||
// Split: the icons move to their own anydpi-v26 chunk, the rest stays as is.
|
||||
out.write(buildTypeChunk(buf, pos, rest, 0))
|
||||
out.write(buildTypeChunk(buf, pos, hit, ADAPTIVE_ICON_SDK))
|
||||
addedChunks.put(typeId, (addedChunks.get(typeId) ?: 0) + 1)
|
||||
}
|
||||
}
|
||||
if (versioned.isEmpty()) return null
|
||||
|
||||
byte[] result = out.toByteArray()
|
||||
def rb = le(result)
|
||||
rb.putInt(4, result.length)
|
||||
for (int pos = headerSize; pos + 8 <= result.length; ) {
|
||||
int size = rb.getInt(pos + 4)
|
||||
if (size <= 0) break
|
||||
int typeId = result[pos + 8] & 0xff
|
||||
if (u16(rb, pos) == RES_TABLE_TYPE_SPEC_TYPE && versioned.containsKey(typeId)) {
|
||||
// These entries now vary by sdk version too.
|
||||
int count = rb.getInt(pos + 12)
|
||||
int flags = pos + u16(rb, pos + 2)
|
||||
for (int idx : versioned.get(typeId)) {
|
||||
if (idx < count) rb.putInt(flags + 4 * idx, rb.getInt(flags + 4 * idx) | CONFIG_VERSION)
|
||||
}
|
||||
// typesCount is only a size hint, and 0 means unset.
|
||||
int typesCount = u16(rb, pos + 10)
|
||||
int added = addedChunks.get(typeId) ?: 0
|
||||
if (typesCount != 0 && added != 0) rb.putShort(pos + 10, (short) (typesCount + added))
|
||||
}
|
||||
pos += size
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static boolean isUnversionedAnyDpi(java.nio.ByteBuffer buf, int chunk) {
|
||||
int cfg = chunk + 20
|
||||
return buf.getInt(cfg) >= 28 && u16(buf, cfg + 14) == DENSITY_ANY && u16(buf, cfg + 24) == 0
|
||||
}
|
||||
|
||||
private static int resId(int pkgId, java.nio.ByteBuffer buf, int chunk, int idx) {
|
||||
return (pkgId << 24) | ((buf.get(chunk + 8) & 0xff) << 16) | idx
|
||||
}
|
||||
|
||||
/** {entry index, absolute entry offset} for every entry present in a type chunk. */
|
||||
private static List<int[]> entries(java.nio.ByteBuffer buf, int chunk) {
|
||||
int flags = buf.get(chunk + 9) & 0xff
|
||||
int count = buf.getInt(chunk + 12)
|
||||
int start = chunk + buf.getInt(chunk + 16)
|
||||
int offsets = chunk + u16(buf, chunk + 2)
|
||||
List<int[]> result = []
|
||||
for (int i = 0; i < count; i++) {
|
||||
if ((flags & 0x01) != 0) { // FLAG_SPARSE: {idx, offset / 4}
|
||||
result.add([u16(buf, offsets + 4 * i), start + 4 * u16(buf, offsets + 4 * i + 2)] as int[])
|
||||
} else if ((flags & 0x02) != 0) { // FLAG_OFFSET16: offset / 4, 0xffff = none
|
||||
int off = u16(buf, offsets + 2 * i)
|
||||
if (off != 0xffff) result.add([i, start + 4 * off] as int[])
|
||||
} else {
|
||||
int off = buf.getInt(offsets + 4 * i)
|
||||
if (off != NO_ENTRY) result.add([i, start + off] as int[])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Bytes taken by the entry at [e], including its value(s). */
|
||||
private static int entryLength(java.nio.ByteBuffer buf, int e) {
|
||||
int size = u16(buf, e)
|
||||
int flags = u16(buf, e + 2)
|
||||
if ((flags & 0x08) != 0) return 8 // FLAG_COMPACT
|
||||
if ((flags & 0x01) != 0) return size + 12 * buf.getInt(e + 12) // FLAG_COMPLEX: map entries
|
||||
return size + u16(buf, e + size) // entry + Res_value
|
||||
}
|
||||
|
||||
/** {dataType, data} of a simple entry, or null for a complex (map) entry. */
|
||||
private static int[] valueOf(java.nio.ByteBuffer buf, int e) {
|
||||
int size = u16(buf, e)
|
||||
int flags = u16(buf, e + 2)
|
||||
if ((flags & 0x01) != 0) return null
|
||||
if ((flags & 0x08) != 0) return [flags >>> 8, buf.getInt(e + 4)] as int[] // FLAG_COMPACT
|
||||
return [buf.get(e + size + 3) & 0xff, buf.getInt(e + size + 4)] as int[]
|
||||
}
|
||||
|
||||
/** The resource id a simple entry points to, or 0. */
|
||||
private static int referenceOf(java.nio.ByteBuffer buf, int e) {
|
||||
int[] value = valueOf(buf, e)
|
||||
return value != null && (value[0] == TYPE_REFERENCE || value[0] == TYPE_DYNAMIC_REFERENCE) ? value[1] : 0
|
||||
}
|
||||
|
||||
/** A dense copy of the type chunk at [chunk] holding only [entries], with the given sdk. */
|
||||
private static byte[] buildTypeChunk(java.nio.ByteBuffer buf, int chunk, List<int[]> entries, int sdk) {
|
||||
byte[] src = buf.array()
|
||||
int headerSize = u16(buf, chunk + 2)
|
||||
int count = 0
|
||||
for (int[] e : entries) count = Math.max(count, e[0] + 1)
|
||||
int[] offsets = new int[count]
|
||||
Arrays.fill(offsets, NO_ENTRY)
|
||||
def data = new ByteArrayOutputStream()
|
||||
for (int[] e : entries) {
|
||||
offsets[e[0]] = data.size()
|
||||
data.write(src, e[1], entryLength(buf, e[1]))
|
||||
while (data.size() % 4 != 0) data.write(0)
|
||||
}
|
||||
int entriesStart = headerSize + 4 * count
|
||||
byte[] out = new byte[entriesStart + data.size()]
|
||||
System.arraycopy(src, chunk, out, 0, headerSize) // header, including the config
|
||||
def b = le(out)
|
||||
b.putInt(4, out.length)
|
||||
out[9] = 0 // dense, 32-bit offsets
|
||||
b.putInt(12, count)
|
||||
b.putInt(16, entriesStart)
|
||||
b.putShort(20 + 24, (short) sdk)
|
||||
for (int i = 0; i < count; i++) b.putInt(headerSize + 4 * i, offsets[i])
|
||||
System.arraycopy(data.toByteArray(), 0, out, entriesStart, data.size())
|
||||
return out
|
||||
}
|
||||
|
||||
private static String stringAt(java.nio.ByteBuffer buf, int pool, int index) {
|
||||
if (u16(buf, pool) != RES_STRING_POOL_TYPE || index < 0 || index >= buf.getInt(pool + 8)) return null
|
||||
int flags = buf.getInt(pool + 16)
|
||||
int p = pool + buf.getInt(pool + 20) + buf.getInt(pool + u16(buf, pool + 2) + 4 * index)
|
||||
if ((flags & 0x100) != 0) { // UTF-8
|
||||
p += ((buf.get(p) & 0x80) != 0) ? 2 : 1 // utf16 length
|
||||
int len = buf.get(p) & 0xff
|
||||
if ((len & 0x80) != 0) { len = ((len & 0x7f) << 8) | (buf.get(p + 1) & 0xff); p += 2 } else { p += 1 }
|
||||
return new String(buf.array(), p, len, 'UTF-8')
|
||||
}
|
||||
int len = u16(buf, p)
|
||||
if ((len & 0x8000) != 0) { len = ((len & 0x7fff) << 16) | u16(buf, p + 2); p += 4 } else { p += 2 }
|
||||
return new String(buf.array(), p, len * 2, 'UTF-16LE')
|
||||
}
|
||||
|
||||
private static java.nio.ByteBuffer le(byte[] b) {
|
||||
return java.nio.ByteBuffer.wrap(b).order(java.nio.ByteOrder.LITTLE_ENDIAN)
|
||||
}
|
||||
|
||||
private static int u16(java.nio.ByteBuffer buf, int pos) {
|
||||
return buf.getShort(pos) & 0xffff
|
||||
}
|
||||
|
||||
// ---- Proto format: AndroidManifest.xml (aapt.pb.XmlNode) and resources.pb ----
|
||||
// frameworks/base/tools/aapt2/Resources.proto, Configuration.proto
|
||||
// XmlNode.element(1) > XmlElement.name(3), attribute(4), child(5)
|
||||
// XmlAttribute.resource_id(5), compiled_item(6) > Item.ref(1) > Reference.type(1), id(2)
|
||||
// ResourceTable.package(2) > Package.package_id(1), type(3) > Type.type_id(1), entry(3)
|
||||
// > Entry.entry_id(1), config_value(6) > ConfigValue.config(1), value(2) > Value.item(4)
|
||||
// Configuration.density(18), sdk_version(24)
|
||||
|
||||
/** Same as [binaryManifestInfo], for a proto manifest. */
|
||||
static Map protoManifestInfo(byte[] xml) {
|
||||
Set<Integer> icons = new HashSet<>()
|
||||
int[] minSdk = [1]
|
||||
collectProtoManifest(xml, icons, minSdk)
|
||||
return [icons: icons, minSdk: minSdk[0]]
|
||||
}
|
||||
|
||||
private static void collectProtoManifest(byte[] node, Set<Integer> icons, int[] minSdk) {
|
||||
byte[] element = messageAt(node, 1)
|
||||
if (element == null) return
|
||||
String name = stringField(element, 3)
|
||||
for (long[] f : fields(element)) {
|
||||
if (f[1] != 2) continue
|
||||
if (f[0] == 4) {
|
||||
byte[] attr = payload(element, f)
|
||||
long attrId = varintField(attr, 5)
|
||||
if (ICON_ELEMENTS.contains(name) && (attrId == ATTR_ICON || attrId == ATTR_ROUND_ICON)) {
|
||||
byte[] ref = messageAt(attr, 6, 1)
|
||||
if (ref != null && varintField(ref, 1) == 0 && varintField(ref, 2) != 0) {
|
||||
icons.add((int) varintField(ref, 2))
|
||||
}
|
||||
} else if (name == 'uses-sdk' && attrId == ATTR_MIN_SDK) {
|
||||
byte[] prim = messageAt(attr, 6, 7)
|
||||
if (prim != null && hasVarint(prim, 6)) minSdk[0] = (int) varintField(prim, 6)
|
||||
else if (prim != null && hasVarint(prim, 7)) minSdk[0] = (int) varintField(prim, 7)
|
||||
else minSdk[0] = (stringField(attr, 3) ?: '').isInteger() ? stringField(attr, 3).toInteger() : PREVIEW_SDK
|
||||
}
|
||||
} else if (f[0] == 5) {
|
||||
collectProtoManifest(payload(element, f), icons, minSdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the patched resources.pb, or null if nothing changed. */
|
||||
static byte[] patchProto(byte[] table, Set<Integer> icons, Closure<byte[]> readFile) {
|
||||
if (icons.isEmpty()) return null
|
||||
Set<Integer> targets = new HashSet<>(icons)
|
||||
for (int depth = 0; depth < MAX_ALIAS_DEPTH; depth++) {
|
||||
Set<Integer> found = new HashSet<>()
|
||||
for (byte[] entry : protoEntries(table, targets)) {
|
||||
for (long[] f : fields(entry)) {
|
||||
if (f[0] != 6 || f[1] != 2) continue
|
||||
byte[] ref = messageAt(payload(entry, f), 2, 4, 1)
|
||||
if (ref != null && varintField(ref, 1) == 0 && varintField(ref, 2) != 0) {
|
||||
found.add((int) varintField(ref, 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!targets.addAll(found)) break
|
||||
}
|
||||
return rewrite(table, 2) { pkg ->
|
||||
int pkgId = idOf(pkg)
|
||||
rewrite(pkg, 3) { type ->
|
||||
int typeId = idOf(type)
|
||||
rewrite(type, 3) { entry ->
|
||||
targets.contains((pkgId << 24) | (typeId << 16) | idOf(entry)) ? patchProtoEntry(entry, readFile) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Entries of [table] whose resource id is in [ids]. */
|
||||
private static List<byte[]> protoEntries(byte[] table, Set<Integer> ids) {
|
||||
List<byte[]> result = []
|
||||
for (long[] p : fields(table)) {
|
||||
if (p[0] != 2 || p[1] != 2) continue
|
||||
byte[] pkg = payload(table, p)
|
||||
int pkgId = idOf(pkg)
|
||||
for (long[] t : fields(pkg)) {
|
||||
if (t[0] != 3 || t[1] != 2) continue
|
||||
byte[] type = payload(pkg, t)
|
||||
int typeId = idOf(type)
|
||||
for (long[] e : fields(type)) {
|
||||
if (e[0] != 3 || e[1] != 2) continue
|
||||
byte[] entry = payload(type, e)
|
||||
if (ids.contains((pkgId << 24) | (typeId << 16) | idOf(entry))) result.add(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static byte[] patchProtoEntry(byte[] entry, Closure<byte[]> readFile) {
|
||||
boolean fallback = false
|
||||
for (long[] f : fields(entry)) {
|
||||
if (f[0] == 6 && f[1] == 2 && !isUnversionedAnyDpi(messageAt(payload(entry, f), 1))) fallback = true
|
||||
}
|
||||
if (!fallback) return null
|
||||
return rewrite(entry, 6) { cv ->
|
||||
byte[] file = messageAt(cv as byte[], 2, 4, 5) // Value.item.file
|
||||
String path = file == null ? null : stringField(file, 1)
|
||||
boolean adaptive = path != null && isUnversionedAnyDpi(messageAt(cv as byte[], 1)) &&
|
||||
rootElement(readFile.call(path)) == ADAPTIVE_ICON
|
||||
adaptive ? rewrite(cv as byte[], 1) { cfg -> patchConfig(cfg as byte[]) } : null
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isUnversionedAnyDpi(byte[] cfg) {
|
||||
return cfg != null && varintField(cfg, 18) == DENSITY_ANY && varintField(cfg, 24) == 0
|
||||
}
|
||||
|
||||
private static byte[] patchConfig(byte[] cfg) {
|
||||
if (!isUnversionedAnyDpi(cfg)) return null
|
||||
def out = new ByteArrayOutputStream()
|
||||
for (long[] f : fields(cfg)) {
|
||||
if (f[0] != 24) out.write(cfg, (int) f[2], (int) (f[3] - f[2]))
|
||||
}
|
||||
writeVarint(out, 24 << 3)
|
||||
writeVarint(out, ADAPTIVE_ICON_SDK)
|
||||
return out.toByteArray()
|
||||
}
|
||||
|
||||
/** The id(1) of a PackageId/TypeId/EntryId message in field 1, 0 if absent. */
|
||||
private static int idOf(byte[] msg) {
|
||||
byte[] id = messageAt(msg, 1)
|
||||
return id == null ? 0 : (int) varintField(id, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies [msg], passing the payload of every length-delimited [field] through [patch].
|
||||
* Returns null if [patch] changed nothing.
|
||||
*/
|
||||
private static byte[] rewrite(byte[] msg, int field, Closure<byte[]> patch) {
|
||||
boolean changed = false
|
||||
def out = new ByteArrayOutputStream()
|
||||
for (long[] f : fields(msg)) {
|
||||
byte[] patched = (f[0] == field && f[1] == 2) ? patch.call(payload(msg, f)) : null
|
||||
if (patched == null) {
|
||||
out.write(msg, (int) f[2], (int) (f[3] - f[2]))
|
||||
} else {
|
||||
writeVarint(out, (field << 3) | 2)
|
||||
writeVarint(out, patched.length)
|
||||
out.write(patched)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
return changed ? out.toByteArray() : null
|
||||
}
|
||||
|
||||
/** The nested message at the given field path (first occurrence at each level), or null. */
|
||||
private static byte[] messageAt(byte[] msg, int... path) {
|
||||
byte[] current = msg
|
||||
for (int field : path) {
|
||||
byte[] next = null
|
||||
for (long[] f : fields(current)) {
|
||||
if (f[0] == field && f[1] == 2) { next = payload(current, f); break }
|
||||
}
|
||||
if (next == null) return null
|
||||
current = next
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
private static long varintField(byte[] msg, int field) {
|
||||
long value = 0
|
||||
for (long[] f : fields(msg)) {
|
||||
if (f[0] == field && f[1] == 0) value = f[4]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private static boolean hasVarint(byte[] msg, int field) {
|
||||
for (long[] f : fields(msg)) {
|
||||
if (f[0] == field && f[1] == 0) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private static String stringField(byte[] msg, int field) {
|
||||
byte[] s = messageAt(msg, field)
|
||||
return s == null ? null : new String(s, 'UTF-8')
|
||||
}
|
||||
|
||||
private static byte[] payload(byte[] msg, long[] f) {
|
||||
return Arrays.copyOfRange(msg, (int) f[5], (int) f[3])
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-level fields of [msg], each as {field, wireType, start, end, varintValue, payloadStart}.
|
||||
* ponytail: plain arrays and loops, Gradle's Groovy miscompiles closures with long params.
|
||||
*/
|
||||
private static List<long[]> fields(byte[] msg) {
|
||||
List<long[]> result = []
|
||||
int[] pos = [0]
|
||||
while (pos[0] < msg.length) {
|
||||
int start = pos[0]
|
||||
long key = readVarint(msg, pos)
|
||||
int wire = (int) (key & 7)
|
||||
long value = 0
|
||||
int payloadStart = 0
|
||||
switch (wire) {
|
||||
case 0: value = readVarint(msg, pos); break
|
||||
case 1: pos[0] += 8; break
|
||||
case 2:
|
||||
int len = (int) readVarint(msg, pos)
|
||||
payloadStart = pos[0]
|
||||
pos[0] += len
|
||||
break
|
||||
case 5: pos[0] += 4; break
|
||||
default: throw new IllegalStateException("Unsupported wire type $wire in proto resources")
|
||||
}
|
||||
result << ([key >>> 3, wire, start, pos[0], value, payloadStart] as long[])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static long readVarint(byte[] b, int[] pos) {
|
||||
long result = 0
|
||||
for (int shift = 0; ; shift += 7) {
|
||||
int x = b[pos[0]++] & 0xff
|
||||
result |= (long) (x & 0x7f) << shift
|
||||
if ((x & 0x80) == 0) return result
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeVarint(ByteArrayOutputStream out, long v) {
|
||||
while ((v & ~0x7fL) != 0) { out.write((int) ((v & 0x7f) | 0x80)); v >>>= 7 }
|
||||
out.write((int) v)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.configureEach { task ->
|
||||
// minify*WithR8 writes the shrunk resources.pb that app bundles are built from
|
||||
if (task.name ==~ /(process|optimize)\w+Resources|convertShrunkResourcesToBinary\w+|minify\w+WithR8/) {
|
||||
task.doLast {
|
||||
task.outputs.files.each { root ->
|
||||
if (!root.exists()) return
|
||||
def files = root.isDirectory()
|
||||
? root.listFiles().findAll { it.name.endsWith('.ap_') }
|
||||
: (root.name.endsWith('.ap_') ? [root] : [])
|
||||
files.each { ap ->
|
||||
if (LegacyIconResPatcher.patchApFile(ap)) {
|
||||
logger.info("Restored anydpi-v26 on the launcher icons in ${ap.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<gradient android:startColor="#960EFC" android:endColor="#FF804A" />
|
||||
<gradient android:startColor="@color/ic_launcher_gradient_start" android:endColor="@color/ic_launcher_gradient_end" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -7,59 +7,69 @@
|
||||
android:scaleY="0.57"
|
||||
android:translateX="220.16"
|
||||
android:translateY="220.16">
|
||||
<!-- nose -->
|
||||
<path
|
||||
android:pathData="m449.7,748.1l125.5,0l-14.3,71.7l-95.6,0z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye, left -->
|
||||
<path
|
||||
android:pathData="m230.1,535 l54.9,-57.1 227.2,188.5 -197,2.7z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye, right -->
|
||||
<path
|
||||
android:pathData="m794.4,535.2 l-54.9,-57.1 -227.2,188.5 197,2.7z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye highlight, right -->
|
||||
<path
|
||||
android:pathData="m727.2,558.1c-11.1,6.4 -22.1,13 -33.1,19.5 -3.3,1.9 -7.2,4.4 -6.5,8.9 0.4,13.4 -2.5,26.9 -9.6,38.3 -9.9,17.8 -41,31.4 -61,33.8l32,4.6 12.9,-6.5c8.6,-3.8 17.1,-7.9 24.6,-13.7 8.3,-6.4 15.4,-14.4 21.1,-23.1 11.8,-18.5 16.9,-40.2 19.5,-61.7z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- eye highlight, left -->
|
||||
<path
|
||||
android:pathData="m296.5,558.3c11.1,6.4 22.1,13 33.1,19.5 3.3,1.9 7.2,4.4 6.5,8.9 -0.4,13.4 2.5,26.9 9.6,38.3 9.9,17.8 41,31.4 61,33.8L374.7,663.3 361.7,656.8c-8.6,-3.8 -17.1,-7.9 -24.6,-13.7 -8.3,-6.4 -15.4,-14.4 -21.1,-23.1 -11.8,-18.5 -16.9,-40.2 -19.5,-61.7z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- inner ear, left -->
|
||||
<path
|
||||
android:pathData="m277,434.4c-11.5,-40.3 -48,-91 -71,-119.9l0.7,77.2 73.8,62.1z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- inner ear, right -->
|
||||
<path
|
||||
android:pathData="m747.6,434c11.5,-40.3 48,-91 71,-119.9l-0.7,77.2 -73.8,62.1z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- ear, left -->
|
||||
<path
|
||||
android:pathData="M102.2,233.8 L186,285.6 237.2,504.7 87.5,451.1Z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- ear, right -->
|
||||
<path
|
||||
android:pathData="M921.3,234.8 L837.5,286.6 786.3,505.7 936,452.1Z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- ear highlight, right -->
|
||||
<path
|
||||
android:pathData="M816.5,314.4 L853.4,280.5c4.7,13.4 8.6,29.7 10.2,43.9 3.4,26.9 0.5,54.4 -6.8,80.5 -2.4,9 -6.8,17.8 -9.9,26.6l-50.7,18.6 -1,-67.8c5,-20.1 13.1,-48.7 21.3,-67.9z"
|
||||
android:fillColor="#b24090"/>
|
||||
<!-- ear highlight, left -->
|
||||
<path
|
||||
android:pathData="m160.9,370.2c-2,-30.5 1.2,-60.3 13.4,-86.4l34.2,32.7c9.6,21 16.8,43.7 22.6,64.8l-2.2,67.6 -52.1,-16.8C167.6,412.9 162.9,391.3 160.9,370.2Z"
|
||||
android:fillColor="#b24090"/>
|
||||
<!-- dark whisker, left -->
|
||||
<path
|
||||
android:pathData="m175.7,712.1 l141.7,-24.8 83.2,80.9 -169.4,5.5 13,-31.5z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- dark whisker, right -->
|
||||
<path
|
||||
android:pathData="m853.4,709.7 l-148.2,-17.5 -80.5,77.5 169.4,5.5 -13,-31.5z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- whisker, left -->
|
||||
<path
|
||||
android:pathData="m147.6,649.3 l81.2,-31.9 184,110.2c-15.6,2.9 -54,0.4 -69.7,2.8l-12.8,-10.9z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- whisker, right -->
|
||||
<path
|
||||
android:pathData="m875.9,649.3 l-81.2,-31.9 -184,110.2c15.6,2.9 54.8,0.3 70.5,2.7l12,-10.8z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- head -->
|
||||
<path
|
||||
android:pathData="m108.8,159.8c25,-7.7 52,-7 77,0 37.3,10.1 69.8,32.8 97.9,58.6 12.7,12.5 25.6,24.7 36.6,38.7 17.2,20.5 31.8,43 44.9,66.3l12.1,20.1 -4.2,-19.8c10.6,18.8 17.2,39.4 25.9,59.1 6.8,16.5 14.9,32.5 22.1,48.9 -13.9,-9.2 -26.9,-19.7 -40.8,-28.9 -9.3,-6 -18.1,-12.8 -27.8,-18.1 10.3,21.3 23.7,40.8 37.1,60.2 12.6,17.5 25.7,34.5 39.6,51L512.6,594.1 611.7,473.9c14,-18 27.4,-36.5 39.8,-55.7 6.9,-10.9 14.1,-21.7 19.2,-33.6 -16.6,9.7 -31.9,21.5 -47.8,32.5 -7.1,4.8 -13.6,10.6 -21.3,14.6 12.4,-24 23.6,-48.7 33.9,-73.7 4.9,-11.1 9.7,-24.4 16,-35.1L648,340.8l8.1,-13.8c20.9,-38.7 47.8,-74 79,-105 28.9,-27.4 62.4,-51.8 101.3,-62.4 27,-7.6 56.2,-7.1 82.8,1.9 36.3,12.3 67.3,39.8 82.8,75 17.5,38.4 19.2,82.5 10.9,123.5 -8.5,40.3 -28.2,78.4 -56.8,108.1 -28.7,30.1 -67.1,52.4 -108.9,57.2 -12.7,1.5 -25.5,-0 -38.1,0.8 11.9,4.3 24.5,6.6 36.4,10.8 -5.9,9.8 -13.7,18.2 -20.9,27 22.9,0.4 45.7,0 68.6,0.3 -10.4,16.2 -23,31 -37.6,43.6 -4.3,3.5 -8.9,6.8 -13.2,10.3l80.8,25.2c-19.6,29 -43.8,55.6 -73.6,74.4 -6.1,3.7 -12.8,6.3 -19.1,9.6 14.6,1.5 29.2,1.5 43.8,1.9 -6.2,6.3 -13.6,11.1 -20.7,16.3 -31.2,22.5 -66.6,38.3 -102.6,51.5 -9.9,4.2 -20.9,6.7 -29.5,13.5 -15.3,11.8 -31.7,22.1 -48.6,31.6 -10.9,5.9 -21.7,12.2 -33.4,16 4.5,-4.9 9.8,-9 14.3,-13.7 10.8,-11.5 21.6,-23.1 31.8,-35.1 4.6,-5.7 10.1,-10.8 13.3,-17.4 -6.2,3.5 -11.5,8.1 -17.3,12.2 -19,13.8 -37.7,28.4 -58.5,39.4 -19.1,9.9 -39.7,17.1 -61.1,20.2 -3.3,0.3 -6.4,-1 -9.4,-2.1 -9.5,-3.8 -18.8,-8.1 -27.9,-12.8 -4,-2 -7.8,-4.5 -12,-6 -2.4,-0.9 -4.7,0.9 -6.8,1.8 -10.5,5.7 -21.2,11 -32.1,15.8 -4.8,2 -9.9,4.2 -15.2,3.2 -17.1,-2.7 -33.8,-7.6 -49.3,-15.1 -31.3,-14.5 -57.4,-37.5 -85.3,-57.4 4,7 9.6,13 14.8,19.2 7.5,8.4 15,16.8 22.5,25.2 7.1,7.9 15.2,14.7 22,22.8 -29.3,-12.6 -57.3,-28.7 -82.1,-48.7 -8,-6.1 -18,-8.5 -27.1,-12.2 -23.5,-8.7 -46.5,-18.8 -68.7,-30.5 -19.3,-10.8 -37.8,-23.1 -54.5,-37.6 14.6,-0.4 29.2,-1 43.8,-1.8 -7.3,-4 -15.3,-6.7 -22.2,-11.3 -28.7,-18.9 -52,-45 -71.8,-72.9l80.8,-25.1C159.7,604.7 144.2,584.3 129.4,564.3c13.9,0.5 27.9,-0.2 41.8,-0.2 9.3,-0.1 18.7,0.2 28,-0.3 -6.8,-9.1 -14.5,-17.5 -20.9,-26.9 12.5,-3.6 25.2,-6.5 37.4,-11 -14.1,-0.6 -28.3,1.1 -42.4,-0.9 -31.1,-4.6 -60.7,-18.2 -84.7,-38.4 -22.1,-18 -40.1,-40.6 -54.2,-65.3C13.5,383.2 3.7,339 6.9,295.7c2.9,-36.5 16.5,-73.2 42.7,-99.5 16.1,-17 36.7,-29.8 59.3,-36.4m-24.2,47.6c6.9,3 14.4,4.4 21.4,7.2 7.3,2.8 14.4,6.2 21.4,9.6 26.3,13.2 49.1,33.3 65,58 7.1,11.6 12.5,24.1 17.2,36.9 -12.1,-10.9 -23,-23.1 -36,-33 -12.3,-10.9 -26,-20.1 -40.3,-28.1 -17.1,19.3 -25.7,45 -26.7,70.5 -1.3,36.1 10.2,71.8 29.3,102.1 13.8,21.9 32.7,40.8 55.4,53.4 7.9,4.3 16.4,8.1 25.5,8.7 -4.6,-5.6 -10,-10.4 -14.6,-16 -11.4,-13.3 -19.9,-28.8 -25.9,-45.2 12.7,0.3 25,3.9 37,7.8 -4.8,-7.7 -9.7,-15.3 -13.7,-23.5 -10.5,-21.8 -15.6,-46.2 -15.5,-70.4 15,8.8 28.8,19.4 41.5,31.3 1.3,1.2 11.6,10.9 16.5,17 4.6,5.7 9.6,11.1 14,17 4.6,6.6 9,13.3 14,19.6 1.9,-45.7 -8.6,-92.1 -30.4,-132.3 -18,-32.7 -44.9,-61.1 -78.3,-78.2 -23.6,-11.9 -51,-17.5 -77.1,-12.4m778.2,11.1c-17.5,8 -32.7,20.2 -46.1,33.8 -29.2,29 -47.8,67.3 -56.5,107.3 -4.9,23.2 -8.1,47 -6.8,70.8 10.3,-14.6 20.6,-29.4 33,-42.3 4.8,-5.2 13.5,-14.2 23.4,-22 9.2,-7.7 19.4,-14.1 29.4,-20.7 0.5,20.1 -3,40.3 -10,59.2 -4.6,12.6 -11.8,24 -19,35.3 12,-4 24.3,-7.5 37,-8.8 -4.3,14.8 -12.2,28.3 -21.3,40.7 -5.8,8 -13.6,14.3 -19.9,21.8 16.6,-2.4 31.4,-11.1 44.7,-21 27.1,-21.2 46.3,-51.5 57.1,-84 8.8,-25.9 11.9,-54.1 6.4,-81 -3.9,-18 -11.4,-35.5 -23.4,-49.6 -13.4,6.9 -25.9,15.6 -37.6,25.1 -14.1,10.6 -26,23.7 -38.5,36.1 9.3,-28.2 29,-57.5 53.8,-77.5 16.6,-13.4 36,-23.1 56.3,-29.6 5.1,-1.5 10.3,-2.8 15.4,-4.7 -26,-4.9 -53.4,-0.3 -77.1,11.1M258.7,517.3c5.8,32.7 16.5,65 34.7,92.9 9.8,15.3 23.3,28.6 39.5,37.1 26.8,14.2 60,13.8 88.7,5.7 3.5,-1 7.3,-3 11,-1.6 8.4,2.7 17,5.3 25.9,5.9 -4.9,-5 -10.9,-8.6 -16.5,-12.7 -24.7,-17.9 -49.5,-35.8 -74.6,-53.2 -36.1,-24.9 -72.4,-49.5 -108.7,-74.1m497,6.3c-18.7,13.2 -37.9,25.8 -56.8,38.7 -37,25.5 -74.5,50.3 -110.5,77.2 -8,6.1 -16.5,11.5 -24.2,17.9 9.1,-0.2 17.7,-3.5 26.4,-6.2 3.9,-1.4 7.9,0.5 11.7,1.6 13.6,4.5 48.8,9.3 69.9,1.4 15.4,-4.9 31,-13.1 42.7,-24.2 12.7,-12.9 22.9,-27.8 30.5,-44.2 10.1,-21.8 16.4,-44.6 20,-68.3 -3.3,2 -6.5,4 -9.6,6.2M178,643.6c6.4,4.4 13.2,8.1 19.9,11.8 37.3,19.6 76.4,35.6 114.1,54.6 6.1,3.1 12.6,5.4 18.2,9.5 -21.9,-4.1 -43.6,-9.1 -65.8,-11 -25,-2.2 -51.1,-2.1 -74.8,7.3 4.3,1.3 8.6,2.4 12.9,3.3 43.9,8.8 87.9,17.7 131.6,27.7 -15.2,2 -30.6,3.1 -45.7,6.1 -18.2,3.8 -36.5,8.6 -52.8,17.7 49.4,-2.4 98.6,-7 147.9,-10.3 -13.5,-9.9 -27.6,-19.1 -40.4,-29.9 15.7,-2.4 31.3,-5 46.9,-7.9 -25.4,-18.2 -53.1,-33 -81.5,-45.8 -41.2,-18.1 -85.1,-32.1 -130.4,-33m536.8,32.6c-18.6,8.4 -37,17.3 -54.2,28.3 -9.4,6.1 -19.4,11.4 -28.4,18.2 16.3,2.5 32.6,5.6 49.1,7.5 -13.7,10.4 -28.1,19.9 -41.7,30.5 7.6,0.2 15.2,0.5 22.9,1.3 29,2.2 58.1,4.4 87.1,6.2 12.1,0.3 24.1,2 36.3,1.3 -15.7,-7.9 -32.9,-12.4 -50,-16.1 -15.2,-3.4 -30.8,-4.3 -46.1,-7 29.4,-7 59,-13 88.7,-18.6 18.8,-4.1 37.9,-6.9 56.5,-11.7 -4.4,-2.4 -9.2,-3.6 -14.1,-4.5 -22.1,-5 -45.1,-5.1 -67.5,-2.6 -20.2,2 -40,6.8 -59.9,10.6 9.5,-6.1 20Line truncated
|
||||
android:fillColor="#fcecdc"/>
|
||||
<path
|
||||
android:pathData="m371.7,271.4c12.1,2.7 24,6.3 36,9.6 12.2,3.5 24.4,6.9 36.2,11.5 4.2,1.5 8.2,4.9 12.9,3.9 31.9,-5.8 64.6,-5.4 96.7,-2.1 6.6,0.5 13.6,3.7 20.1,0.5 12.3,-5.6 25.4,-9.3 38.3,-13.1 13.2,-3.6 26.3,-7.9 39.8,-10.3L512.6,501.3Z"
|
||||
android:fillColor="#fcecdc"/>
|
||||
<path
|
||||
android:pathData="m509,859.9c2.3,-1.3 4.7,0.1 6.8,1.1 11,5.8 22.1,11.5 33.3,17 2.8,1.1 5.7,3 8.8,2.2l52.2,-15.5c-18.8,24.1 -40.6,46.2 -65.9,63.6l1.4,-15 -34.3,25.3 -34.2,-25.8 2.2,15.7c-18.9,-13.7 -36.6,-29.2 -52.6,-46.4 -5.1,-5.5 -10.2,-11 -14.5,-17.1l36.5,10.8c5.9,1.4 11.4,4.4 17.5,4.9 5,-1.2 9.4,-4.2 14.1,-6.3 9.7,-4.6 19,-10 28.8,-14.3z"
|
||||
android:pathData="@string/ic_launcher_path_head"
|
||||
android:fillColor="#fcecdc"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,100 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<!-- Fits the stroked head edge to edge: its bounds, stroke included, are
|
||||
x -30.19..1054.21, y 117.78..975.1 (half of strokeWidth 73 around the path).
|
||||
scale = 1024 / width (rounded down), pivot = bounds center; recompute if either changes.
|
||||
The gradient spans the same bounds, so it still covers the canvas like the adaptive one -->
|
||||
<!-- the icon is slighly moved to the bottom (looks nicer); to center, set android:translateY to -34.44 -->
|
||||
<group android:pivotX="512.01" android:pivotY="546.44"
|
||||
android:translateX="-0.01" android:translateY="-16"
|
||||
android:scaleX="0.9442" android:scaleY="0.9442">
|
||||
<!-- Border: the head stroked with the adaptive background gradient;
|
||||
half of strokeWidth shows outside the head -->
|
||||
<path android:pathData="@string/ic_launcher_path_head"
|
||||
android:strokeWidth="73"
|
||||
android:strokeLineJoin="round">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:type="linear"
|
||||
android:startX="-30.19" android:startY="546.44"
|
||||
android:endX="1054.21" android:endY="546.44"
|
||||
android:startColor="@color/ic_launcher_gradient_start"
|
||||
android:endColor="@color/ic_launcher_gradient_end"/>
|
||||
</aapt:attr>
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient android:type="linear"
|
||||
android:startX="-30.19" android:startY="546.44"
|
||||
android:endX="1054.21" android:endY="546.44"
|
||||
android:startColor="@color/ic_launcher_gradient_start"
|
||||
android:endColor="@color/ic_launcher_gradient_end"/>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- nose -->
|
||||
<path
|
||||
android:pathData="m449.7,748.1l125.5,0l-14.3,71.7l-95.6,0z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye, left -->
|
||||
<path
|
||||
android:pathData="m230.1,535 l54.9,-57.1 227.2,188.5 -197,2.7z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye, right -->
|
||||
<path
|
||||
android:pathData="m794.4,535.2 l-54.9,-57.1 -227.2,188.5 197,2.7z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- eye highlight, right -->
|
||||
<path
|
||||
android:pathData="m727.2,558.1c-11.1,6.4 -22.1,13 -33.1,19.5 -3.3,1.9 -7.2,4.4 -6.5,8.9 0.4,13.4 -2.5,26.9 -9.6,38.3 -9.9,17.8 -41,31.4 -61,33.8l32,4.6 12.9,-6.5c8.6,-3.8 17.1,-7.9 24.6,-13.7 8.3,-6.4 15.4,-14.4 21.1,-23.1 11.8,-18.5 16.9,-40.2 19.5,-61.7z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- eye highlight, left -->
|
||||
<path
|
||||
android:pathData="m296.5,558.3c11.1,6.4 22.1,13 33.1,19.5 3.3,1.9 7.2,4.4 6.5,8.9 -0.4,13.4 2.5,26.9 9.6,38.3 9.9,17.8 41,31.4 61,33.8L374.7,663.3 361.7,656.8c-8.6,-3.8 -17.1,-7.9 -24.6,-13.7 -8.3,-6.4 -15.4,-14.4 -21.1,-23.1 -11.8,-18.5 -16.9,-40.2 -19.5,-61.7z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- inner ear, left -->
|
||||
<path
|
||||
android:pathData="m277,434.4c-11.5,-40.3 -48,-91 -71,-119.9l0.7,77.2 73.8,62.1z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- inner ear, right -->
|
||||
<path
|
||||
android:pathData="m747.6,434c11.5,-40.3 48,-91 71,-119.9l-0.7,77.2 -73.8,62.1z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- ear, left -->
|
||||
<path
|
||||
android:pathData="M102.2,233.8 L186,285.6 237.2,504.7 87.5,451.1Z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- ear, right -->
|
||||
<path
|
||||
android:pathData="M921.3,234.8 L837.5,286.6 786.3,505.7 936,452.1Z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- ear highlight, right -->
|
||||
<path
|
||||
android:pathData="M816.5,314.4 L853.4,280.5c4.7,13.4 8.6,29.7 10.2,43.9 3.4,26.9 0.5,54.4 -6.8,80.5 -2.4,9 -6.8,17.8 -9.9,26.6l-50.7,18.6 -1,-67.8c5,-20.1 13.1,-48.7 21.3,-67.9z"
|
||||
android:fillColor="#b24090"/>
|
||||
<!-- ear highlight, left -->
|
||||
<path
|
||||
android:pathData="m160.9,370.2c-2,-30.5 1.2,-60.3 13.4,-86.4l34.2,32.7c9.6,21 16.8,43.7 22.6,64.8l-2.2,67.6 -52.1,-16.8C167.6,412.9 162.9,391.3 160.9,370.2Z"
|
||||
android:fillColor="#b24090"/>
|
||||
<!-- dark whisker, left -->
|
||||
<path
|
||||
android:pathData="m175.7,712.1 l141.7,-24.8 83.2,80.9 -169.4,5.5 13,-31.5z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- dark whisker, right -->
|
||||
<path
|
||||
android:pathData="m853.4,709.7 l-148.2,-17.5 -80.5,77.5 169.4,5.5 -13,-31.5z"
|
||||
android:fillColor="#5a1e64"/>
|
||||
<!-- whisker, left -->
|
||||
<path
|
||||
android:pathData="m147.6,649.3 l81.2,-31.9 184,110.2c-15.6,2.9 -54,0.4 -69.7,2.8l-12.8,-10.9z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- whisker, right -->
|
||||
<path
|
||||
android:pathData="m875.9,649.3 l-81.2,-31.9 -184,110.2c15.6,2.9 54.8,0.3 70.5,2.7l12,-10.8z"
|
||||
android:fillColor="#c89ab1"/>
|
||||
<!-- head -->
|
||||
<path
|
||||
android:pathData="@string/ic_launcher_path_head"
|
||||
android:fillColor="#fcecdc"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -39,15 +39,7 @@
|
||||
android:fillAlpha="0.43"/>
|
||||
<!-- head -->
|
||||
<path
|
||||
android:pathData="m108.8,159.8c25,-7.7 52,-7 77,0 37.3,10.1 69.8,32.8 97.9,58.6 12.7,12.5 25.6,24.7 36.6,38.7 17.2,20.5 31.8,43 44.9,66.3l12.1,20.1 -4.2,-19.8c10.6,18.8 17.2,39.4 25.9,59.1 6.8,16.5 14.9,32.5 22.1,48.9 -13.9,-9.2 -26.9,-19.7 -40.8,-28.9 -9.3,-6 -18.1,-12.8 -27.8,-18.1 10.3,21.3 23.7,40.8 37.1,60.2 12.6,17.5 25.7,34.5 39.6,51L512.6,594.1 611.7,473.9c14,-18 27.4,-36.5 39.8,-55.7 6.9,-10.9 14.1,-21.7 19.2,-33.6 -16.6,9.7 -31.9,21.5 -47.8,32.5 -7.1,4.8 -13.6,10.6 -21.3,14.6 12.4,-24 23.6,-48.7 33.9,-73.7 4.9,-11.1 9.7,-24.4 16,-35.1L648,340.8l8.1,-13.8c20.9,-38.7 47.8,-74 79,-105 28.9,-27.4 62.4,-51.8 101.3,-62.4 27,-7.6 56.2,-7.1 82.8,1.9 36.3,12.3 67.3,39.8 82.8,75 17.5,38.4 19.2,82.5 10.9,123.5 -8.5,40.3 -28.2,78.4 -56.8,108.1 -28.7,30.1 -67.1,52.4 -108.9,57.2 -12.7,1.5 -25.5,-0 -38.1,0.8 11.9,4.3 24.5,6.6 36.4,10.8 -5.9,9.8 -13.7,18.2 -20.9,27 22.9,0.4 45.7,0 68.6,0.3 -10.4,16.2 -23,31 -37.6,43.6 -4.3,3.5 -8.9,6.8 -13.2,10.3l80.8,25.2c-19.6,29 -43.8,55.6 -73.6,74.4 -6.1,3.7 -12.8,6.3 -19.1,9.6 14.6,1.5 29.2,1.5 43.8,1.9 -6.2,6.3 -13.6,11.1 -20.7,16.3 -31.2,22.5 -66.6,38.3 -102.6,51.5 -9.9,4.2 -20.9,6.7 -29.5,13.5 -15.3,11.8 -31.7,22.1 -48.6,31.6 -10.9,5.9 -21.7,12.2 -33.4,16 4.5,-4.9 9.8,-9 14.3,-13.7 10.8,-11.5 21.6,-23.1 31.8,-35.1 4.6,-5.7 10.1,-10.8 13.3,-17.4 -6.2,3.5 -11.5,8.1 -17.3,12.2 -19,13.8 -37.7,28.4 -58.5,39.4 -19.1,9.9 -39.7,17.1 -61.1,20.2 -3.3,0.3 -6.4,-1 -9.4,-2.1 -9.5,-3.8 -18.8,-8.1 -27.9,-12.8 -4,-2 -7.8,-4.5 -12,-6 -2.4,-0.9 -4.7,0.9 -6.8,1.8 -10.5,5.7 -21.2,11 -32.1,15.8 -4.8,2 -9.9,4.2 -15.2,3.2 -17.1,-2.7 -33.8,-7.6 -49.3,-15.1 -31.3,-14.5 -57.4,-37.5 -85.3,-57.4 4,7 9.6,13 14.8,19.2 7.5,8.4 15,16.8 22.5,25.2 7.1,7.9 15.2,14.7 22,22.8 -29.3,-12.6 -57.3,-28.7 -82.1,-48.7 -8,-6.1 -18,-8.5 -27.1,-12.2 -23.5,-8.7 -46.5,-18.8 -68.7,-30.5 -19.3,-10.8 -37.8,-23.1 -54.5,-37.6 14.6,-0.4 29.2,-1 43.8,-1.8 -7.3,-4 -15.3,-6.7 -22.2,-11.3 -28.7,-18.9 -52,-45 -71.8,-72.9l80.8,-25.1C159.7,604.7 144.2,584.3 129.4,564.3c13.9,0.5 27.9,-0.2 41.8,-0.2 9.3,-0.1 18.7,0.2 28,-0.3 -6.8,-9.1 -14.5,-17.5 -20.9,-26.9 12.5,-3.6 25.2,-6.5 37.4,-11 -14.1,-0.6 -28.3,1.1 -42.4,-0.9 -31.1,-4.6 -60.7,-18.2 -84.7,-38.4 -22.1,-18 -40.1,-40.6 -54.2,-65.3C13.5,383.2 3.7,339 6.9,295.7c2.9,-36.5 16.5,-73.2 42.7,-99.5 16.1,-17 36.7,-29.8 59.3,-36.4m-24.2,47.6c6.9,3 14.4,4.4 21.4,7.2 7.3,2.8 14.4,6.2 21.4,9.6 26.3,13.2 49.1,33.3 65,58 7.1,11.6 12.5,24.1 17.2,36.9 -12.1,-10.9 -23,-23.1 -36,-33 -12.3,-10.9 -26,-20.1 -40.3,-28.1 -17.1,19.3 -25.7,45 -26.7,70.5 -1.3,36.1 10.2,71.8 29.3,102.1 13.8,21.9 32.7,40.8 55.4,53.4 7.9,4.3 16.4,8.1 25.5,8.7 -4.6,-5.6 -10,-10.4 -14.6,-16 -11.4,-13.3 -19.9,-28.8 -25.9,-45.2 12.7,0.3 25,3.9 37,7.8 -4.8,-7.7 -9.7,-15.3 -13.7,-23.5 -10.5,-21.8 -15.6,-46.2 -15.5,-70.4 15,8.8 28.8,19.4 41.5,31.3 1.3,1.2 11.6,10.9 16.5,17 4.6,5.7 9.6,11.1 14,17 4.6,6.6 9,13.3 14,19.6 1.9,-45.7 -8.6,-92.1 -30.4,-132.3 -18,-32.7 -44.9,-61.1 -78.3,-78.2 -23.6,-11.9 -51,-17.5 -77.1,-12.4m778.2,11.1c-17.5,8 -32.7,20.2 -46.1,33.8 -29.2,29 -47.8,67.3 -56.5,107.3 -4.9,23.2 -8.1,47 -6.8,70.8 10.3,-14.6 20.6,-29.4 33,-42.3 4.8,-5.2 13.5,-14.2 23.4,-22 9.2,-7.7 19.4,-14.1 29.4,-20.7 0.5,20.1 -3,40.3 -10,59.2 -4.6,12.6 -11.8,24 -19,35.3 12,-4 24.3,-7.5 37,-8.8 -4.3,14.8 -12.2,28.3 -21.3,40.7 -5.8,8 -13.6,14.3 -19.9,21.8 16.6,-2.4 31.4,-11.1 44.7,-21 27.1,-21.2 46.3,-51.5 57.1,-84 8.8,-25.9 11.9,-54.1 6.4,-81 -3.9,-18 -11.4,-35.5 -23.4,-49.6 -13.4,6.9 -25.9,15.6 -37.6,25.1 -14.1,10.6 -26,23.7 -38.5,36.1 9.3,-28.2 29,-57.5 53.8,-77.5 16.6,-13.4 36,-23.1 56.3,-29.6 5.1,-1.5 10.3,-2.8 15.4,-4.7 -26,-4.9 -53.4,-0.3 -77.1,11.1M258.7,517.3c5.8,32.7 16.5,65 34.7,92.9 9.8,15.3 23.3,28.6 39.5,37.1 26.8,14.2 60,13.8 88.7,5.7 3.5,-1 7.3,-3 11,-1.6 8.4,2.7 17,5.3 25.9,5.9 -4.9,-5 -10.9,-8.6 -16.5,-12.7 -24.7,-17.9 -49.5,-35.8 -74.6,-53.2 -36.1,-24.9 -72.4,-49.5 -108.7,-74.1m497,6.3c-18.7,13.2 -37.9,25.8 -56.8,38.7 -37,25.5 -74.5,50.3 -110.5,77.2 -8,6.1 -16.5,11.5 -24.2,17.9 9.1,-0.2 17.7,-3.5 26.4,-6.2 3.9,-1.4 7.9,0.5 11.7,1.6 13.6,4.5 48.8,9.3 69.9,1.4 15.4,-4.9 31,-13.1 42.7,-24.2 12.7,-12.9 22.9,-27.8 30.5,-44.2 10.1,-21.8 16.4,-44.6 20,-68.3 -3.3,2 -6.5,4 -9.6,6.2M178,643.6c6.4,4.4 13.2,8.1 19.9,11.8 37.3,19.6 76.4,35.6 114.1,54.6 6.1,3.1 12.6,5.4 18.2,9.5 -21.9,-4.1 -43.6,-9.1 -65.8,-11 -25,-2.2 -51.1,-2.1 -74.8,7.3 4.3,1.3 8.6,2.4 12.9,3.3 43.9,8.8 87.9,17.7 131.6,27.7 -15.2,2 -30.6,3.1 -45.7,6.1 -18.2,3.8 -36.5,8.6 -52.8,17.7 49.4,-2.4 98.6,-7 147.9,-10.3 -13.5,-9.9 -27.6,-19.1 -40.4,-29.9 15.7,-2.4 31.3,-5 46.9,-7.9 -25.4,-18.2 -53.1,-33 -81.5,-45.8 -41.2,-18.1 -85.1,-32.1 -130.4,-33m536.8,32.6c-18.6,8.4 -37,17.3 -54.2,28.3 -9.4,6.1 -19.4,11.4 -28.4,18.2 16.3,2.5 32.6,5.6 49.1,7.5 -13.7,10.4 -28.1,19.9 -41.7,30.5 7.6,0.2 15.2,0.5 22.9,1.3 29,2.2 58.1,4.4 87.1,6.2 12.1,0.3 24.1,2 36.3,1.3 -15.7,-7.9 -32.9,-12.4 -50,-16.1 -15.2,-3.4 -30.8,-4.3 -46.1,-7 29.4,-7 59,-13 88.7,-18.6 18.8,-4.1 37.9,-6.9 56.5,-11.7 -4.4,-2.4 -9.2,-3.6 -14.1,-4.5 -22.1,-5 -45.1,-5.1 -67.5,-2.6 -20.2,2 -40,6.8 -59.9,10.6 9.5,-6.1 20Line truncated
|
||||
android:fillColor="#000000"/>
|
||||
<!-- brow -->
|
||||
<path
|
||||
android:pathData="m371.7,271.4c12.1,2.7 24,6.3 36,9.6 12.2,3.5 24.4,6.9 36.2,11.5 4.2,1.5 8.2,4.9 12.9,3.9 31.9,-5.8 64.6,-5.4 96.7,-2.1 6.6,0.5 13.6,3.7 20.1,0.5 12.3,-5.6 25.4,-9.3 38.3,-13.1 13.2,-3.6 26.3,-7.9 39.8,-10.3L512.6,501.3Z"
|
||||
android:fillColor="#000000"/>
|
||||
<!-- muzzle -->
|
||||
<path
|
||||
android:pathData="m509,859.9c2.3,-1.3 4.7,0.1 6.8,1.1 11,5.8 22.1,11.5 33.3,17 2.8,1.1 5.7,3 8.8,2.2l52.2,-15.5c-18.8,24.1 -40.6,46.2 -65.9,63.6l1.4,-15 -34.3,25.3 -34.2,-25.8 2.2,15.7c-18.9,-13.7 -36.6,-29.2 -52.6,-46.4 -5.1,-5.5 -10.2,-11 -14.5,-17.1l36.5,10.8c5.9,1.4 11.4,4.4 17.5,4.9 5,-1.2 9.4,-4.2 14.1,-6.3 9.7,-4.6 19,-10 28.8,-14.3z"
|
||||
android:pathData="@string/ic_launcher_path_head"
|
||||
android:fillColor="#000000"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Background gradient, shared by ic_launcher_background and ic_launcher_legacy -->
|
||||
<color name="ic_launcher_gradient_start">#960EFC</color>
|
||||
<color name="ic_launcher_gradient_end">#FF804A</color>
|
||||
|
||||
<!-- Head outline plus brow and muzzle, shared by ic_launcher_foreground, ic_launcher_monochrome and ic_launcher_legacy -->
|
||||
<!-- See below for (commented) decoupled version -->
|
||||
<string name="ic_launcher_path_head" translatable="false">m108.8,159.8c25,-7.7 52,-7 77,0 37.3,10.1 69.8,32.8 97.9,58.6 12.7,12.5 25.6,24.7 36.6,38.7 17.2,20.5 31.8,43 44.9,66.3l12.1,20.1 -4.2,-19.8c10.6,18.8 17.2,39.4 25.9,59.1 6.8,16.5 14.9,32.5 22.1,48.9 -13.9,-9.2 -26.9,-19.7 -40.8,-28.9 -9.3,-6 -18.1,-12.8 -27.8,-18.1 10.3,21.3 23.7,40.8 37.1,60.2 12.6,17.5 25.7,34.5 39.6,51L512.6,594.1 611.7,473.9c14,-18 27.4,-36.5 39.8,-55.7 6.9,-10.9 14.1,-21.7 19.2,-33.6 -16.6,9.7 -31.9,21.5 -47.8,32.5 -7.1,4.8 -13.6,10.6 -21.3,14.6 12.4,-24 23.6,-48.7 33.9,-73.7 4.9,-11.1 9.7,-24.4 16,-35.1L648,340.8l8.1,-13.8c20.9,-38.7 47.8,-74 79,-105 28.9,-27.4 62.4,-51.8 101.3,-62.4 27,-7.6 56.2,-7.1 82.8,1.9 36.3,12.3 67.3,39.8 82.8,75 17.5,38.4 19.2,82.5 10.9,123.5 -8.5,40.3 -28.2,78.4 -56.8,108.1 -28.7,30.1 -67.1,52.4 -108.9,57.2 -12.7,1.5 -25.5,-0 -38.1,0.8 11.9,4.3 24.5,6.6 36.4,10.8 -5.9,9.8 -13.7,18.2 -20.9,27 22.9,0.4 45.7,0 68.6,0.3 -10.4,16.2 -23,31 -37.6,43.6 -4.3,3.5 -8.9,6.8 -13.2,10.3l80.8,25.2c-19.6,29 -43.8,55.6 -73.6,74.4 -6.1,3.7 -12.8,6.3 -19.1,9.6 14.6,1.5 29.2,1.5 43.8,1.9 -6.2,6.3 -13.6,11.1 -20.7,16.3 -31.2,22.5 -66.6,38.3 -102.6,51.5 -9.9,4.2 -20.9,6.7 -29.5,13.5 -15.3,11.8 -31.7,22.1 -48.6,31.6 -10.9,5.9 -21.7,12.2 -33.4,16 4.5,-4.9 9.8,-9 14.3,-13.7 10.8,-11.5 21.6,-23.1 31.8,-35.1 4.6,-5.7 10.1,-10.8 13.3,-17.4 -6.2,3.5 -11.5,8.1 -17.3,12.2 -19,13.8 -37.7,28.4 -58.5,39.4 -19.1,9.9 -39.7,17.1 -61.1,20.2 -3.3,0.3 -6.4,-1 -9.4,-2.1 -9.5,-3.8 -18.8,-8.1 -27.9,-12.8 -4,-2 -7.8,-4.5 -12,-6 -2.4,-0.9 -4.7,0.9 -6.8,1.8 -10.5,5.7 -21.2,11 -32.1,15.8 -4.8,2 -9.9,4.2 -15.2,3.2 -17.1,-2.7 -33.8,-7.6 -49.3,-15.1 -31.3,-14.5 -57.4,-37.5 -85.3,-57.4 4,7 9.6,13 14.8,19.2 7.5,8.4 15,16.8 22.5,25.2 7.1,7.9 15.2,14.7 22,22.8 -29.3,-12.6 -57.3,-28.7 -82.1,-48.7 -8,-6.1 -18,-8.5 -27.1,-12.2 -23.5,-8.7 -46.5,-18.8 -68.7,-30.5 -19.3,-10.8 -37.8,-23.1 -54.5,-37.6 14.6,-0.4 29.2,-1 43.8,-1.8 -7.3,-4 -15.3,-6.7 -22.2,-11.3 -28.7,-18.9 -52,-45 -71.8,-72.9l80.8,-25.1C159.7,604.7 144.2,584.3 129.4,564.3c13.9,0.5 27.9,-0.2 41.8,-0.2 9.3,-0.1 18.7,0.2 28,-0.3 -6.8,-9.1 -14.5,-17.5 -20.9,-26.9 12.5,-3.6 25.2,-6.5 37.4,-11 -14.1,-0.6 -28.3,1.1 -42.4,-0.9 -31.1,-4.6 -60.7,-18.2 -84.7,-38.4 -22.1,-18 -40.1,-40.6 -54.2,-65.3C13.5,383.2 3.7,339 6.9,295.7c2.9,-36.5 16.5,-73.2 42.7,-99.5 16.1,-17 36.7,-29.8 59.3,-36.4m-24.2,47.6c6.9,3 14.4,4.4 21.4,7.2 7.3,2.8 14.4,6.2 21.4,9.6 26.3,13.2 49.1,33.3 65,58 7.1,11.6 12.5,24.1 17.2,36.9 -12.1,-10.9 -23,-23.1 -36,-33 -12.3,-10.9 -26,-20.1 -40.3,-28.1 -17.1,19.3 -25.7,45 -26.7,70.5 -1.3,36.1 10.2,71.8 29.3,102.1 13.8,21.9 32.7,40.8 55.4,53.4 7.9,4.3 16.4,8.1 25.5,8.7 -4.6,-5.6 -10,-10.4 -14.6,-16 -11.4,-13.3 -19.9,-28.8 -25.9,-45.2 12.7,0.3 25,3.9 37,7.8 -4.8,-7.7 -9.7,-15.3 -13.7,-23.5 -10.5,-21.8 -15.6,-46.2 -15.5,-70.4 15,8.8 28.8,19.4 41.5,31.3 1.3,1.2 11.6,10.9 16.5,17 4.6,5.7 9.6,11.1 14,17 4.6,6.6 9,13.3 14,19.6 1.9,-45.7 -8.6,-92.1 -30.4,-132.3 -18,-32.7 -44.9,-61.1 -78.3,-78.2 -23.6,-11.9 -51,-17.5 -77.1,-12.4m778.2,11.1c-17.5,8 -32.7,20.2 -46.1,33.8 -29.2,29 -47.8,67.3 -56.5,107.3 -4.9,23.2 -8.1,47 -6.8,70.8 10.3,-14.6 20.6,-29.4 33,-42.3 4.8,-5.2 13.5,-14.2 23.4,-22 9.2,-7.7 19.4,-14.1 29.4,-20.7 0.5,20.1 -3,40.3 -10,59.2 -4.6,12.6 -11.8,24 -19,35.3 12,-4 24.3,-7.5 37,-8.8 -4.3,14.8 -12.2,28.3 -21.3,40.7 -5.8,8 -13.6,14.3 -19.9,21.8 16.6,-2.4 31.4,-11.1 44.7,-21 27.1,-21.2 46.3,-51.5 57.1,-84 8.8,-25.9 11.9,-54.1 6.4,-81 -3.9,-18 -11.4,-35.5 -23.4,-49.6 -13.4,6.9 -25.9,15.6 -37.6,25.1 -14.1,10.6 -26,23.7 -38.5,36.1 9.3,-28.2 29,-57.5 53.8,-77.5 16.6,-13.4 36,-23.1 56.3,-29.6 5.1,-1.5 10.3,-2.8 15.4,-4.7 -26,-4.9 -53.4,-0.3 -77.1,11.1M258.7,517.3c5.8,32.7 16.5,65 34.7,92.9 9.8,15.3 23.3,28.6 39.5,37.1 26.8,14.2 60,13.8 88.7,5.7 3.5,-1 7.3,-3 11,-1.6 8.4,2.7 17,5.3 25.9,5.9 -4.9,-5 -10.9,-8.6 -16.5,-12.7 -24.7,-17.9 -49.5,-35.8 -74.6,-53.2 -36.1,-24.9 -72.4,-49.5 -108.7,-74.1m497,6.3c-18.7,13.2 -37.9,25.8 -56.8,38.7 -37,25.5 -74.5,50.3 -110.5,77.2 -8,6.1 -16.5,11.5 -24.2,17.9 9.1,-0.2 17.7,-3.5 26.4,-6.2 3.9,-1.4 7.9,0.5 11.7,1.6 13.6,4.5 48.8,9.3 69.9,1.4 15.4,-4.9 31,-13.1 42.7,-24.2 12.7,-12.9 22.9,-27.8 30.5,-44.2 10.1,-21.8 16.4,-44.6 20,-68.3 -3.3,2 -6.5,4 -9.6,6.2M178,643.6c6.4,4.4 13.2,8.1 19.9,11.8 37.3,19.6 76.4,35.6 114.1,54.6 6.1,3.1 12.6,5.4 18.2,9.5 -21.9,-4.1 -43.6,-9.1 -65.8,-11 -25,-2.2 -51.1,-2.1 -74.8,7.3 4.3,1.3 8.6,2.4 12.9,3.3 43.9,8.8 87.9,17.7 131.6,27.7 -15.2,2 -30.6,3.1 -45.7,6.1 -18.2,3.8 -36.5,8.6 -52.8,17.7 49.4,-2.4 98.6,-7 147.9,-10.3 -13.5,-9.9 -27.6,-19.1 -40.4,-29.9 15.7,-2.4 31.3,-5 46.9,-7.9 -25.4,-18.2 -53.1,-33 -81.5,-45.8 -41.2,-18.1 -85.1,-32.1 -130.4,-33m536.8,32.6c-18.6,8.4 -37,17.3 -54.2,28.3 -9.4,6.1 -19.4,11.4 -28.4,18.2 16.3,2.5 32.6,5.6 49.1,7.5 -13.7,10.4 -28.1,19.9 -41.7,30.5 7.6,0.2 15.2,0.5 22.9,1.3 29,2.2 58.1,4.4 87.1,6.2 12.1,0.3 24.1,2 36.3,1.3 -15.7,-7.9 -32.9,-12.4 -50,-16.1 -15.2,-3.4 -30.8,-4.3 -46.1,-7 29.4,-7 59,-13 88.7,-18.6 18.8,-4.1 37.9,-6.9 56.5,-11.7 -4.4,-2.4 -9.2,-3.6 -14.1,-4.5 -22.1,-5 -45.1,-5.1 -67.5,-2.6 -2Line truncated
|
||||
|
||||
<!-- OLD ICON PATHS: decoupled head, brow and muzzle respectvely
|
||||
<path
|
||||
android:pathData="m108.8,159.8c25,-7.7 52,-7 77,0 37.3,10.1 69.8,32.8 97.9,58.6 12.7,12.5 25.6,24.7 36.6,38.7 17.2,20.5 31.8,43 44.9,66.3l12.1,20.1 -4.2,-19.8c10.6,18.8 17.2,39.4 25.9,59.1 6.8,16.5 14.9,32.5 22.1,48.9 -13.9,-9.2 -26.9,-19.7 -40.8,-28.9 -9.3,-6 -18.1,-12.8 -27.8,-18.1 10.3,21.3 23.7,40.8 37.1,60.2 12.6,17.5 25.7,34.5 39.6,51L512.6,594.1 611.7,473.9c14,-18 27.4,-36.5 39.8,-55.7 6.9,-10.9 14.1,-21.7 19.2,-33.6 -16.6,9.7 -31.9,21.5 -47.8,32.5 -7.1,4.8 -13.6,10.6 -21.3,14.6 12.4,-24 23.6,-48.7 33.9,-73.7 4.9,-11.1 9.7,-24.4 16,-35.1L648,340.8l8.1,-13.8c20.9,-38.7 47.8,-74 79,-105 28.9,-27.4 62.4,-51.8 101.3,-62.4 27,-7.6 56.2,-7.1 82.8,1.9 36.3,12.3 67.3,39.8 82.8,75 17.5,38.4 19.2,82.5 10.9,123.5 -8.5,40.3 -28.2,78.4 -56.8,108.1 -28.7,30.1 -67.1,52.4 -108.9,57.2 -12.7,1.5 -25.5,-0 -38.1,0.8 11.9,4.3 24.5,6.6 36.4,10.8 -5.9,9.8 -13.7,18.2 -20.9,27 22.9,0.4 45.7,0 68.6,0.3 -10.4,16.2 -23,31 -37.6,43.6 -4.3,3.5 -8.9,6.8 -13.2,10.3l80.8,25.2c-19.6,29 -43.8,55.6 -73.6,74.4 -6.1,3.7 -12.8,6.3 -19.1,9.6 14.6,1.5 29.2,1.5 43.8,1.9 -6.2,6.3 -13.6,11.1 -20.7,16.3 -31.2,22.5 -66.6,38.3 -102.6,51.5 -9.9,4.2 -20.9,6.7 -29.5,13.5 -15.3,11.8 -31.7,22.1 -48.6,31.6 -10.9,5.9 -21.7,12.2 -33.4,16 4.5,-4.9 9.8,-9 14.3,-13.7 10.8,-11.5 21.6,-23.1 31.8,-35.1 4.6,-5.7 10.1,-10.8 13.3,-17.4 -6.2,3.5 -11.5,8.1 -17.3,12.2 -19,13.8 -37.7,28.4 -58.5,39.4 -19.1,9.9 -39.7,17.1 -61.1,20.2 -3.3,0.3 -6.4,-1 -9.4,-2.1 -9.5,-3.8 -18.8,-8.1 -27.9,-12.8 -4,-2 -7.8,-4.5 -12,-6 -2.4,-0.9 -4.7,0.9 -6.8,1.8 -10.5,5.7 -21.2,11 -32.1,15.8 -4.8,2 -9.9,4.2 -15.2,3.2 -17.1,-2.7 -33.8,-7.6 -49.3,-15.1 -31.3,-14.5 -57.4,-37.5 -85.3,-57.4 4,7 9.6,13 14.8,19.2 7.5,8.4 15,16.8 22.5,25.2 7.1,7.9 15.2,14.7 22,22.8 -29.3,-12.6 -57.3,-28.7 -82.1,-48.7 -8,-6.1 -18,-8.5 -27.1,-12.2 -23.5,-8.7 -46.5,-18.8 -68.7,-30.5 -19.3,-10.8 -37.8,-23.1 -54.5,-37.6 14.6,-0.4 29.2,-1 43.8,-1.8 -7.3,-4 -15.3,-6.7 -22.2,-11.3 -28.7,-18.9 -52,-45 -71.8,-72.9l80.8,-25.1C159.7,604.7 144.2,584.3 129.4,564.3c13.9,0.5 27.9,-0.2 41.8,-0.2 9.3,-0.1 18.7,0.2 28,-0.3 -6.8,-9.1 -14.5,-17.5 -20.9,-26.9 12.5,-3.6 25.2,-6.5 37.4,-11 -14.1,-0.6 -28.3,1.1 -42.4,-0.9 -31.1,-4.6 -60.7,-18.2 -84.7,-38.4 -22.1,-18 -40.1,-40.6 -54.2,-65.3C13.5,383.2 3.7,339 6.9,295.7c2.9,-36.5 16.5,-73.2 42.7,-99.5 16.1,-17 36.7,-29.8 59.3,-36.4m-24.2,47.6c6.9,3 14.4,4.4 21.4,7.2 7.3,2.8 14.4,6.2 21.4,9.6 26.3,13.2 49.1,33.3 65,58 7.1,11.6 12.5,24.1 17.2,36.9 -12.1,-10.9 -23,-23.1 -36,-33 -12.3,-10.9 -26,-20.1 -40.3,-28.1 -17.1,19.3 -25.7,45 -26.7,70.5 -1.3,36.1 10.2,71.8 29.3,102.1 13.8,21.9 32.7,40.8 55.4,53.4 7.9,4.3 16.4,8.1 25.5,8.7 -4.6,-5.6 -10,-10.4 -14.6,-16 -11.4,-13.3 -19.9,-28.8 -25.9,-45.2 12.7,0.3 25,3.9 37,7.8 -4.8,-7.7 -9.7,-15.3 -13.7,-23.5 -10.5,-21.8 -15.6,-46.2 -15.5,-70.4 15,8.8 28.8,19.4 41.5,31.3 1.3,1.2 11.6,10.9 16.5,17 4.6,5.7 9.6,11.1 14,17 4.6,6.6 9,13.3 14,19.6 1.9,-45.7 -8.6,-92.1 -30.4,-132.3 -18,-32.7 -44.9,-61.1 -78.3,-78.2 -23.6,-11.9 -51,-17.5 -77.1,-12.4m778.2,11.1c-17.5,8 -32.7,20.2 -46.1,33.8 -29.2,29 -47.8,67.3 -56.5,107.3 -4.9,23.2 -8.1,47 -6.8,70.8 10.3,-14.6 20.6,-29.4 33,-42.3 4.8,-5.2 13.5,-14.2 23.4,-22 9.2,-7.7 19.4,-14.1 29.4,-20.7 0.5,20.1 -3,40.3 -10,59.2 -4.6,12.6 -11.8,24 -19,35.3 12,-4 24.3,-7.5 37,-8.8 -4.3,14.8 -12.2,28.3 -21.3,40.7 -5.8,8 -13.6,14.3 -19.9,21.8 16.6,-2.4 31.4,-11.1 44.7,-21 27.1,-21.2 46.3,-51.5 57.1,-84 8.8,-25.9 11.9,-54.1 6.4,-81 -3.9,-18 -11.4,-35.5 -23.4,-49.6 -13.4,6.9 -25.9,15.6 -37.6,25.1 -14.1,10.6 -26,23.7 -38.5,36.1 9.3,-28.2 29,-57.5 53.8,-77.5 16.6,-13.4 36,-23.1 56.3,-29.6 5.1,-1.5 10.3,-2.8 15.4,-4.7 -26,-4.9 -53.4,-0.3 -77.1,11.1M258.7,517.3c5.8,32.7 16.5,65 34.7,92.9 9.8,15.3 23.3,28.6 39.5,37.1 26.8,14.2 60,13.8 88.7,5.7 3.5,-1 7.3,-3 11,-1.6 8.4,2.7 17,5.3 25.9,5.9 -4.9,-5 -10.9,-8.6 -16.5,-12.7 -24.7,-17.9 -49.5,-35.8 -74.6,-53.2 -36.1,-24.9 -72.4,-49.5 -108.7,-74.1m497,6.3c-18.7,13.2 -37.9,25.8 -56.8,38.7 -37,25.5 -74.5,50.3 -110.5,77.2 -8,6.1 -16.5,11.5 -24.2,17.9 9.1,-0.2 17.7,-3.5 26.4,-6.2 3.9,-1.4 7.9,0.5 11.7,1.6 13.6,4.5 48.8,9.3 69.9,1.4 15.4,-4.9 31,-13.1 42.7,-24.2 12.7,-12.9 22.9,-27.8 30.5,-44.2 10.1,-21.8 16.4,-44.6 20,-68.3 -3.3,2 -6.5,4 -9.6,6.2M178,643.6c6.4,4.4 13.2,8.1 19.9,11.8 37.3,19.6 76.4,35.6 114.1,54.6 6.1,3.1 12.6,5.4 18.2,9.5 -21.9,-4.1 -43.6,-9.1 -65.8,-11 -25,-2.2 -51.1,-2.1 -74.8,7.3 4.3,1.3 8.6,2.4 12.9,3.3 43.9,8.8 87.9,17.7 131.6,27.7 -15.2,2 -30.6,3.1 -45.7,6.1 -18.2,3.8 -36.5,8.6 -52.8,17.7 49.4,-2.4 98.6,-7 147.9,-10.3 -13.5,-9.9 -27.6,-19.1 -40.4,-29.9 15.7,-2.4 31.3,-5 46.9,-7.9 -25.4,-18.2 -53.1,-33 -81.5,-45.8 -41.2,-18.1 -85.1,-32.1 -130.4,-33m536.8,32.6c-18.6,8.4 -37,17.3 -54.2,28.3 -9.4,6.1 -19.4,11.4 -28.4,18.2 16.3,2.5 32.6,5.6 49.1,7.5 -13.7,10.4 -28.1,19.9 -41.7,30.5 7.6,0.2 15.2,0.5 22.9,1.3 29,2.2 58.1,4.4 87.1,6.2 12.1,0.3 24.1,2 36.3,1.3 -15.7,-7.9 -32.9,-12.4 -50,-16.1 -15.2,-3.4 -30.8,-4.3 -46.1,-7 29.4,-7 59,-13 88.7,-18.6 18.8,-4.1 37.9,-6.9 56.5,-11.7 -4.4,-2.4 -9.2,-3.6 -14.1,-4.5 -22.1,-5 -45.1,-5.1 -67.5,-2.6 -20.2,2 -40,6.8 -59.9,10.6 9.5,-6.1 20Line truncated
|
||||
android:fillColor="#fcecdc"/>
|
||||
<path
|
||||
android:pathData="m371.7,271.4c12.1,2.7 24,6.3 36,9.6 12.2,3.5 24.4,6.9 36.2,11.5 4.2,1.5 8.2,4.9 12.9,3.9 31.9,-5.8 64.6,-5.4 96.7,-2.1 6.6,0.5 13.6,3.7 20.1,0.5 12.3,-5.6 25.4,-9.3 38.3,-13.1 13.2,-3.6 26.3,-7.9 39.8,-10.3L512.6,501.3Z"
|
||||
android:fillColor="#fcecdc"/>
|
||||
<path
|
||||
android:pathData="m509,859.9c2.3,-1.3 4.7,0.1 6.8,1.1 11,5.8 22.1,11.5 33.3,17 2.8,1.1 5.7,3 8.8,2.2l52.2,-15.5c-18.8,24.1 -40.6,46.2 -65.9,63.6l1.4,-15 -34.3,25.3 -34.2,-25.8 2.2,15.7c-18.9,-13.7 -36.6,-29.2 -52.6,-46.4 -5.1,-5.5 -10.2,-11 -14.5,-17.1l36.5,10.8c5.9,1.4 11.4,4.4 17.5,4.9 5,-1.2 9.4,-4.2 14.1,-6.3 9.7,-4.6 19,-10 28.8,-14.3z"
|
||||
android:fillColor="#fcecdc"/>
|
||||
-->
|
||||
|
||||
<!-- Legacy (non-adaptive) icon: unscaled, see drawable/ic_launcher_legacy. The adaptive icon in
|
||||
mipmap-anydpi-v26 takes precedence on API 26+; see gradle/legacy-icon.gradle -->
|
||||
<!-- A vector in res/mipmap trips the IDE, so alias the drawable instead -->
|
||||
<item name="ic_launcher" type="mipmap" tools:ignore="ReferenceType">@drawable/ic_launcher_legacy</item>
|
||||
</resources>
|
||||
Reference in new issue
Block a user