@Test
  public void lastDescentWithChildren_shouldReturnTrueAndSetItselfForLastChildren() {
    AccessibilityNodeInfo parentNode = AccessibilityNodeInfo.obtain();
    parentNode.setVisibleToUser(true);
    parentNode.setContentDescription("Parent");
    AccessibilityNodeInfo child1Node = AccessibilityNodeInfo.obtain();
    child1Node.setVisibleToUser(true);
    child1Node.setContentDescription("Child1");
    AccessibilityNodeInfo child2Node = AccessibilityNodeInfo.obtain();
    child2Node.setVisibleToUser(true);
    child2Node.setContentDescription("Child2");

    ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(parentNode)).addChild(child1Node);
    ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(parentNode)).addChild(child2Node);

    AccessibilityNodeInfoCompat parentCompat = new AccessibilityNodeInfoCompat(parentNode);

    AccessibilityNodeInfoRef ref = AccessibilityNodeInfoRef.obtain(parentCompat);
    System.out.println("lastDescentWithChildren_shouldReturnTrueAndSetItselfForLastChildren");
    assertTrue(ref.lastDescendant());
    assertEquals(child2Node, ref.get().getInfo());
    parentNode.recycle();
    child1Node.recycle();
    child2Node.recycle();
    ref.recycle();
  }
    @Override
    protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
      final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);

      if (!hasBounds) {
        // The day is invalid, kill the node.
        mTempRect.setEmpty();
        node.setContentDescription("");
        node.setBoundsInParent(mTempRect);
        node.setVisibleToUser(false);
        return;
      }

      node.setText(getDayText(virtualViewId));
      node.setContentDescription(getDayDescription(virtualViewId));
      node.setBoundsInParent(mTempRect);

      final boolean isDayEnabled = isDayEnabled(virtualViewId);
      if (isDayEnabled) {
        node.addAction(AccessibilityAction.ACTION_CLICK);
      }

      node.setEnabled(isDayEnabled);

      if (virtualViewId == mActivatedDay) {
        // TODO: This should use activated once that's supported.
        node.setChecked(true);
      }
    }
  @Test
  public void parentWithSelfReferencedParent_shouldReturnFalse() {
    AccessibilityNodeInfo curNode = AccessibilityNodeInfo.obtain();
    curNode.setVisibleToUser(true);
    AccessibilityNodeInfo parentNode = AccessibilityNodeInfo.obtain();
    parentNode.setVisibleToUser(true);

    curNode.setContentDescription("Current");
    ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(curNode)).addChild(curNode);
    ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(curNode)).setVisibleToUser(true);

    AccessibilityNodeInfoCompat currentCompat = new AccessibilityNodeInfoCompat(curNode);

    AccessibilityNodeInfoRef ref = AccessibilityNodeInfoRef.obtain(currentCompat);
    assertFalse(ref.parent());
    parentNode.recycle();
    curNode.recycle();
    ref.recycle();
  }
 public void b(Object paramObject, CharSequence paramCharSequence) {
   ((AccessibilityNodeInfo) paramObject).setContentDescription(paramCharSequence);
 }
  public static void sendAccessibilityEvent(final JSONObject message) {
    if (!sEnabled) return;

    final int eventType = message.optInt("eventType", -1);
    if (eventType < 0) {
      Log.e(LOGTAG, "No accessibility event type provided");
      return;
    }

    if (Versions.preJB) {
      // Before Jelly Bean we send events directly from here while spoofing the source by setting
      // the package and class name manually.
      ThreadUtils.postToBackgroundThread(
          new Runnable() {
            @Override
            public void run() {
              sendDirectAccessibilityEvent(eventType, message);
            }
          });
    } else {
      // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have
      // it work with TalkBack.
      final LayerView view = GeckoAppShell.getLayerView();
      if (view == null) return;

      if (sVirtualCursorNode == null)
        sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION);
      sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true));
      sVirtualCursorNode.setClickable(message.optBoolean("clickable"));
      sVirtualCursorNode.setCheckable(message.optBoolean("checkable"));
      sVirtualCursorNode.setChecked(message.optBoolean("checked"));
      sVirtualCursorNode.setPassword(message.optBoolean("password"));

      final JSONArray textArray = message.optJSONArray("text");
      StringBuilder sb = new StringBuilder();
      if (textArray != null && textArray.length() > 0) {
        sb.append(textArray.optString(0));
        for (int i = 1; i < textArray.length(); i++) {
          sb.append(" ").append(textArray.optString(i));
        }
      }
      sVirtualCursorNode.setText(sb.toString());
      sVirtualCursorNode.setContentDescription(message.optString("description"));

      JSONObject bounds = message.optJSONObject("bounds");
      if (bounds != null) {
        Rect relativeBounds =
            new Rect(
                bounds.optInt("left"), bounds.optInt("top"),
                bounds.optInt("right"), bounds.optInt("bottom"));
        sVirtualCursorNode.setBoundsInParent(relativeBounds);
        int[] locationOnScreen = new int[2];
        view.getLocationOnScreen(locationOnScreen);
        Rect screenBounds = new Rect(relativeBounds);
        screenBounds.offset(locationOnScreen[0], locationOnScreen[1]);
        sVirtualCursorNode.setBoundsInScreen(screenBounds);
      }

      final JSONObject braille = message.optJSONObject("brailleOutput");
      if (braille != null) {
        sendBrailleText(
            view,
            braille.optString("text"),
            braille.optInt("selectionStart"),
            braille.optInt("selectionEnd"));
      }

      ThreadUtils.postToUiThread(
          new Runnable() {
            @Override
            public void run() {
              // If this is an accessibility focus, a lot of internal voodoo happens so we perform
              // an
              // accessibility focus action on the view, and it in turn sends the right events.
              switch (eventType) {
                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
                  sEventMessage = message;
                  view.performAccessibilityAction(
                      AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
                  break;
                case AccessibilityEvent.TYPE_ANNOUNCEMENT:
                case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                  sEventMessage = null;
                  final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType);
                  view.onInitializeAccessibilityEvent(accEvent);
                  populateEventFromJSON(accEvent, message);
                  view.getParent().requestSendAccessibilityEvent(view, accEvent);
                  break;
                default:
                  sEventMessage = message;
                  view.sendAccessibilityEvent(eventType);
                  break;
              }
            }
          });
    }
  }