mirror of
https://github.com/recloudstream/cloudstream.git
synced 2025-05-17 11:15:54 +08:00
refactor: convert monkey activity 🐒 into a fragment monke 🦍 (#1576)
* Turn monkey activity 🐒 into a fragment monke 🦍
This commit is contained in:
@ -196,11 +196,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.EasterEggMonkeActivity"
|
||||
android:exported="false"
|
||||
android:noHistory="true" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.VideoDownloadRestartReceiver"
|
||||
android:enabled="false"
|
||||
|
@ -1,97 +0,0 @@
|
||||
// https://github.com/googlecodelabs/android-kotlin-animation-property-animation/tree/master/begin
|
||||
|
||||
package com.lagradost.cloudstream3.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.view.isVisible
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.databinding.ActivityEasterEggMonkeBinding
|
||||
|
||||
class EasterEggMonkeActivity : ComponentActivity() {
|
||||
|
||||
lateinit var binding : ActivityEasterEggMonkeBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityEasterEggMonkeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val handler = Handler(mainLooper)
|
||||
lateinit var runnable: Runnable
|
||||
runnable = Runnable {
|
||||
shower()
|
||||
handler.postDelayed(runnable, 300)
|
||||
}
|
||||
handler.postDelayed(runnable, 1000)
|
||||
}
|
||||
|
||||
private fun shower() {
|
||||
|
||||
val containerW = binding.frame.width
|
||||
val containerH = binding.frame.height
|
||||
var starW: Float = binding.monke.width.toFloat()
|
||||
var starH: Float = binding.monke.height.toFloat()
|
||||
|
||||
val newStar = AppCompatImageView(this)
|
||||
val idx = (monkeys.size * Math.random()).toInt()
|
||||
newStar.setImageResource(monkeys[idx])
|
||||
newStar.isVisible = true
|
||||
newStar.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT)
|
||||
binding.frame.addView(newStar)
|
||||
|
||||
newStar.scaleX += Math.random().toFloat() * 1.5f
|
||||
newStar.scaleY = newStar.scaleX
|
||||
starW *= newStar.scaleX
|
||||
starH *= newStar.scaleY
|
||||
|
||||
newStar.translationX = Math.random().toFloat() * containerW - starW / 2
|
||||
|
||||
val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y, -starH, containerH + starH)
|
||||
mover.interpolator = AccelerateInterpolator(1f)
|
||||
|
||||
val rotator = ObjectAnimator.ofFloat(newStar, View.ROTATION,
|
||||
(Math.random() * 1080).toFloat())
|
||||
rotator.interpolator = LinearInterpolator()
|
||||
|
||||
val set = AnimatorSet()
|
||||
set.playTogether(mover, rotator)
|
||||
set.duration = (Math.random() * 1500 + 2500).toLong()
|
||||
|
||||
set.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.frame.removeView(newStar)
|
||||
}
|
||||
})
|
||||
|
||||
set.start()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val monkeys = listOf(
|
||||
R.drawable.monke_benene,
|
||||
R.drawable.monke_burrito,
|
||||
R.drawable.monke_coco,
|
||||
R.drawable.monke_cookie,
|
||||
R.drawable.monke_flusdered,
|
||||
R.drawable.monke_funny,
|
||||
R.drawable.monke_like,
|
||||
R.drawable.monke_party,
|
||||
R.drawable.monke_sob,
|
||||
R.drawable.monke_drink,
|
||||
R.drawable.benene,
|
||||
R.drawable.ic_launcher_foreground
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.lagradost.cloudstream3.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.databinding.FragmentEasterEggMonkeBinding
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.random.Random
|
||||
|
||||
class EasterEggMonkeFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentEasterEggMonkeBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
// planet of monks
|
||||
private val monkeys: List<Int> = listOf(
|
||||
R.drawable.monke_benene,
|
||||
R.drawable.monke_burrito,
|
||||
R.drawable.monke_coco,
|
||||
R.drawable.monke_cookie,
|
||||
R.drawable.monke_flusdered,
|
||||
R.drawable.monke_funny,
|
||||
R.drawable.monke_like,
|
||||
R.drawable.monke_party,
|
||||
R.drawable.monke_sob,
|
||||
R.drawable.monke_drink,
|
||||
R.drawable.benene,
|
||||
R.drawable.ic_launcher_foreground,
|
||||
R.drawable.quick_novel_icon,
|
||||
)
|
||||
|
||||
private val activeMonkeys = mutableListOf<ImageView>()
|
||||
private var spawningJob: Job? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentEasterEggMonkeBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
activity?.hideSystemUI()
|
||||
spawningJob = lifecycleScope.launch {
|
||||
delay(1000)
|
||||
while (isActive) {
|
||||
spawnMonkey()
|
||||
delay(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun spawnMonkey() {
|
||||
val newMonkey = ImageView(context ?: return).apply {
|
||||
setImageResource(monkeys.random())
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
val initialScale = Random.nextFloat() * 1.5f + 0.5f
|
||||
newMonkey.scaleX = initialScale
|
||||
newMonkey.scaleY = initialScale
|
||||
|
||||
newMonkey.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
val monkeyW = newMonkey.measuredWidth * initialScale
|
||||
val monkeyH = newMonkey.measuredHeight * initialScale
|
||||
|
||||
newMonkey.x = Random.nextFloat() * (binding.frame.width.toFloat() - monkeyW)
|
||||
newMonkey.y = Random.nextFloat() * (binding.frame.height.toFloat() - monkeyH)
|
||||
|
||||
binding.frame.addView(newMonkey, FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
))
|
||||
|
||||
activeMonkeys.add(newMonkey)
|
||||
|
||||
newMonkey.alpha = 0f
|
||||
ObjectAnimator.ofFloat(newMonkey, View.ALPHA, 0f, 1f).apply {
|
||||
duration = Random.nextLong(1000, 2500)
|
||||
interpolator = AccelerateInterpolator()
|
||||
start()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
newMonkey.setOnTouchListener { view, event -> handleTouch(view, event) }
|
||||
|
||||
startFloatingAnimation(newMonkey)
|
||||
}
|
||||
|
||||
private fun startFloatingAnimation(monkey: ImageView) {
|
||||
val floatUpAnimator = ObjectAnimator.ofFloat(
|
||||
monkey, View.TRANSLATION_Y, monkey.y, -monkey.height.toFloat()
|
||||
).apply {
|
||||
duration = Random.nextLong(8000, 15000)
|
||||
interpolator = LinearInterpolator()
|
||||
}
|
||||
|
||||
floatUpAnimator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
// necessary check because binding becomes null but monkes are still moving until onDestroy()
|
||||
if (_binding != null) {
|
||||
binding.frame.removeView(monkey)
|
||||
activeMonkeys.remove(monkey)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
floatUpAnimator.start()
|
||||
monkey.tag = floatUpAnimator
|
||||
}
|
||||
|
||||
private fun handleTouch(view: View, event: MotionEvent): Boolean {
|
||||
val monkey = view as ImageView
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
(monkey.tag as? ObjectAnimator)?.pause()
|
||||
return true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// Update both X and Y positions properly
|
||||
monkey.x = event.rawX - monkey.width / 2
|
||||
monkey.y = event.rawY - monkey.height / 2
|
||||
|
||||
// Check if monkey touches the screen edge
|
||||
if (isTouchingEdge(monkey)) {
|
||||
removeMonkey(monkey)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (isTouchingEdge(monkey)) {
|
||||
removeMonkey(monkey)
|
||||
} else {
|
||||
startFloatingAnimation(monkey)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isTouchingEdge(monkey: ImageView): Boolean {
|
||||
return monkey.x <= 0 || monkey.x + monkey.width >= binding.frame.width ||
|
||||
monkey.y <= 0 || monkey.y + monkey.height >= binding.frame.height
|
||||
}
|
||||
|
||||
private fun removeMonkey(monkey: ImageView) {
|
||||
// Fade out and remove the monkey
|
||||
ObjectAnimator.ofFloat(monkey, View.ALPHA, 1f, 0f).apply {
|
||||
duration = 300
|
||||
addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.frame.removeView(monkey)
|
||||
activeMonkeys.remove(monkey)
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
activity?.showSystemUI()
|
||||
spawningJob?.cancel()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@ -391,7 +391,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||
try {
|
||||
beneneCount++
|
||||
if (beneneCount%20 == 0) {
|
||||
activity?.navigate(R.id.easterEggMonkeActivity)
|
||||
activity?.navigate(R.id.action_navigation_settings_general_to_easterEggMonkeFragment)
|
||||
}
|
||||
settingsManager.edit().putInt(
|
||||
getString(R.string.benene_count),
|
||||
|
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~ 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
|
||||
~ http://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.
|
||||
-->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/black"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/monke"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:scaleX="3"
|
||||
android:scaleY="3"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/monke_benene"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
15
app/src/main/res/layout/fragment_easter_egg_monke.xml
Normal file
15
app/src/main/res/layout/fragment_easter_egg_monke.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/frame">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/monke"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
@ -184,7 +184,15 @@
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
app:exitAnim="@anim/exit_anim"
|
||||
app:popEnterAnim="@anim/enter_anim"
|
||||
app:popExitAnim="@anim/exit_anim" />
|
||||
app:popExitAnim="@anim/exit_anim" >
|
||||
<action
|
||||
android:id="@+id/action_navigation_settings_general_to_easterEggMonkeFragment"
|
||||
app:destination="@id/easterEggMonkeFragment"
|
||||
app:enterAnim="@anim/go_right"
|
||||
app:exitAnim="@anim/go_left"
|
||||
app:popEnterAnim="@anim/go_right"
|
||||
app:popExitAnim="@anim/go_left" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_settings_extensions"
|
||||
@ -668,10 +676,9 @@
|
||||
android:label="AccountSelectActivity"
|
||||
tools:layout="@layout/activity_account_select" />
|
||||
|
||||
<activity
|
||||
android:id="@+id/easterEggMonkeActivity"
|
||||
android:name="com.lagradost.cloudstream3.ui.EasterEggMonkeActivity"
|
||||
android:label="EasterEggMonkeActivity"
|
||||
tools:layout="@layout/activity_easter_egg_monke" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/easterEggMonkeFragment"
|
||||
android:name="com.lagradost.cloudstream3.ui.EasterEggMonkeFragment"
|
||||
tools:layout="@layout/fragment_easter_egg_monke"
|
||||
android:label="EasterEggMonkeFragment" />
|
||||
</navigation>
|
||||
|
Reference in New Issue
Block a user