| /* |
| * Copyright (C) 2022 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.build.shrinker |
| |
| import com.android.aapt.Resources |
| |
| internal fun Resources.ResourceTable.entriesSequence(): Sequence<EntryWrapper> = sequence { |
| for (resourcePackage in packageList) { |
| for (resourceType in resourcePackage.typeList) { |
| for (resourceEntry in resourceType.entryList) { |
| val id = toIdentifier(resourcePackage, resourceType, resourceEntry) |
| yield( |
| EntryWrapper(id, resourcePackage.packageName, resourceType.name, resourceEntry) |
| ) |
| } |
| } |
| } |
| } |
| |
| internal fun Resources.ResourceTable.nullOutEntriesWithIds(ids: List<Int>) |
| : Resources.ResourceTable { |
| return nullOutEntriesWithIds(ids, false) |
| } |
| |
| internal fun Resources.ResourceTable.nullOutEntriesWithIds( |
| ids: List<Int>, pruneResourceNames: Boolean): Resources.ResourceTable { |
| if (ids.isEmpty()) { |
| return this |
| } |
| val packageMappings = calculatePackageMappings(ids) |
| val tableBuilder = this.toBuilder() |
| tableBuilder.packageBuilderList.forEach { |
| val typeMappings = packageMappings[it.packageId.id] |
| if (typeMappings != null) { |
| it.typeBuilderList.forEach { type -> |
| val entryList = typeMappings[type.typeId.id] |
| if (entryList != null) { |
| type.entryBuilderList.forEach { entry -> |
| if (entryList.contains(entry.entryId.id)) { |
| entry.clearConfigValue() |
| if (pruneResourceNames) { |
| entry.clearName(); |
| } |
| if (entry.hasOverlayableItem()) { |
| entry.clearOverlayableItem() |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return tableBuilder.build() |
| } |
| |
| private fun calculatePackageMappings(ids: List<Int>): MutableMap<Int, Map<Int, List<Int>>> { |
| val sortedIds = ids.sorted() |
| val packageMapping = mutableMapOf<Int, Map<Int, List<Int>>>() |
| var typeMapping = mutableMapOf<Int, List<Int>>() |
| var entryList = mutableListOf<Int>() |
| var oldPackageId = -1 |
| var oldTypeId = -1 |
| for (value in sortedIds) { |
| val packageId = packageIdFromIdentifier(value) |
| val typeId = typeIdFromIdentifier(value) |
| val entryId = entryIdFromIdentifier(value) |
| if (packageId != oldPackageId) { |
| typeMapping = mutableMapOf() |
| packageMapping.put(packageId, typeMapping) |
| oldPackageId = packageId |
| oldTypeId = -1 |
| } |
| if (typeId != oldTypeId) { |
| entryList = mutableListOf() |
| typeMapping.put(typeId, entryList) |
| oldTypeId = typeId |
| } |
| entryList.add(entryId) |
| } |
| return packageMapping |
| } |
| |
| internal data class EntryWrapper( |
| val id: Int, |
| val packageName: String, |
| val type: String, |
| val entry: Resources.Entry |
| ) |
| |
| fun toIdentifier( |
| resourcePackage: Resources.Package, |
| type: Resources.Type, |
| entry: Resources.Entry |
| ): Int = |
| (resourcePackage.packageId.id shl 24) or (type.typeId.id shl 16) or entry.entryId.id |
| |
| private fun packageIdFromIdentifier( |
| identifier: Int |
| ): Int = |
| identifier shr 24 |
| |
| private fun typeIdFromIdentifier( |
| identifier: Int |
| ): Int = |
| (identifier and 0x00FF0000) shr 16 |
| |
| private fun entryIdFromIdentifier( |
| identifier: Int |
| ): Int = |
| (identifier and 0x0000FFFF) |
| |
| |