<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.ground

import android.R
import android.view.View
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.FragmentActivity
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.IdlingResource
import java.util.*

/**
 * Used to make Espresso work with DataBinding. Without it the tests will be flaky because
 * DataBinding uses Choreographer class to synchronize its view updates hence using this to monitor
 * a launched fragment in fragment scenario will make Espresso wait before doing additional checks
 */
class DataBindingIdlingResource : IdlingResource {
  // Holds whether isIdle was called and the result was false. We track this to avoid calling
  // onTransitionToIdle callbacks if Espresso never thought we were idle in the first place.
  private var wasNotIdle = false
  private var activity: FragmentActivity? = null
  override fun getName(): String {
    return String.format("DataBinding %s", ID)
  }

  override fun isIdleNow(): Boolean {
    var idle = false
    for (b in bindings) {
      if (b == null) {
        continue
      }
      if (!b.hasPendingBindings()) {
        idle = true
        break
      }
    }
    if (idle) {
      if (wasNotIdle) {
        // Notify observers to avoid Espresso race detector.
        for (cb in IDLING_CALLBACKS) {
          cb.onTransitionToIdle()
        }
      }
      wasNotIdle = false
    } else {
      wasNotIdle = true
      // Check next frame.
      if (activity != null) {
        activity!!.findViewById<View>(R.id.content).postDelayed({ this.isIdleNow }, 16)
      }
    }
    return idle
  }

  override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
    IDLING_CALLBACKS.add(callback)
  }

  /** Sets the activity from an [ActivityScenario] to be used from [DataBindingIdlingResource]. */
  fun <T : FragmentActivity?> monitorActivity(activityScenario: ActivityScenario<T>) {
    activityScenario.onActivity { activity: T -> this.activity = activity }
  }

  private fun getBinding(view: View?): ViewDataBinding? {
    return DataBindingUtil.getBinding(view!!)
  }

  private val bindings: List<ViewDataBinding?>
    get() {
      val fragments =
        if (activity == null) emptyList() else activity!!.supportFragmentManager.fragments
      val bindings: MutableList<ViewDataBinding?> = ArrayList()
      for (f in fragments) {
        if (f.view == null) {
          continue
        }
        bindings.add(getBinding(f.view))
        for (cf in f.childFragmentManager.fragments) {
          if (cf.view == null) {
            continue
          }
          bindings.add(getBinding(cf.view))
          for (cf2 in cf.childFragmentManager.fragments) {
            if (cf2.view == null) {
              continue
            }
            bindings.add(getBinding(cf2.view))<commit_msg>Apply suggested changes<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.ground

import android.R
import android.view.View
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.FragmentActivity
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.IdlingResource
import java.util.*

/**
 * Used to make Espresso work with DataBinding. Without it the tests will be flaky because
 * DataBinding uses Choreographer class to synchronize its view updates hence using this to monitor
 * a launched fragment in fragment scenario will make Espresso wait before doing additional checks
 */
class DataBindingIdlingResource : IdlingResource {
  // Holds whether isIdle was called and the result was false. We track this to avoid calling
  // onTransitionToIdle callbacks if Espresso never thought we were idle in the first place.
  private var wasNotIdle = false
  private var activity: FragmentActivity? = null

  override fun getName(): String = String.format("DataBinding $ID")

  override fun isIdleNow(): Boolean {
    var idle = false
    for (b in bindings) {
      if (b == null) {
        continue
      }
      if (!b.hasPendingBindings()) {
        idle = true
        break
      }
    }
    if (idle) {
      if (wasNotIdle) {
        // Notify observers to avoid Espresso race detector.
        for (cb in IDLING_CALLBACKS) {
          cb.onTransitionToIdle()
        }
      }
      wasNotIdle = false
    } else {
      wasNotIdle = true
      // Check next frame.
      if (activity != null) {
        activity!!.findViewById<View>(R.id.content).postDelayed({ this.isIdleNow }, 16)
      }
    }
    return idle
  }

  override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
    IDLING_CALLBACKS.add(callback)
  }

  /** Sets the activity from an [ActivityScenario] to be used from [DataBindingIdlingResource]. */
  fun <T : FragmentActivity?> monitorActivity(activityScenario: ActivityScenario<T>) {
    activityScenario.onActivity { activity: T -> this.activity = activity }
  }

  private fun getBinding(view: View?): ViewDataBinding? {
    return DataBindingUtil.getBinding(view!!)
  }

  private val bindings: List<ViewDataBinding?>
    get() {
      val fragments = activity?.supportFragmentManager?.fragments ?: emptyList()
      val bindings: MutableList<ViewDataBinding?> = ArrayList()
      for (f in fragments) {
        val fv: View = f.view ?: continue
        bindings.add(getBinding(fv))
        for (cf in f.childFragmentManager.fragments) {
          val cfv: View = cf.view ?: continue
          bindings.add(getBinding(cfv))
          for (cf2 in cf.childFragmentManager.fragments) {
            val cf2v: View = cf2.view ?: continue
            bindings.add(getBinding(cf2v))
{'fraction_non_alphanumeric': 0.0847898309981222, 'fraction_numerical': 0.00390004333381482, 'mean_word_length': 3.049122807017544, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7395313', 'n_tokens_mistral': 1921, 'n_tokens_neox': 1882, 'n_words': 642}