UNPKG

20.2 kBJavaScriptView Raw
1import { ActionItemBase, ActionBarBase, isVisible, flatProperty, traceMissingIcon, androidContentInsetLeftProperty, androidContentInsetRightProperty } from './action-bar-common';
2import { View } from '../core/view';
3import { Color } from '../../color';
4import { layout, RESOURCE_PREFIX, isFontIconURI } from '../../utils';
5import { colorProperty } from '../styling/style-properties';
6import { ImageSource } from '../../image-source';
7import { Application } from '../../application';
8import { isAccessibilityServiceEnabled, updateContentDescription } from '../../accessibility';
9import { SDK_VERSION } from '../../utils/constants';
10export * from './action-bar-common';
11const R_ID_HOME = 0x0102002c;
12const ACTION_ITEM_ID_OFFSET = 10000;
13const DEFAULT_ELEVATION = 4;
14let AppCompatTextView;
15let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET;
16function generateItemId() {
17 actionItemIdGenerator++;
18 return actionItemIdGenerator;
19}
20function loadActionIconDrawableOrResourceId(item) {
21 const itemIcon = item.icon;
22 const itemStyle = item.style;
23 let drawableOrId = null;
24 if (isFontIconURI(itemIcon)) {
25 const fontIconCode = itemIcon.split('//')[1];
26 const font = itemStyle.fontInternal;
27 const color = itemStyle.color;
28 const is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color);
29 if (is && is.android) {
30 drawableOrId = new android.graphics.drawable.BitmapDrawable(appResources, is.android);
31 }
32 }
33 else {
34 drawableOrId = getDrawableOrResourceId(itemIcon, appResources);
35 }
36 if (!drawableOrId) {
37 traceMissingIcon(itemIcon);
38 }
39 return drawableOrId;
40}
41let appResources;
42let MenuItemClickListener;
43function initializeMenuItemClickListener() {
44 if (MenuItemClickListener) {
45 return;
46 }
47 AppCompatTextView = androidx.appcompat.widget.AppCompatTextView;
48 var MenuItemClickListenerImpl = /** @class */ (function (_super) {
49 __extends(MenuItemClickListenerImpl, _super);
50 function MenuItemClickListenerImpl(owner) {
51 var _this = _super.call(this) || this;
52 _this.owner = owner;
53 return global.__native(_this);
54 }
55 MenuItemClickListenerImpl.prototype.onMenuItemClick = function (item) {
56 var itemId = item.getItemId();
57 return this.owner._onAndroidItemSelected(itemId);
58 };
59 var _a;
60 MenuItemClickListenerImpl = __decorate([
61 Interfaces([androidx.appcompat.widget.Toolbar.OnMenuItemClickListener]),
62 __metadata("design:paramtypes", [typeof (_a = typeof ActionBar !== "undefined" && ActionBar) === "function" ? _a : Object])
63 ], MenuItemClickListenerImpl);
64 return MenuItemClickListenerImpl;
65}(java.lang.Object));
66 MenuItemClickListener = MenuItemClickListenerImpl;
67 appResources = Application.android.context.getResources();
68}
69export class ActionItem extends ActionItemBase {
70 constructor() {
71 super();
72 this._androidPosition = {
73 position: 'actionBar',
74 systemIcon: undefined,
75 };
76 this._itemId = generateItemId();
77 }
78 // @ts-ignore
79 get android() {
80 return this._androidPosition;
81 }
82 set android(value) {
83 throw new Error('ActionItem.android is read-only');
84 }
85 _getItemId() {
86 return this._itemId;
87 }
88}
89export class AndroidActionBarSettings {
90 constructor(actionBar) {
91 this._iconVisibility = 'auto';
92 this._actionBar = actionBar;
93 }
94 get icon() {
95 return this._icon;
96 }
97 set icon(value) {
98 if (value !== this._icon) {
99 this._icon = value;
100 this._actionBar._onIconPropertyChanged();
101 }
102 }
103 get iconVisibility() {
104 return this._iconVisibility;
105 }
106 set iconVisibility(value) {
107 if (value !== this._iconVisibility) {
108 this._iconVisibility = value;
109 this._actionBar._onIconPropertyChanged();
110 }
111 }
112}
113export class NavigationButton extends ActionItem {
114}
115export class ActionBar extends ActionBarBase {
116 constructor() {
117 super();
118 this._android = new AndroidActionBarSettings(this);
119 }
120 get android() {
121 return this._android;
122 }
123 _addChildFromBuilder(name, value) {
124 if (value instanceof NavigationButton) {
125 this.navigationButton = value;
126 }
127 else if (value instanceof ActionItem) {
128 this.actionItems?.addItem(value);
129 }
130 else if (value instanceof View) {
131 this.titleView = value;
132 }
133 }
134 createNativeView() {
135 return new androidx.appcompat.widget.Toolbar(this._context);
136 }
137 initNativeView() {
138 super.initNativeView();
139 const nativeView = this.nativeViewProtected;
140 initializeMenuItemClickListener();
141 const menuItemClickListener = new MenuItemClickListener(this);
142 nativeView.setOnMenuItemClickListener(menuItemClickListener);
143 nativeView.menuItemClickListener = menuItemClickListener;
144 }
145 disposeNativeView() {
146 if (this.nativeViewProtected?.menuItemClickListener) {
147 this.nativeViewProtected.menuItemClickListener.owner = null;
148 }
149 super.disposeNativeView();
150 }
151 onLoaded() {
152 super.onLoaded();
153 this.update();
154 }
155 update() {
156 if (!this.nativeViewProtected) {
157 return;
158 }
159 const page = this.page;
160 if (!page.frame || !page.frame._getNavBarVisible(page)) {
161 this.nativeViewProtected.setVisibility(android.view.View.GONE);
162 // If action bar is hidden - no need to fill it with items.
163 return;
164 }
165 this.nativeViewProtected.setVisibility(android.view.View.VISIBLE);
166 // Add menu items
167 this._addActionItems();
168 // Set title
169 this._updateTitleAndTitleView();
170 // Set home icon
171 this._updateIcon();
172 // Set navigation button
173 this._updateNavigationButton();
174 }
175 _applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable) {
176 const nativeView = this.nativeViewProtected;
177 if (backgroundDrawable && onlyColor && SDK_VERSION >= 21) {
178 if (isBorderDrawable && nativeView._cachedDrawable) {
179 backgroundDrawable = nativeView._cachedDrawable;
180 // we need to duplicate the drawable or we lose the "default" cached drawable
181 const constantState = backgroundDrawable.getConstantState();
182 if (constantState) {
183 try {
184 backgroundDrawable = constantState.newDrawable(nativeView.getResources());
185 // eslint-disable-next-line no-empty
186 }
187 catch { }
188 }
189 nativeView.setBackground(backgroundDrawable);
190 }
191 const backgroundColor = (backgroundDrawable.backgroundColor = background.color.android);
192 backgroundDrawable.mutate();
193 backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
194 backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
195 backgroundDrawable.backgroundColor = backgroundColor;
196 }
197 else {
198 super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable);
199 }
200 }
201 _onAndroidItemSelected(itemId) {
202 // Handle home button
203 if (this.navigationButton && itemId === R_ID_HOME) {
204 this.navigationButton._raiseTap();
205 return true;
206 }
207 // Find item with the right ID;
208 let menuItem = undefined;
209 const items = this.actionItems?.getItems() ?? [];
210 for (let i = 0; i < items.length; i++) {
211 if (items[i]._getItemId() === itemId) {
212 menuItem = items[i];
213 break;
214 }
215 }
216 if (menuItem) {
217 menuItem._raiseTap();
218 return true;
219 }
220 return false;
221 }
222 _updateNavigationButton() {
223 const navButton = this.navigationButton;
224 if (navButton && isVisible(navButton)) {
225 const systemIcon = navButton.android.systemIcon;
226 if (systemIcon !== undefined) {
227 // Try to look in the system resources.
228 const systemResourceId = getSystemResourceId(systemIcon);
229 if (systemResourceId) {
230 this.nativeViewProtected.setNavigationIcon(systemResourceId);
231 }
232 }
233 else if (navButton.icon) {
234 const drawableOrId = loadActionIconDrawableOrResourceId(navButton);
235 if (drawableOrId) {
236 this.nativeViewProtected.setNavigationIcon(drawableOrId);
237 }
238 }
239 // Set navigation content description, used by screen readers for the vision-impaired users
240 this.nativeViewProtected.setNavigationContentDescription(navButton.text || null);
241 const navBtn = new WeakRef(navButton);
242 this.nativeViewProtected.setNavigationOnClickListener(new android.view.View.OnClickListener({
243 onClick: function (v) {
244 const owner = navBtn?.get();
245 if (owner) {
246 owner._raiseTap();
247 }
248 },
249 }));
250 }
251 else {
252 this.nativeViewProtected.setNavigationIcon(null);
253 }
254 }
255 _updateIcon() {
256 const visibility = getIconVisibility(this.android.iconVisibility);
257 if (visibility) {
258 const icon = this.android.icon;
259 if (icon !== undefined) {
260 const drawableOrId = getDrawableOrResourceId(icon, appResources);
261 if (drawableOrId) {
262 this.nativeViewProtected.setLogo(drawableOrId);
263 }
264 else {
265 traceMissingIcon(icon);
266 }
267 }
268 else {
269 const defaultIcon = Application.android.nativeApp.getApplicationInfo().icon;
270 this.nativeViewProtected.setLogo(defaultIcon);
271 }
272 }
273 else {
274 this.nativeViewProtected.setLogo(null);
275 }
276 }
277 _updateTitleAndTitleView() {
278 if (!this.titleView) {
279 // No title view - show the title
280 const title = this.title;
281 if (title !== undefined) {
282 this.nativeViewProtected.setTitle(title);
283 }
284 else {
285 const appContext = Application.android.context;
286 const appInfo = appContext.getApplicationInfo();
287 const appLabel = appContext.getPackageManager().getApplicationLabel(appInfo);
288 if (appLabel) {
289 this.nativeViewProtected.setTitle(appLabel);
290 }
291 }
292 }
293 // Update content description for the screen reader.
294 updateContentDescription(this, true);
295 }
296 _addActionItems() {
297 const menu = this.nativeViewProtected.getMenu();
298 const items = this.actionItems?.getVisibleItems() ?? [];
299 menu.clear();
300 for (let i = 0; i < items.length; i++) {
301 const item = items[i];
302 const menuItem = menu.add(android.view.Menu.NONE, item._getItemId(), android.view.Menu.NONE, item.text + '');
303 if (item.actionView && item.actionView.android) {
304 // With custom action view, the menuitem cannot be displayed in a popup menu.
305 item.android.position = 'actionBar';
306 menuItem.setActionView(item.actionView.android);
307 ActionBar._setOnClickListener(item);
308 }
309 else if (item.android.systemIcon) {
310 // Try to look in the system resources.
311 const systemResourceId = getSystemResourceId(item.android.systemIcon);
312 if (systemResourceId) {
313 menuItem.setIcon(systemResourceId);
314 }
315 }
316 else if (item.icon) {
317 const drawableOrId = loadActionIconDrawableOrResourceId(item);
318 if (drawableOrId) {
319 menuItem.setIcon(drawableOrId);
320 }
321 }
322 const showAsAction = getShowAsAction(item);
323 menuItem.setShowAsAction(showAsAction);
324 }
325 }
326 static _setOnClickListener(item) {
327 const weakRef = new WeakRef(item);
328 item.actionView.android.setOnClickListener(new android.view.View.OnClickListener({
329 onClick: function (v) {
330 const owner = weakRef?.get();
331 if (owner) {
332 owner._raiseTap();
333 }
334 },
335 }));
336 }
337 _onTitlePropertyChanged() {
338 if (this.nativeViewProtected) {
339 this._updateTitleAndTitleView();
340 }
341 }
342 _onIconPropertyChanged() {
343 if (this.nativeViewProtected) {
344 this._updateIcon();
345 }
346 }
347 _addViewToNativeVisualTree(child, atIndex = Number.MAX_VALUE) {
348 super._addViewToNativeVisualTree(child);
349 if (this.nativeViewProtected && child.nativeViewProtected) {
350 if (atIndex >= this.nativeViewProtected.getChildCount()) {
351 this.nativeViewProtected.addView(child.nativeViewProtected);
352 }
353 else {
354 this.nativeViewProtected.addView(child.nativeViewProtected, atIndex);
355 }
356 return true;
357 }
358 return false;
359 }
360 _removeViewFromNativeVisualTree(child) {
361 super._removeViewFromNativeVisualTree(child);
362 if (this.nativeViewProtected && child.nativeViewProtected) {
363 this.nativeViewProtected.removeView(child.nativeViewProtected);
364 }
365 }
366 [colorProperty.getDefault]() {
367 const nativeView = this.nativeViewProtected;
368 if (!defaultTitleTextColor) {
369 let tv = getAppCompatTextView(nativeView);
370 if (!tv) {
371 const title = nativeView.getTitle();
372 // setTitle will create AppCompatTextView internally;
373 nativeView.setTitle('');
374 tv = getAppCompatTextView(nativeView);
375 if (title) {
376 // restore title.
377 nativeView.setTitle(title);
378 }
379 }
380 // Fallback to hardcoded falue if we don't find TextView instance...
381 // using new TextView().getTextColors().getDefaultColor() returns different value: -1979711488
382 defaultTitleTextColor = tv ? tv.getTextColors().getDefaultColor() : -570425344;
383 }
384 return defaultTitleTextColor;
385 }
386 [colorProperty.setNative](value) {
387 const color = value instanceof Color ? value.android : value;
388 this.nativeViewProtected.setTitleTextColor(color);
389 }
390 [flatProperty.setNative](value) {
391 const compat = androidx.core.view.ViewCompat;
392 if (compat.setElevation) {
393 if (value) {
394 compat.setElevation(this.nativeViewProtected, 0);
395 }
396 else {
397 const val = DEFAULT_ELEVATION * layout.getDisplayDensity();
398 compat.setElevation(this.nativeViewProtected, val);
399 }
400 }
401 }
402 [androidContentInsetLeftProperty.setNative]() {
403 if (SDK_VERSION >= 21) {
404 this.nativeViewProtected.setContentInsetsAbsolute(this.effectiveContentInsetLeft, this.effectiveContentInsetRight);
405 }
406 }
407 [androidContentInsetRightProperty.setNative]() {
408 if (SDK_VERSION >= 21) {
409 this.nativeViewProtected.setContentInsetsAbsolute(this.effectiveContentInsetLeft, this.effectiveContentInsetRight);
410 }
411 }
412 accessibilityScreenChanged() {
413 if (!isAccessibilityServiceEnabled()) {
414 return;
415 }
416 const nativeView = this.nativeViewProtected;
417 if (!nativeView) {
418 return;
419 }
420 const originalFocusableState = SDK_VERSION >= 26 && nativeView.getFocusable();
421 const originalImportantForAccessibility = nativeView.getImportantForAccessibility();
422 const originalIsAccessibilityHeading = SDK_VERSION >= 28 && nativeView.isAccessibilityHeading();
423 try {
424 nativeView.setFocusable(false);
425 nativeView.setImportantForAccessibility(android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO);
426 let announceView = null;
427 const numChildren = nativeView.getChildCount();
428 for (let i = 0; i < numChildren; i += 1) {
429 const childView = nativeView.getChildAt(i);
430 if (!childView) {
431 continue;
432 }
433 childView.setFocusable(true);
434 if (childView instanceof androidx.appcompat.widget.AppCompatTextView) {
435 announceView = childView;
436 if (SDK_VERSION >= 28) {
437 announceView.setAccessibilityHeading(true);
438 }
439 }
440 }
441 if (!announceView) {
442 announceView = nativeView;
443 }
444 announceView.setFocusable(true);
445 announceView.setImportantForAccessibility(android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES);
446 announceView.sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED);
447 announceView.sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
448 }
449 catch {
450 // ignore
451 }
452 finally {
453 setTimeout(() => {
454 // Reset status after the focus have been reset.
455 const localNativeView = this.nativeViewProtected;
456 if (!localNativeView) {
457 return;
458 }
459 if (SDK_VERSION >= 28) {
460 nativeView.setAccessibilityHeading(originalIsAccessibilityHeading);
461 }
462 if (SDK_VERSION >= 26) {
463 localNativeView.setFocusable(originalFocusableState);
464 }
465 localNativeView.setImportantForAccessibility(originalImportantForAccessibility);
466 });
467 }
468 }
469}
470function getAppCompatTextView(toolbar) {
471 for (let i = 0, count = toolbar.getChildCount(); i < count; i++) {
472 const child = toolbar.getChildAt(i);
473 if (child instanceof AppCompatTextView) {
474 return child;
475 }
476 }
477 return null;
478}
479ActionBar.prototype.recycleNativeView = 'auto';
480let defaultTitleTextColor;
481function getDrawableOrResourceId(icon, resources) {
482 if (typeof icon !== 'string') {
483 return null;
484 }
485 let result = null;
486 if (icon.indexOf(RESOURCE_PREFIX) === 0) {
487 const resourceId = resources.getIdentifier(icon.substr(RESOURCE_PREFIX.length), 'drawable', Application.android.packageName);
488 if (resourceId > 0) {
489 result = resourceId;
490 }
491 }
492 else {
493 let drawable;
494 const is = ImageSource.fromFileOrResourceSync(icon);
495 if (is) {
496 drawable = new android.graphics.drawable.BitmapDrawable(appResources, is.android);
497 }
498 result = drawable;
499 }
500 return result;
501}
502function getShowAsAction(menuItem) {
503 switch (menuItem.android.position) {
504 case 'actionBarIfRoom':
505 return android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM;
506 case 'popup':
507 return android.view.MenuItem.SHOW_AS_ACTION_NEVER;
508 case 'actionBar':
509 default:
510 return android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
511 }
512}
513function getIconVisibility(iconVisibility) {
514 switch (iconVisibility) {
515 case 'always':
516 return true;
517 case 'auto':
518 case 'never':
519 default:
520 return false;
521 }
522}
523function getSystemResourceId(systemIcon) {
524 return android.content.res.Resources.getSystem().getIdentifier(systemIcon, 'drawable', 'android');
525}
526//# sourceMappingURL=index.android.js.map
\No newline at end of file