/**
* 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 java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.atomic.AtomicInteger;
import android.support.annotation.IntDef;
import android.util.SparseArray;
import com.facebook.infer.annotation.ThreadConfined;
/**
* NodeInfo holds information that are set to the {@link InternalNode} and needs to be used
* while mounting a {@link MountItem} in {@link MountState}.
*/
@ThreadConfined(ThreadConfined.ANY)
class NodeInfo {
static final short FOCUS_UNSET = 0;
static final short FOCUS_SET_TRUE = 1;
static final short FOCUS_SET_FALSE = 2;
@IntDef({FOCUS_UNSET, FOCUS_SET_TRUE, FOCUS_SET_FALSE})
@Retention(RetentionPolicy.SOURCE)
@interface FocusState {}
// When this flag is set, contentDescription was explicitly set on this node.
private static final int PFLAG_CONTENT_DESCRIPTION_IS_SET = 1 << 0;
// When this flag is set, viewTag was explicitly set on this node.
private static final int PFLAG_VIEW_TAG_IS_SET = 1 << 1;
// When this flag is set, viewTags was explicitly set on this node.
private static final int PFLAG_VIEW_TAGS_IS_SET = 1 << 2;
// When this flag is set, clickHandler was explicitly set on this node.
private static final int PFLAG_CLICK_HANDLER_IS_SET = 1 << 3;
// When this flag is set, longClickHandler was explicitly set on this node.
private static final int PFLAG_LONG_CLICK_HANDLER_IS_SET = 1 << 4;
// When this flag is set, touchHandler was explicitly set on this node.
private static final int PFLAG_TOUCH_HANDLER_IS_SET = 1 << 5;
// When this flag is set, dispatchPopulateAccessibilityEventHandler
// was explicitly set on this node.
private static final int PFLAG_DISPATCH_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET = 1 << 6;
// When this flag is set, onInitializeAccessibilityEventHandler was explicitly set on this node.
private static final int PFLAG_ON_INITIALIZE_ACCESSIBILITY_EVENT_HANDLER_IS_SET = 1 << 7;
// When this flag is set, onInitializeAccessibilityNodeInfo was explicitly set on this node.
private static final int PFLAG_ON_INITIALIZE_ACCESSIBILITY_NODE_INFO_HANDLER_IS_SET = 1 << 8;
// When this flag is set, onPopulateAccessibilityEventHandler was explicitly set on this node
private static final int PFLAG_ON_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET = 1 << 9;
// When this flag is set, onRequestSendAccessibilityEventHandler was explicitly set on this node.
private static final int PFLAG_ON_REQUEST_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET = 1 << 10;
// When this flag is set, performAccessibilityActionHandler was explicitly set on this node.
private static final int PFLAG_PERFORM_ACCESSIBILITY_ACTION_HANDLER_IS_SET = 1 << 11;
// When this flag is set, sendAccessibilityEventHandler was explicitly set on this node.
private static final int PFLAG_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET = 1 << 12;
// When this flag is set, sendAccessibilityEventUncheckedHandler was explicitly set on this node.
private static final int PFLAG_SEND_ACCESSIBILITY_EVENT_UNCHECKED_HANDLER_IS_SET = 1 << 13;
private final AtomicInteger mReferenceCount = new AtomicInteger(0);
private CharSequence mContentDescription;
private Object mViewTag;
private SparseArray<Object> mViewTags;
private EventHandler<ClickEvent> mClickHandler;
private EventHandler<LongClickEvent> mLongClickHandler;
private EventHandler<TouchEvent> mTouchHandler;
private EventHandler<InterceptTouchEvent> mInterceptTouchHandler;
private EventHandler<DispatchPopulateAccessibilityEventEvent>
mDispatchPopulateAccessibilityEventHandler;
private EventHandler<OnInitializeAccessibilityEventEvent>
mOnInitializeAccessibilityEventHandler;
private EventHandler<OnPopulateAccessibilityEventEvent> mOnPopulateAccessibilityEventHandler;
private EventHandler<OnInitializeAccessibilityNodeInfoEvent>
mOnInitializeAccessibilityNodeInfoHandler;
private EventHandler<OnRequestSendAccessibilityEventEvent>
mOnRequestSendAccessibilityEventHandler;
private EventHandler<PerformAccessibilityActionEvent> mPerformAccessibilityActionHandler;
private EventHandler<SendAccessibilityEventEvent> mSendAccessibilityEventHandler;
private EventHandler<SendAccessibilityEventUncheckedEvent>
mSendAccessibilityEventUncheckedHandler;
private @NodeInfo.FocusState short mFocusState = FOCUS_UNSET;
private int mPrivateFlags;
void setContentDescription(CharSequence contentDescription) {
mPrivateFlags |= PFLAG_CONTENT_DESCRIPTION_IS_SET;
mContentDescription = contentDescription;
}
CharSequence getContentDescription() {
return mContentDescription;
}
void setViewTag(Object viewTag) {
mPrivateFlags |= PFLAG_VIEW_TAG_IS_SET;
mViewTag = viewTag;
}
Object getViewTag() {
return mViewTag;
}
void setViewTags(SparseArray<Object> viewTags) {
mPrivateFlags |= PFLAG_VIEW_TAGS_IS_SET;
mViewTags = viewTags;
}
SparseArray<Object> getViewTags() {
return mViewTags;
}
void setClickHandler(EventHandler<ClickEvent> clickHandler) {
mPrivateFlags |= PFLAG_CLICK_HANDLER_IS_SET;
mClickHandler = clickHandler;
}
EventHandler<ClickEvent> getClickHandler() {
return mClickHandler;
}
boolean isClickable() {
return (mClickHandler != null);
}
void setLongClickHandler(EventHandler<LongClickEvent> longClickHandler) {
mPrivateFlags |= PFLAG_LONG_CLICK_HANDLER_IS_SET;
mLongClickHandler = longClickHandler;
}
EventHandler<LongClickEvent> getLongClickHandler() {
mPrivateFlags |= PFLAG_TOUCH_HANDLER_IS_SET;
return mLongClickHandler;
}
boolean isLongClickable() {
return (mLongClickHandler != null);
}
void setTouchHandler(EventHandler<TouchEvent> touchHandler) {
mTouchHandler = touchHandler;
}
EventHandler<TouchEvent> getTouchHandler() {
return mTouchHandler;
}
void setInterceptTouchHandler(EventHandler interceptTouchHandler) {
mInterceptTouchHandler = interceptTouchHandler;
}
EventHandler<InterceptTouchEvent> getInterceptTouchHandler() {
return mInterceptTouchHandler;
}
boolean isTouchable() {
return (mTouchHandler != null);
}
boolean hasTouchEventHandlers() {
return mClickHandler != null
|| mLongClickHandler != null
|| mTouchHandler != null;
}
void setDispatchPopulateAccessibilityEventHandler(
EventHandler<DispatchPopulateAccessibilityEventEvent>
dispatchPopulateAccessibilityEventHandler) {
mPrivateFlags |= PFLAG_DISPATCH_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET;
mDispatchPopulateAccessibilityEventHandler = dispatchPopulateAccessibilityEventHandler;
}
EventHandler<DispatchPopulateAccessibilityEventEvent>
getDispatchPopulateAccessibilityEventHandler() {
return mDispatchPopulateAccessibilityEventHandler;
}
void setOnInitializeAccessibilityEventHandler(
EventHandler<OnInitializeAccessibilityEventEvent> onInitializeAccessibilityEventHandler) {
mPrivateFlags |= PFLAG_ON_INITIALIZE_ACCESSIBILITY_EVENT_HANDLER_IS_SET;
mOnInitializeAccessibilityEventHandler = onInitializeAccessibilityEventHandler;
}
EventHandler<OnInitializeAccessibilityEventEvent>
getOnInitializeAccessibilityEventHandler() {
return mOnInitializeAccessibilityEventHandler;
}
void setOnInitializeAccessibilityNodeInfoHandler(
EventHandler<OnInitializeAccessibilityNodeInfoEvent>
onInitializeAccessibilityNodeInfoHandler) {
mPrivateFlags |= PFLAG_ON_INITIALIZE_ACCESSIBILITY_NODE_INFO_HANDLER_IS_SET;
mOnInitializeAccessibilityNodeInfoHandler = onInitializeAccessibilityNodeInfoHandler;
}
EventHandler<OnInitializeAccessibilityNodeInfoEvent>
getOnInitializeAccessibilityNodeInfoHandler() {
return mOnInitializeAccessibilityNodeInfoHandler;
}
void setOnPopulateAccessibilityEventHandler(
EventHandler<OnPopulateAccessibilityEventEvent> onPopulateAccessibilityEventHandler) {
mPrivateFlags |= PFLAG_ON_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET;
mOnPopulateAccessibilityEventHandler = onPopulateAccessibilityEventHandler;
}
EventHandler<OnPopulateAccessibilityEventEvent> getOnPopulateAccessibilityEventHandler() {
return mOnPopulateAccessibilityEventHandler;
}
void setOnRequestSendAccessibilityEventHandler(
EventHandler<OnRequestSendAccessibilityEventEvent> onRequestSendAccessibilityEventHandler) {
mPrivateFlags |= PFLAG_ON_REQUEST_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET;
mOnRequestSendAccessibilityEventHandler = onRequestSendAccessibilityEventHandler;
}
EventHandler<OnRequestSendAccessibilityEventEvent>
getOnRequestSendAccessibilityEventHandler() {
return mOnRequestSendAccessibilityEventHandler;
}
void setPerformAccessibilityActionHandler(
EventHandler<PerformAccessibilityActionEvent> performAccessibilityActionHandler) {
mPrivateFlags |= PFLAG_PERFORM_ACCESSIBILITY_ACTION_HANDLER_IS_SET;
mPerformAccessibilityActionHandler = performAccessibilityActionHandler;
}
EventHandler<PerformAccessibilityActionEvent> getPerformAccessibilityActionHandler() {
return mPerformAccessibilityActionHandler;
}
void setSendAccessibilityEventHandler(
EventHandler<SendAccessibilityEventEvent> sendAccessibilityEventHandler) {
mPrivateFlags |= PFLAG_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET;
mSendAccessibilityEventHandler = sendAccessibilityEventHandler;
}
EventHandler<SendAccessibilityEventEvent> getSendAccessibilityEventHandler() {
return mSendAccessibilityEventHandler;
}
void setSendAccessibilityEventUncheckedHandler(
EventHandler<SendAccessibilityEventUncheckedEvent> sendAccessibilityEventUncheckedHandler) {
mPrivateFlags |= PFLAG_SEND_ACCESSIBILITY_EVENT_UNCHECKED_HANDLER_IS_SET;
mSendAccessibilityEventUncheckedHandler = sendAccessibilityEventUncheckedHandler;
}
EventHandler<SendAccessibilityEventUncheckedEvent> getSendAccessibilityEventUncheckedHandler() {
return mSendAccessibilityEventUncheckedHandler;
}
boolean hasAccessibilityHandlers() {
return mOnInitializeAccessibilityEventHandler != null
|| mOnInitializeAccessibilityNodeInfoHandler != null
|| mOnPopulateAccessibilityEventHandler != null
|| mOnRequestSendAccessibilityEventHandler != null
|| mPerformAccessibilityActionHandler != null
|| mDispatchPopulateAccessibilityEventHandler != null
|| mSendAccessibilityEventHandler != null
|| mSendAccessibilityEventUncheckedHandler != null;
}
void setFocusable(boolean isFocusable) {
if (isFocusable) {
mFocusState = FOCUS_SET_TRUE;
} else {
mFocusState = FOCUS_SET_FALSE;
}
}
@NodeInfo.FocusState
short getFocusState() {
return mFocusState;
}
void updateWith(NodeInfo newInfo) {
if ((newInfo.mPrivateFlags & PFLAG_CLICK_HANDLER_IS_SET) != 0) {
mClickHandler = newInfo.mClickHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_LONG_CLICK_HANDLER_IS_SET) != 0) {
mLongClickHandler = newInfo.mLongClickHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_TOUCH_HANDLER_IS_SET) != 0) {
mTouchHandler = newInfo.mTouchHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_DISPATCH_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET) != 0) {
mDispatchPopulateAccessibilityEventHandler =
newInfo.mDispatchPopulateAccessibilityEventHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_ON_INITIALIZE_ACCESSIBILITY_EVENT_HANDLER_IS_SET) != 0) {
mOnInitializeAccessibilityEventHandler = newInfo.mOnInitializeAccessibilityEventHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_ON_INITIALIZE_ACCESSIBILITY_NODE_INFO_HANDLER_IS_SET) != 0) {
mOnInitializeAccessibilityNodeInfoHandler = newInfo.mOnInitializeAccessibilityNodeInfoHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_ON_POPULATE_ACCESSIBILITY_EVENT_HANDLER_IS_SET) != 0) {
mOnPopulateAccessibilityEventHandler = newInfo.mOnPopulateAccessibilityEventHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_ON_REQUEST_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET) != 0) {
mOnRequestSendAccessibilityEventHandler = newInfo.mOnRequestSendAccessibilityEventHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_PERFORM_ACCESSIBILITY_ACTION_HANDLER_IS_SET) != 0) {
mPerformAccessibilityActionHandler = newInfo.mPerformAccessibilityActionHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_SEND_ACCESSIBILITY_EVENT_HANDLER_IS_SET) != 0) {
mSendAccessibilityEventHandler = newInfo.mSendAccessibilityEventHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_SEND_ACCESSIBILITY_EVENT_UNCHECKED_HANDLER_IS_SET) != 0) {
mSendAccessibilityEventUncheckedHandler = newInfo.mSendAccessibilityEventUncheckedHandler;
}
if ((newInfo.mPrivateFlags & PFLAG_CONTENT_DESCRIPTION_IS_SET) != 0) {
mContentDescription = newInfo.mContentDescription;
}
if (newInfo.mViewTag != null) {
mViewTag = newInfo.mViewTag;
}
if (newInfo.mViewTags != null ) {
mViewTags = newInfo.mViewTags;
}
if (newInfo.getFocusState() != FOCUS_UNSET) {
mFocusState = newInfo.getFocusState();
}
}
static NodeInfo acquire() {
final NodeInfo nodeInfo = ComponentsPools.acquireNodeInfo();
if (nodeInfo.mReferenceCount.getAndSet(1) != 0) {
throw new IllegalStateException("The NodeInfo reference acquired from the pool " +
" wasn't correctly released.");
}
return nodeInfo;
}
NodeInfo acquireRef() {
if (mReferenceCount.getAndIncrement() < 1) {
throw new IllegalStateException("The NodeInfo being acquired wasn't correctly initialized.");
}
return this;
}
void release() {
final int count = mReferenceCount.decrementAndGet();
if (count < 0) {
throw new IllegalStateException("Trying to release a recycled NodeInfo.");
} else if (count > 0) {
return;
}
mContentDescription = null;
mViewTag = null;
mViewTags = null;
mClickHandler = null;
mLongClickHandler = null;
mTouchHandler = null;
mDispatchPopulateAccessibilityEventHandler = null;
mOnInitializeAccessibilityEventHandler = null;
mOnPopulateAccessibilityEventHandler = null;
mOnInitializeAccessibilityNodeInfoHandler = null;
mOnRequestSendAccessibilityEventHandler = null;
mPerformAccessibilityActionHandler = null;
mSendAccessibilityEventHandler = null;
mSendAccessibilityEventUncheckedHandler = null;
mFocusState = FOCUS_UNSET;
mPrivateFlags = 0;
ComponentsPools.release(this);
}
}