1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "res_config_impl.h"
16 #ifdef SUPPORT_GRAPHICS
17 #include <unicode/localebuilder.h>
18 #include <unicode/locid.h>
19 #include <unicode/utypes.h>
20 #endif
21 #include "locale_matcher.h"
22 #include "res_locale.h"
23 #include "utils/utils.h"
24 #ifdef SUPPORT_GRAPHICS
25 using icu::Locale;
26 using icu::LocaleBuilder;
27 #endif
28 namespace OHOS {
29 namespace Global {
30 namespace Resource {
31
32 static const std::vector<std::pair<float, ScreenDensity>> resolutions = {
33 { 0.0, ScreenDensity::SCREEN_DENSITY_NOT_SET },
34 { 120.0, ScreenDensity::SCREEN_DENSITY_SDPI },
35 { 160.0, ScreenDensity::SCREEN_DENSITY_MDPI },
36 { 240.0, ScreenDensity::SCREEN_DENSITY_LDPI },
37 { 320.0, ScreenDensity::SCREEN_DENSITY_XLDPI },
38 { 480.0, ScreenDensity::SCREEN_DENSITY_XXLDPI },
39 { 640.0, ScreenDensity::SCREEN_DENSITY_XXXLDPI },
40 };
41
ResConfigImpl()42 ResConfigImpl::ResConfigImpl()
43 : resLocale_(nullptr),
44 direction_(DIRECTION_NOT_SET),
45 density_(SCREEN_DENSITY_NOT_SET),
46 screenDensityDpi_(SCREEN_DENSITY_NOT_SET),
47 colorMode_(LIGHT),
48 mcc_(MCC_UNDEFINED),
49 mnc_(MNC_UNDEFINED),
50 deviceType_(DEVICE_NOT_SET),
51 inputDevice_(INPUTDEVICE_NOT_SET),
52 #ifdef SUPPORT_GRAPHICS
53 localeInfo_(nullptr),
54 #endif
55 isCompletedScript_(false)
56 {}
57
58 #ifdef SUPPORT_GRAPHICS
SetLocaleInfo(Locale & localeInfo)59 RState ResConfigImpl::SetLocaleInfo(Locale &localeInfo)
60 {
61 return this->SetLocaleInfo(localeInfo.getLanguage(), localeInfo.getScript(), localeInfo.getCountry());
62 }
63 #endif
64
SetLocaleInfo(const char * language,const char * script,const char * region)65 RState ResConfigImpl::SetLocaleInfo(const char *language,
66 const char *script,
67 const char *region)
68 {
69 #ifdef SUPPORT_GRAPHICS
70 RState state = SUCCESS;
71 if (Utils::IsStrEmpty(language)) {
72 delete this->resLocale_;
73 delete this->localeInfo_;
74 this->resLocale_ = nullptr;
75 this->localeInfo_ = nullptr;
76 return state;
77 }
78 ResLocale *resLocale =
79 ResLocale::BuildFromParts(language, script, region, state);
80 if (state == SUCCESS) {
81 this->isCompletedScript_ = false;
82 if (script == nullptr || script[0] == '\0') {
83 if (LocaleMatcher::Normalize(resLocale)) {
84 this->isCompletedScript_ = true;
85 } else {
86 delete resLocale;
87 return NOT_ENOUGH_MEM;
88 }
89 }
90 UErrorCode errCode = U_ZERO_ERROR;
91 Locale temp = icu::LocaleBuilder().setLanguage(resLocale->GetLanguage())
92 .setRegion(resLocale->GetRegion()).setScript(resLocale->GetScript()).build(errCode);
93
94 if (!U_SUCCESS(errCode)) {
95 state = NOT_ENOUGH_MEM;
96 delete resLocale;
97 return state;
98 }
99 delete resLocale_;
100 delete localeInfo_;
101 resLocale_ = resLocale;
102 localeInfo_ = new Locale(temp);
103 }
104
105 return state;
106 #else
107 return NOT_SUPPORT_SEP;
108 #endif
109 }
110
SetDeviceType(DeviceType deviceType)111 void ResConfigImpl::SetDeviceType(DeviceType deviceType)
112 {
113 this->deviceType_ = deviceType;
114 }
115
SetDirection(Direction direction)116 void ResConfigImpl::SetDirection(Direction direction)
117 {
118 this->direction_ = direction;
119 }
120
SetColorMode(ColorMode colorMode)121 void ResConfigImpl::SetColorMode(ColorMode colorMode)
122 {
123 this->colorMode_ = colorMode;
124 }
125
SetInputDevice(InputDevice inputDevice)126 void ResConfigImpl::SetInputDevice(InputDevice inputDevice)
127 {
128 this->inputDevice_ = inputDevice;
129 }
130
SetMcc(uint32_t mcc)131 void ResConfigImpl::SetMcc(uint32_t mcc)
132 {
133 this->mcc_ = mcc;
134 }
135
SetMnc(uint32_t mnc)136 void ResConfigImpl::SetMnc(uint32_t mnc)
137 {
138 this->mnc_ = mnc;
139 }
140
ConvertDensity(float density)141 ScreenDensity ResConfigImpl::ConvertDensity(float density)
142 {
143 float deviceDpi = density * Utils::DPI_BASE;
144 auto resolution = SCREEN_DENSITY_NOT_SET;
145 for (const auto& [dpi, value] : resolutions) {
146 resolution = value;
147 if (deviceDpi <= dpi) {
148 break;
149 }
150 }
151 return resolution;
152 }
153
SetScreenDensity(float screenDensity)154 void ResConfigImpl::SetScreenDensity(float screenDensity)
155 {
156 this->density_ = screenDensity;
157 this->screenDensityDpi_ = ConvertDensity(screenDensity);
158 }
159
160 #ifdef SUPPORT_GRAPHICS
GetLocaleInfo() const161 const Locale *ResConfigImpl::GetLocaleInfo() const
162 {
163 return localeInfo_;
164 }
165 #endif
166
GetResLocale() const167 const ResLocale *ResConfigImpl::GetResLocale() const
168 {
169 return this->resLocale_;
170 }
171
GetDirection() const172 Direction ResConfigImpl::GetDirection() const
173 {
174 return this->direction_;
175 }
176
GetScreenDensity() const177 float ResConfigImpl::GetScreenDensity() const
178 {
179 return this->density_;
180 }
181
GetColorMode() const182 ColorMode ResConfigImpl::GetColorMode() const
183 {
184 return this->colorMode_;
185 }
186
GetInputDevice() const187 InputDevice ResConfigImpl::GetInputDevice() const
188 {
189 return this->inputDevice_;
190 }
191
GetMcc() const192 uint32_t ResConfigImpl::GetMcc() const
193 {
194 return this->mcc_;
195 }
196
GetMnc() const197 uint32_t ResConfigImpl::GetMnc() const
198 {
199 return this->mnc_;
200 }
201
GetDeviceType() const202 DeviceType ResConfigImpl::GetDeviceType() const
203 {
204 return this->deviceType_;
205 }
CopyLocale(ResConfig & other)206 bool ResConfigImpl::CopyLocale(ResConfig &other)
207 {
208 #ifdef SUPPORT_GRAPHICS
209 bool needCopy = false;
210 if (this->GetLocaleInfo() == nullptr && other.GetLocaleInfo() != nullptr) {
211 needCopy = true;
212 }
213 if (this->GetLocaleInfo() != nullptr && other.GetLocaleInfo() == nullptr) {
214 delete this->resLocale_;
215 delete this->localeInfo_;
216 this->resLocale_ = nullptr;
217 this->localeInfo_ = nullptr;
218 return true;
219 }
220 if (this->GetResLocale() != nullptr && other.GetLocaleInfo() != nullptr) {
221 uint64_t encodedLocale = Utils::EncodeLocale(
222 this->GetResLocale()->GetLanguage(),
223 this->GetResLocale()->GetScript(), this->GetResLocale()->GetRegion());
224 uint64_t otherEncodedLocale = Utils::EncodeLocale(
225 other.GetLocaleInfo()->getLanguage(),
226 other.GetLocaleInfo()->getScript(), other.GetLocaleInfo()->getCountry());
227 if (encodedLocale != otherEncodedLocale) {
228 needCopy = true;
229 }
230 }
231 if (needCopy) {
232 ResLocale *temp = new(std::nothrow) ResLocale;
233 if (temp == nullptr) {
234 return false;
235 }
236 RState rs = temp->CopyFromLocaleInfo(other.GetLocaleInfo());
237 if (rs != SUCCESS) {
238 delete temp;
239 return false;
240 }
241 UErrorCode errCode = U_ZERO_ERROR;
242 Locale tempLocale = icu::LocaleBuilder().setLocale(*other.GetLocaleInfo()).build(errCode);
243
244 if (!U_SUCCESS(errCode)) {
245 delete temp;
246 return false;
247 }
248 delete this->resLocale_;
249 delete this->localeInfo_;
250 this->resLocale_ = temp;
251 this->localeInfo_ = new Locale(tempLocale);
252 }
253 return true;
254 #else
255 return false;
256 #endif
257 }
Copy(ResConfig & other)258 bool ResConfigImpl::Copy(ResConfig &other)
259 {
260 bool isSuccess = this->CopyLocale(other);
261 if (!isSuccess) {
262 return false;
263 }
264 if (this->GetDeviceType() != other.GetDeviceType()) {
265 this->SetDeviceType(other.GetDeviceType());
266 }
267 if (this->GetDirection() != other.GetDirection()) {
268 this->SetDirection(other.GetDirection());
269 }
270 if (this->GetColorMode() != other.GetColorMode()) {
271 this->SetColorMode(other.GetColorMode());
272 }
273 if (this->GetInputDevice() != other.GetInputDevice()) {
274 this->SetInputDevice(other.GetInputDevice());
275 }
276 if (this->GetMcc() != other.GetMcc()) {
277 this->SetMcc(other.GetMcc());
278 }
279 if (this->GetMnc() != other.GetMnc()) {
280 this->SetMnc(other.GetMnc());
281 }
282 if (this->GetScreenDensity() != other.GetScreenDensity()) {
283 this->SetScreenDensity(other.GetScreenDensity());
284 }
285 return true;
286 }
287
Match(const ResConfigImpl * other) const288 bool ResConfigImpl::Match(const ResConfigImpl *other) const
289 {
290 if (other == nullptr) {
291 return false;
292 }
293 if (!IsMccMncMatch(other->mcc_, other->mnc_)) {
294 return false;
295 }
296 if (!(LocaleMatcher::Match(this->resLocale_, other->GetResLocale()))) {
297 return false;
298 }
299 if (!IsDirectionMatch(other->direction_)) {
300 return false;
301 }
302 if (!IsDeviceTypeMatch(other->deviceType_)) {
303 return false;
304 }
305 if (!IsColorModeMatch(other->colorMode_)) {
306 return false;
307 }
308 if (!IsInputDeviceMatch(other->inputDevice_)) {
309 return false;
310 }
311 return true;
312 }
313
IsMccMncMatch(uint32_t mcc,uint32_t mnc) const314 bool ResConfigImpl::IsMccMncMatch(uint32_t mcc, uint32_t mnc) const
315 {
316 if (this->mcc_ != MCC_UNDEFINED && this->mnc_ != MNC_UNDEFINED) {
317 if (mcc != MCC_UNDEFINED && mnc != MNC_UNDEFINED) {
318 if (this->mcc_ != mcc || this->mnc_ != mnc) {
319 return false;
320 }
321 }
322 } else if (this->mcc_ != MCC_UNDEFINED && this->mnc_ == MNC_UNDEFINED) {
323 if (mcc != MCC_UNDEFINED && this->mcc_ != mcc) {
324 return false;
325 }
326 }
327 return true;
328 }
329
IsDirectionMatch(Direction direction) const330 bool ResConfigImpl::IsDirectionMatch(Direction direction) const
331 {
332 if (this->direction_ != DIRECTION_NOT_SET && direction != DIRECTION_NOT_SET) {
333 if (this->direction_ != direction) {
334 return false;
335 }
336 }
337 return true;
338 }
339
IsDeviceTypeMatch(DeviceType deviceType) const340 bool ResConfigImpl::IsDeviceTypeMatch(DeviceType deviceType) const
341 {
342 if (this->deviceType_ != DEVICE_NOT_SET && deviceType != DEVICE_NOT_SET) {
343 if (this->deviceType_ != deviceType) {
344 return false;
345 }
346 }
347 return true;
348 }
349
IsColorModeMatch(ColorMode colorMode) const350 bool ResConfigImpl::IsColorModeMatch(ColorMode colorMode) const
351 {
352 if (this->colorMode_ != COLOR_MODE_NOT_SET && colorMode != COLOR_MODE_NOT_SET) {
353 if (this->colorMode_ != colorMode) {
354 return false;
355 }
356 }
357 return true;
358 }
359
IsInputDeviceMatch(InputDevice inputDevice) const360 bool ResConfigImpl::IsInputDeviceMatch(InputDevice inputDevice) const
361 {
362 if (this->inputDevice_ == INPUTDEVICE_NOT_SET && inputDevice != INPUTDEVICE_NOT_SET) {
363 return false;
364 }
365 // reserve for future InputDevice expansion
366 if (this->inputDevice_ != INPUTDEVICE_NOT_SET && inputDevice != INPUTDEVICE_NOT_SET) {
367 if (this->inputDevice_ != inputDevice) {
368 return false;
369 }
370 }
371 return true;
372 }
373
374 /**
375 * compare this and target
376 * if this more match request,then return true
377 * else
378 * return false
379 *
380 */
IsMoreSuitable(const ResConfigImpl * other,const ResConfigImpl * request,uint32_t density) const381 bool ResConfigImpl::IsMoreSuitable(const ResConfigImpl *other,
382 const ResConfigImpl *request, uint32_t density) const
383 {
384 if (request != nullptr && other != nullptr) {
385 int ret = IsMccMncMoreSuitable(other->mcc_, other->mnc_, request->mcc_, request->mnc_);
386 if (ret != 0) {
387 return ret > 0;
388 }
389 int8_t result = LocaleMatcher::IsMoreSuitable(this->GetResLocale(), other->GetResLocale(),
390 request->GetResLocale());
391 if (result != 0) {
392 return result > 0;
393 }
394 /**
395 * direction must full match.
396 * when request is set direction and this is not equal other.
397 * this or other oriention is not set.
398 */
399 if (this->direction_ != other->direction_ &&
400 request->direction_ != Direction::DIRECTION_NOT_SET) {
401 return this->direction_ != Direction::DIRECTION_NOT_SET;
402 }
403 if (this->deviceType_ != other->deviceType_ &&
404 request->deviceType_ != DeviceType::DEVICE_NOT_SET) {
405 return this->deviceType_ != DeviceType::DEVICE_NOT_SET;
406 }
407 if (this->colorMode_ != other->colorMode_ &&
408 request->colorMode_ != ColorMode::COLOR_MODE_NOT_SET) {
409 return this->colorMode_ != ColorMode::COLOR_MODE_NOT_SET;
410 }
411 if (this->inputDevice_ != other->inputDevice_ &&
412 request->inputDevice_ != InputDevice::INPUTDEVICE_NOT_SET) {
413 return this->inputDevice_ != InputDevice::INPUTDEVICE_NOT_SET;
414 }
415 ret = IsDensityMoreSuitable(other->screenDensityDpi_, request->screenDensityDpi_, density);
416 if (ret != 0) {
417 return ret > 0;
418 }
419 }
420 return this->IsMoreSpecificThan(other, density);
421 }
422
423 /**
424 * compare this and target mcc/mnc
425 * if this more match other,then return 1, else if other more match this, return -1,
426 * else
427 * return 0
428 *
429 */
IsMccMncMoreSuitable(uint32_t otherMcc,uint32_t otherMnc,uint32_t requestMcc,uint32_t requestMnc) const430 int ResConfigImpl::IsMccMncMoreSuitable(uint32_t otherMcc, uint32_t otherMnc, uint32_t requestMcc,
431 uint32_t requestMnc) const
432 {
433 int ret = 0;
434 bool defined = requestMcc != MCC_UNDEFINED && requestMnc != MNC_UNDEFINED;
435 bool mccDefined = requestMcc != MCC_UNDEFINED && requestMnc == MNC_UNDEFINED;
436 bool isMccOrMncDiff = this->mcc_ != otherMcc || this->mnc_ != otherMnc;
437 bool isMccDiff = this->mcc_ != otherMcc;
438 if (defined && isMccOrMncDiff) {
439 if ((this->mcc_ != MCC_UNDEFINED) && (this->mnc_ != MNC_UNDEFINED)) {
440 // the mcc/mnc of this resConfig is suitable than other resConfig
441 ret = 1;
442 } else {
443 // the mcc/mnc of other resConfig mcc/mnc is suitable than this resConfig
444 ret = -1;
445 }
446 } else if (mccDefined && isMccDiff) {
447 if (this->mcc_ != MCC_UNDEFINED) {
448 // the mcc of this resConfig is suitable than other resConfig
449 ret = 1;
450 } else {
451 // the mcc of other resConfig is suitable than this resConfig
452 ret = -1;
453 }
454 }
455 return ret;
456 }
457
458 /**
459 * compare this and target density
460 * if this more match other,then return 1, else if other more match this, return -1,
461 * else
462 * return 0
463 *
464 */
IsDensityMoreSuitable(ScreenDensity otherDensity,ScreenDensity requestDensity,uint32_t density) const465 int ResConfigImpl::IsDensityMoreSuitable(ScreenDensity otherDensity, ScreenDensity requestDensity,
466 uint32_t density) const
467 {
468 int ret = 0;
469 int thisDistance;
470 int otherDistance;
471 if (density == ScreenDensity::SCREEN_DENSITY_NOT_SET) {
472 if (requestDensity != ScreenDensity::SCREEN_DENSITY_NOT_SET &&
473 this->screenDensityDpi_ != otherDensity) {
474 thisDistance = this->screenDensityDpi_ - requestDensity;
475 otherDistance = otherDensity - requestDensity;
476 if (IsDensityMoreSuitable(thisDistance, otherDistance)) {
477 // the density of this resConfig is suitable than other resConfig
478 ret = 1;
479 } else {
480 // the density of other resConfig is suitable than this resConfig
481 ret = -1;
482 }
483 }
484 } else {
485 if (this->screenDensityDpi_ != otherDensity) {
486 thisDistance = static_cast<int>(this->screenDensityDpi_ - density);
487 otherDistance = static_cast<int>(otherDensity - density);
488 if (IsDensityMoreSuitable(thisDistance, otherDistance)) {
489 // the density of this resConfig is suitable than other resConfig
490 ret = 1;
491 } else {
492 // the density of other resConfig is suitable than this resConfig
493 ret = -1;
494 }
495 }
496 }
497 return ret;
498 }
499
IsDensityMoreSuitable(int thisDistance,int otherDistance) const500 bool ResConfigImpl::IsDensityMoreSuitable(int thisDistance, int otherDistance) const
501 {
502 if (thisDistance >= 0 && otherDistance >= 0) {
503 return (thisDistance <= otherDistance);
504 }
505 if (thisDistance > 0) {
506 return true;
507 }
508 if (otherDistance > 0) {
509 return false;
510 }
511 return (thisDistance >= otherDistance);
512 }
513
~ResConfigImpl()514 ResConfigImpl::~ResConfigImpl()
515 {
516 if (resLocale_ != nullptr) {
517 delete resLocale_;
518 resLocale_ = nullptr;
519 }
520 #ifdef SUPPORT_GRAPHICS
521 if (localeInfo_ != nullptr) {
522 delete localeInfo_;
523 localeInfo_ = nullptr;
524 }
525 #endif
526 }
527
CompleteScript()528 void ResConfigImpl::CompleteScript()
529 {
530 if (isCompletedScript_) {
531 return;
532 }
533 if (LocaleMatcher::Normalize(this->resLocale_)) {
534 isCompletedScript_ = true;
535 }
536 }
537
IsCompletedScript() const538 bool ResConfigImpl::IsCompletedScript() const
539 {
540 return isCompletedScript_;
541 }
542
IsMoreSpecificThan(const ResConfigImpl * other,uint32_t density) const543 bool ResConfigImpl::IsMoreSpecificThan(const ResConfigImpl *other, uint32_t density) const
544 {
545 if (other == nullptr) {
546 return true;
547 }
548 if (this->mcc_ != MCC_UNDEFINED && this->mnc_ != MNC_UNDEFINED) {
549 if (this->mcc_ != other->mcc_ || this->mnc_ != other->mnc_) {
550 return false;
551 }
552 } else if (this->mcc_ != MCC_UNDEFINED && this->mnc_ == MNC_UNDEFINED) {
553 if (this->mcc_ != other->mcc_) {
554 return true;
555 }
556 }
557 int8_t result = LocaleMatcher::IsMoreSpecificThan(
558 this->GetResLocale(),
559 (other == nullptr) ? nullptr : other->GetResLocale());
560 if (result > 0) {
561 return true;
562 }
563 if (result < 0) {
564 return false;
565 }
566 if (this->direction_ != other->direction_) {
567 return (this->direction_ != Direction::DIRECTION_NOT_SET);
568 }
569 if (this->deviceType_ != other->deviceType_) {
570 return (this->deviceType_ != DeviceType::DEVICE_NOT_SET);
571 }
572 if (this->colorMode_ != other->colorMode_) {
573 return (this->colorMode_ != ColorMode::COLOR_MODE_NOT_SET);
574 }
575 if (this->inputDevice_ != other->inputDevice_) {
576 return (this->inputDevice_ == InputDevice::INPUTDEVICE_NOT_SET);
577 }
578 int ret = IsDensityMoreSpecificThan(other->screenDensityDpi_, density);
579 if (ret != 0) {
580 return ret > 0;
581 }
582
583 return true;
584 }
585
IsDensityMoreSpecificThan(ScreenDensity otherDensity,uint32_t density) const586 int ResConfigImpl::IsDensityMoreSpecificThan(ScreenDensity otherDensity, uint32_t density) const
587 {
588 int ret = 0;
589 if (density == SCREEN_DENSITY_NOT_SET) {
590 if (this->screenDensityDpi_ != otherDensity) {
591 if (this->screenDensityDpi_ != ScreenDensity::SCREEN_DENSITY_NOT_SET) {
592 // the density of this resConfig is suitable than other resConfig
593 ret = 1;
594 } else {
595 // the density of other resConfig is suitable than this resConfig
596 ret = -1;
597 }
598 }
599 } else {
600 if ((this->screenDensityDpi_ != ScreenDensity::SCREEN_DENSITY_NOT_SET) &&
601 (otherDensity == ScreenDensity::SCREEN_DENSITY_NOT_SET)) {
602 // the density of this resConfig is suitable than other resConfig
603 ret = 1;
604 }
605 if ((this->screenDensityDpi_ == ScreenDensity::SCREEN_DENSITY_NOT_SET) &&
606 (otherDensity != ScreenDensity::SCREEN_DENSITY_NOT_SET)) {
607 // the density of other resConfig is suitable than this resConfig
608 ret = -1;
609 }
610 if (this->screenDensityDpi_ != otherDensity) {
611 int thisDistance = static_cast<int>(this->screenDensityDpi_ - density);
612 int otherDistance = static_cast<int>(otherDensity - density);
613 if (IsDensityMoreSuitable(thisDistance, otherDistance)) {
614 // the density of this resConfig is suitable than other resConfig
615 ret = 1;
616 } else {
617 // the density of other resConfig is suitable than this resConfig
618 ret = -1;
619 }
620 }
621 }
622 return ret;
623 }
624
CreateResConfig()625 ResConfig *CreateResConfig()
626 {
627 ResConfigImpl *temp = new(std::nothrow) ResConfigImpl;
628 return temp;
629 }
630 } // namespace Resource
631 } // namespace Global
632 } // namespace OHOS