<commit_before>package com.nextfaze.devfun.menu.controllers

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.support.v4.app.FragmentActivity
import android.support.v7.view.WindowCallbackWrapper
import android.view.KeyEvent
import com.nextfaze.devfun.internal.ActivityProvider
import com.nextfaze.devfun.internal.logger
import com.nextfaze.devfun.internal.registerOnActivityCreated
import com.nextfaze.devfun.internal.t
import com.nextfaze.devfun.menu.DeveloperMenu
import com.nextfaze.devfun.menu.MenuController

private val DEFAULT_KEY_SEQUENCE = intArrayOf(
        KeyEvent.KEYCODE_VOLUME_DOWN,
        KeyEvent.KEYCODE_VOLUME_DOWN,
        KeyEvent.KEYCODE_VOLUME_UP,
        KeyEvent.KEYCODE_VOLUME_DOWN
)

class KeySequence(context: Context,
                  private val activityProvider: ActivityProvider,
                  private val keySequence: IntArray = DEFAULT_KEY_SEQUENCE) : MenuController {
    private val log = logger()
    private val application = context.applicationContext as Application

    private var listener: Application.ActivityLifecycleCallbacks? = null
    private var developerMenu: DeveloperMenu? = null

    override fun attach(developerMenu: DeveloperMenu) {
        this.developerMenu = developerMenu
        listener = application.registerOnActivityCreated(this::onActivityCreated)
    }

    override fun detach() {
        application.unregisterActivityLifecycleCallbacks(listener)
        listener = null
        developerMenu = null
    }

    private fun onActivityCreated(activity: Activity, @Suppress("UNUSED_PARAMETER") savedInstanceState: Bundle?) {
        activity.window?.let {
            it.callback = object : WindowCallbackWrapper(it.callback) {
                override fun dispatchKeyEvent(event: KeyEvent): Boolean {
                    onKeyEvent(event.action, event.keyCode)
                    return super.dispatchKeyEvent(event)
                }
            }
        }
    }

    private var sequenceIdx = 0
    private fun onKeyEvent(action: Int, code: Int) {
        if (developerMenu?.isVisible ?: true || action != KeyEvent.ACTION_DOWN) return

        if (code == keySequence[sequenceIdx]) {
            sequenceIdx++
            log.t { "Matched code=$code or $sequenceIdx/${keySequence.size}" }
        } else {
            sequenceIdx = 0
            log.t { "Sequence reset" }
        }

        if (sequenceIdx == keySequence.size) {
            sequenceIdx = 0
            (activityProvider() as? FragmentActivity)?.let { developerMenu?.show(it) }
        }
    }

    override fun onShown() {
        sequenceIdx = 0
    }

    override fun onDismissed() {
        sequenceIdx = 0
    }
}
<commit_msg>Add ability to hide dev menu on key sequence #8<commit_after>package com.nextfaze.devfun.menu.controllers

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks
import android.view.KeyEvent
import android.view.Window
import com.nextfaze.devfun.internal.*
import com.nextfaze.devfun.menu.DeveloperMenu
import com.nextfaze.devfun.menu.MenuController

private val DEFAULT_KEY_SEQUENCE = intArrayOf(
        KeyEvent.KEYCODE_VOLUME_DOWN,
        KeyEvent.KEYCODE_VOLUME_DOWN,
        KeyEvent.KEYCODE_VOLUME_UP,
        KeyEvent.KEYCODE_VOLUME_DOWN
)

class KeySequence(
        context: Context,
        private val activityProvider: ActivityProvider,
        private val keySequence: IntArray = DEFAULT_KEY_SEQUENCE
) : MenuController {

    private val log = logger()
    private val application = context.applicationContext as Application

    private var listener: Application.ActivityLifecycleCallbacks? = null
    private var developerMenu: DeveloperMenu? = null

    private var sequenceIdx = 0

    override fun attach(developerMenu: DeveloperMenu) {
        this.developerMenu = developerMenu
        listener = application.registerOnActivityCreated(this::onActivityCreated)
    }

    override fun detach() {
        application.unregisterActivityLifecycleCallbacks(listener)
        listener = null
        developerMenu = null
    }

    private fun onActivityCreated(activity: Activity, @Suppress("UNUSED_PARAMETER") savedInstanceState: Bundle?) {
        activity.window?.wrapCallbackIfNecessary()

        if (activity is FragmentActivity) {
            activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentLifecycleCallbacks() {
                override fun onFragmentStarted(fm: FragmentManager?, f: Fragment?) {
                    when (f) {
                        is DialogFragment -> f.dialog?.window?.wrapCallbackIfNecessary()
                        is android.app.DialogFragment -> f.dialog?.window?.wrapCallbackIfNecessary()
                    }
                }
            }, false)
        }
    }

    private fun onKeyEvent(action: Int, code: Int) {
        if (action != KeyEvent.ACTION_DOWN) return

        if (code == keySequence[sequenceIdx]) {
            sequenceIdx++
            log.t { "Matched code=$code or $sequenceIdx/${keySequence.size}" }
        } else {
            sequenceIdx = 0
            log.t { "Sequence reset" }
        }

        if (sequenceIdx == keySequence.size) {
            sequenceIdx = 0
            (activityProvider() as? FragmentActivity)?.let {
                if (developerMenu?.isVisible == true) {
                    developerMenu?.hide(it)
                } else {
                    developerMenu?.show(it)
                }
            }
        }
    }

    override fun onShown() {
        sequenceIdx = 0
    }

    override fun onDismissed() {
        sequenceIdx = 0
    }

    private fun Window.wrapCallbackIfNecessary() {
        if (callback !is WindowCallbackWrapper) callback = WindowCallbackWrapper(callback)
    }

    private inner class WindowCallbackWrapper(callback: Window.Callback) : android.support.v7.view.WindowCallbackWrapper(callback) {
        override fun dispatchKeyEvent(event: KeyEvent): Boolean {
            onKeyEvent(event.action, event.keyCode)
            return super.dispatchKeyEvent(event)
        }
    }
}

{'fraction_non_alphanumeric': 0.08184222810020227, 'fraction_numerical': 0.0029562782013381047, 'mean_word_length': 2.7091748413156376, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3022603', 'n_tokens_mistral': 1725, 'n_tokens_neox': 1656, 'n_words': 358}