1e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn// 2fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski// Copyright 2014 The Android Open Source Project 3e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn// 4e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn// Build resource files from raw assets. 5e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn// 6e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 7e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn#include "ResourceFilter.h" 8fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski#include "AaptUtil.h" 9fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski#include "AaptConfig.h" 10e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 11e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornstatus_t 12fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam LesinskiWeakResourceFilter::parse(const String8& str) 13e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn{ 14fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski Vector<String8> configStrs = AaptUtil::split(str, ','); 15fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski const size_t N = configStrs.size(); 16fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigs.clear(); 17fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigMask = 0; 18fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigs.resize(N); 19fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski for (size_t i = 0; i < N; i++) { 20fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski const String8& part = configStrs[i]; 21a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Krumin if (part == "en_XA") { 22a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Krumin mContainsPseudoAccented = true; 23a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Krumin } else if (part == "ar_XB") { 24a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Krumin mContainsPseudoBidi = true; 25e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 26e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 27fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i); 28788fa41482b9d398591b7db8b0b01839029611adNarayan Kamath 29fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski AaptLocaleValue val; 30fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski if (val.initFromFilterString(part)) { 31fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // For backwards compatibility, we accept configurations that 32fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // only specify locale in the standard 'en_US' format. 33fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski val.writeTo(&entry.first); 34fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski } else if (!AaptConfig::parse(part, &entry.first)) { 35fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski fprintf(stderr, "Invalid configuration: %s\n", part.string()); 36fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski return UNKNOWN_ERROR; 37e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 38fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski 39fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski entry.second = mDefault.diff(entry.first); 40fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski 41fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // Ignore the version 42fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski entry.second &= ~ResTable_config::CONFIG_VERSION; 43fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski 44a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski // Ignore any densities. Those are best handled in --preferred-density 45a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski if ((entry.second & ResTable_config::CONFIG_DENSITY) != 0) { 46a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski fprintf(stderr, "warning: ignoring flag -c %s. Use --preferred-density instead.\n", entry.first.toString().string()); 47a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski entry.first.density = 0; 48a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski entry.second &= ~ResTable_config::CONFIG_DENSITY; 49a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski } 50a2bb565db553e3def89a6e010b59f2fd1db98ff4Adam Lesinski 51fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigMask |= entry.second; 52e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 53e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 54e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn return NO_ERROR; 55e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn} 56e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 573f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// Returns true if the locale script of the config should be considered matching 583f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// the locale script of entry. 593f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// 603f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// If both the scripts are empty, the scripts are considered matching for 613f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// backward compatibility reasons. 623f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// 633f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// If only one script is empty, we try to compute it based on the provided 643f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// language and country. If we could not compute it, we assume it's either a 653f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// new language we don't know about, or a private use language. We return true 663f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// since we don't know any better and they might as well be a match. 673f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// 683f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// Finally, when we have two scripts (one of which could be computed), we return 693f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader// true if and only if they are an exact match. 703f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournaderinline bool 713f32c27ccfee10e77f850747b50f6df76b150805Roozbeh PournaderscriptsMatch(const ResTable_config& config, const ResTable_config& entry) { 723f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader const char* configScript = config.localeScript; 733f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader const char* entryScript = entry.localeScript; 743f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader if (configScript[0] == '\0' && entryScript[0] == '\0') { 753f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader return true; // both scripts are empty. We match for backward compatibility reasons. 763f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader } 773f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader 783f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader char scriptBuffer[sizeof(config.localeScript)]; 793f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader if (configScript[0] == '\0') { 803f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader localeDataComputeScript(scriptBuffer, config.language, config.country); 813f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match. 823f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader return true; 833f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader } 843f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader configScript = scriptBuffer; 853f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader } else if (entryScript[0] == '\0') { 863f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader localeDataComputeScript( 873f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader scriptBuffer, entry.language, entry.country); 883f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match. 893f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader return true; 903f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader } 913f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader entryScript = scriptBuffer; 923f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader } 933f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader return (memcmp(configScript, entryScript, sizeof(config.localeScript)) == 0); 943f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader} 953f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader 963f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader 97e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornbool 98fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam LesinskiWeakResourceFilter::match(const ResTable_config& config) const 99e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn{ 100fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski uint32_t mask = mDefault.diff(config); 101fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski if ((mConfigMask & mask) == 0) { 102fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // The two configurations don't have any common axis. 103e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn return true; 104e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 105e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 106a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski uint32_t matchedAxis = 0x0; 107fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski const size_t N = mConfigs.size(); 108fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski for (size_t i = 0; i < N; i++) { 109fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i]; 110fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski uint32_t diff = entry.first.diff(config); 111fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski if ((diff & entry.second) == 0) { 112a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski // Mark the axis that was matched. 113a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski matchedAxis |= entry.second; 114fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) { 115fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // If the locales differ, but the languages are the same and 116fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // the locale we are matching only has a language specified, 117fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski // we match. 1183f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // 1193f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // Exception: we won't match if a script is specified for at least 1203f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // one of the locales and it's different from the other locale's 1213f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // script. (We will compute the other script if at least one of the 1223f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // scripts were explicitly set. In cases we can't compute an script, 1233f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader // we match.) 1243f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader if (config.language[0] != '\0' && 1253f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader config.country[0] == '\0' && 1263f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader config.localeVariant[0] == '\0' && 1273f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader config.language[0] == entry.first.language[0] && 1283f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader config.language[1] == entry.first.language[1] && 1293f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader scriptsMatch(config, entry.first)) { 1303f32c27ccfee10e77f850747b50f6df76b150805Roozbeh Pournader matchedAxis |= ResTable_config::CONFIG_LOCALE; 131fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski } 132a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski } else if ((diff & entry.second) == ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) { 133a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski // Special case if the smallest screen width doesn't match. We check that the 134a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski // config being matched has a smaller screen width than the filter specified. 135a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski if (config.smallestScreenWidthDp != 0 && 136a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski config.smallestScreenWidthDp < entry.first.smallestScreenWidthDp) { 137a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski matchedAxis |= ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE; 138a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski } 139e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 140e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 141a5cc002bfe7ca8a6536549189b44e7143c8611dcAdam Lesinski return matchedAxis == (mConfigMask & mask); 142e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn} 143e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn 144fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskistatus_t 145fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam LesinskiStrongResourceFilter::parse(const String8& str) { 146fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski Vector<String8> configStrs = AaptUtil::split(str, ','); 147fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski ConfigDescription config; 148fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigs.clear(); 149fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski for (size_t i = 0; i < configStrs.size(); i++) { 150fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski if (!AaptConfig::parse(configStrs[i], &config)) { 151fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string()); 152fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski return UNKNOWN_ERROR; 153fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski } 154fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski mConfigs.insert(config); 155e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn } 156fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski return NO_ERROR; 157e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn} 158