/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.litho;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.facebook.litho.displaylist.DisplayList;
import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO;
/**
* Represents a mounted UI element in a {@link MountState}. It holds a
* key and a content instance which might be any type of UI element
* supported by the framework e.g. {@link Drawable}.
*/
class MountItem {
static final int FLAG_DUPLICATE_PARENT_STATE = 1 << 0;
static final int FLAG_VIEW_CLICKABLE = 1 << 1;
static final int FLAG_VIEW_LONG_CLICKABLE = 1 << 2;
static final int FLAG_VIEW_FOCUSABLE = 1 << 3;
private NodeInfo mNodeInfo;
private ViewNodeInfo mViewNodeInfo;
private Component<?> mComponent;
private Object mContent;
private ComponentHost mHost;
private boolean mIsBound;
private int mImportantForAccessibility;
private DisplayListDrawable mDisplayListDrawable;
// ComponentHost flags defined in the LayoutOutput specifying
// the behaviour of this item when mounted.
private int mFlags;
void init(
Component<?> component,
MountItem mountItem,
LayoutOutput layoutOutput) {
init(
component,
mountItem.getHost(),
mountItem.getContent(),
layoutOutput,
mountItem.getDisplayListDrawable());
}
void init(
Component<?> component,
ComponentHost host,
Object content,
LayoutOutput layoutOutput,
DisplayListDrawable displayListDrawable) {
init(
component,
host,
content,
layoutOutput.getNodeInfo(),
layoutOutput.getViewNodeInfo(),
acquireDisplayListDrawableIfNeeded(
content,
layoutOutput.getDisplayList(),
displayListDrawable),
layoutOutput.getFlags(),
layoutOutput.getImportantForAccessibility());
}
void init(
Component<?> component,
ComponentHost host,
Object content,
NodeInfo nodeInfo,
ViewNodeInfo viewNodeInfo,
DisplayListDrawable displayListDrawable,
int flags,
int importantForAccessibility) {
mComponent = component;
mContent = content;
mHost = host;
mFlags = flags;
mImportantForAccessibility = importantForAccessibility;
mDisplayListDrawable = displayListDrawable;
if (mNodeInfo != null) {
mNodeInfo.release();
mNodeInfo = null;
}
if (nodeInfo != null) {
mNodeInfo = nodeInfo.acquireRef();
}
if (mViewNodeInfo != null) {
mViewNodeInfo.release();
mViewNodeInfo = null;
}
if (viewNodeInfo != null) {
mViewNodeInfo = viewNodeInfo.acquireRef();
}
if (mContent instanceof View) {
final View view = (View) mContent;
if (view.isClickable()) {
mFlags |= FLAG_VIEW_CLICKABLE;
}
if (view.isLongClickable()) {
mFlags |= FLAG_VIEW_LONG_CLICKABLE;
}
if (view.isFocusable()) {
mFlags |= FLAG_VIEW_FOCUSABLE;
}
}
}
private DisplayListDrawable acquireDisplayListDrawableIfNeeded(
Object content,
DisplayList displayList,
DisplayListDrawable convertDisplayListDrawable) {
if (displayList != null) {
if (convertDisplayListDrawable != null) {
convertDisplayListDrawable.setWrappedDrawable((Drawable) content, displayList);
} else {
convertDisplayListDrawable = ComponentsPools.acquireDisplayListDrawable(
(Drawable) content, displayList);
}
convertDisplayListDrawable.suppressInvalidations(true);
} else if (convertDisplayListDrawable != null) {
convertDisplayListDrawable.setWrappedDrawable
((Drawable) content, null);
}
return convertDisplayListDrawable;
}
Component<?> getComponent() {
return mComponent;
}
ComponentHost getHost() {
return mHost;
}
Object getContent() {
return mContent;
}
int getFlags() {
return mFlags;
}
int getImportantForAccessibility() {
return mImportantForAccessibility;
}
NodeInfo getNodeInfo() {
return mNodeInfo;
}
ViewNodeInfo getViewNodeInfo() {
return mViewNodeInfo;
}
boolean isAccessible() {
if (mComponent == null) {
return false;
}
if (mImportantForAccessibility == IMPORTANT_FOR_ACCESSIBILITY_NO) {
return false;
}
return (mNodeInfo != null && mNodeInfo.hasAccessibilityHandlers())
|| mComponent.getLifecycle().implementsAccessibility();
}
void release(Context context) {
// Component hosts are recycled within other hosts instead of the global pool.
if (!(mContent instanceof ComponentHost)) {
ComponentsPools.release(context, mComponent.getLifecycle(), mContent);
}
if (mDisplayListDrawable != null) {
ComponentsPools.release(mDisplayListDrawable);
mDisplayListDrawable = null;
}
if (mNodeInfo != null) {
mNodeInfo.release();
mNodeInfo = null;
}
if (mViewNodeInfo != null) {
mViewNodeInfo.release();
mViewNodeInfo = null;
}
mComponent = null;
mHost = null;
mContent = null;
mFlags = 0;
mIsBound = false;
mImportantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
}
static boolean isDuplicateParentState(int flags) {
return (flags & FLAG_DUPLICATE_PARENT_STATE) == FLAG_DUPLICATE_PARENT_STATE;
}
/**
* @return Whether the view associated with this MountItem is clickable.
*/
static boolean isViewClickable(int flags) {
return (flags & FLAG_VIEW_CLICKABLE) == FLAG_VIEW_CLICKABLE;
}
/**
* @return Whether the view associated with this MountItem is long clickable.
*/
static boolean isViewLongClickable(int flags) {
return (flags & FLAG_VIEW_LONG_CLICKABLE) == FLAG_VIEW_LONG_CLICKABLE;
}
/**
* @return Whether the view associated with this MountItem is setFocusable.
*/
static boolean isViewFocusable(int flags) {
return (flags & FLAG_VIEW_FOCUSABLE) == FLAG_VIEW_FOCUSABLE;
}
/**
* @return Whether this MountItem is currently bound. A bound mount item is a Mount item that has
* been mounted and is currently active on screen.
*/
boolean isBound() {
return mIsBound;
}
/**
* Sets whether this MountItem is currently bound.
*/
void setIsBound(boolean bound) {
mIsBound = bound;
}
DisplayListDrawable getDisplayListDrawable() {
return mDisplayListDrawable;
}
}