Home screen side swipe app launching added. App menu closing implemented.

This commit is contained in:
ottoptj 2024-05-27 17:17:58 +03:00
commit b12d076208
6 changed files with 173 additions and 60 deletions

View file

@ -38,6 +38,8 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>

View file

@ -0,0 +1,31 @@
package eu.ottop.yamlauncher
import android.widget.EdgeEffect
import androidx.recyclerview.widget.RecyclerView
class AppMenuEdgeFactory(private val activity: MainActivity) : RecyclerView.EdgeEffectFactory() {
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
return AppMenuEdgeEffect(activity)
}
}
class AppMenuEdgeEffect(activity: MainActivity) : EdgeEffect(activity) {
// Adjust the speed here
private val animationSpeedFactor = 0.5f
override fun onAbsorb(velocity: Int) {
super.onAbsorb((velocity * animationSpeedFactor).toInt())
}
override fun onPull(deltaDistance: Float, displacement: Float) {
super.onPull(deltaDistance * animationSpeedFactor, displacement)
}
override fun onRelease() {
super.onRelease()
}
override fun onPullDistance(deltaDistance: Float, displacement: Float): Float {
return super.onPullDistance(deltaDistance * animationSpeedFactor, displacement)
}
}

View file

@ -0,0 +1,23 @@
package eu.ottop.yamlauncher
import android.content.Context
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class AppMenuLinearLayoutManager(private val activity: MainActivity) : LinearLayoutManager(activity) {
private var scrollState = RecyclerView.SCROLL_STATE_IDLE
fun setScrollState(state: Int) {
scrollState = state
}
override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
val scrollRange = super.scrollVerticallyBy(dy, recycler, state)
val overscroll: Int = dy - scrollRange
if (overscroll < 0 && scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
activity.showHome()
}
return scrollRange
}
}

View file

@ -4,16 +4,16 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ArgbEvaluator
import android.animation.ObjectAnimator
import android.content.BroadcastReceiver
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.UserHandle
import android.provider.MediaStore
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
@ -32,6 +32,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import eu.ottop.yamlauncher.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -52,10 +53,14 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
private lateinit var searchView: EditText
private lateinit var adapter: AppMenuAdapter
private var job: Job? = null
private var appActionMenu = AppActionMenu()
val cameraIntent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
val phoneIntent = Intent(Intent.ACTION_DIAL)
private var appActionMenu = AppActionMenu()
private val sharedPreferenceManager = SharedPreferenceManager()
private val appUtils = AppUtils()
private val appMenuLinearLayoutManager = AppMenuLinearLayoutManager(this@MainActivity)
private val appMenuEdgeFactory = AppMenuEdgeFactory(this@MainActivity)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -65,19 +70,70 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
gestureDetector = GestureDetector(this, GestureListener())
setupApps()
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showHome()
}
})
}
private fun setupApps() {
handleListItems()
CoroutineScope(Dispatchers.Default).launch {
installedApps = appUtils.getInstalledApps(this@MainActivity)
val newApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
newApps.addAll(installedApps)
setupRecyclerView(newApps)
searchView = findViewById(R.id.searchView)
setupSearch()
}
}
private suspend fun setupRecyclerView(newApps: MutableList<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity)
withContext(Dispatchers.Main) {
recyclerView = findViewById(R.id.recycler_view)
recyclerView.layoutManager = appMenuLinearLayoutManager
recyclerView.edgeEffectFactory = appMenuEdgeFactory
recyclerView.adapter = adapter
recyclerView.scrollToPosition(0)
}
recyclerView.addOnScrollListener(object: OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
appMenuLinearLayoutManager.setScrollState(newState)
}
})
}
@SuppressLint("ClickableViewAccessibility")
private fun handleListItems() {
for (i in findViewById<LinearLayout>(R.id.shortcuts).children) {
val textView = i as TextView
textView.setOnTouchListener() {_, event ->
gestureDetector.onTouchEvent(event)
super.onTouchEvent(event)
}
val savedView = sharedPreferenceManager.getShortcut(this, textView)
textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null)
textView.compoundDrawablePadding = 0
i.setOnClickListener {
Toast.makeText(this, "Long click to select an app", Toast.LENGTH_SHORT).show()
}
unselectedListeners(textView)
if (savedView?.get(1) != "e") {
@ -88,56 +144,35 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null)
}
textView.text = savedView?.get(2)
textView.setOnClickListener {
val mainActivity = launcherApps.getActivityList(savedView?.get(0).toString(), launcherApps.profiles[savedView?.get(1)!!.toInt()]).firstOrNull()
if (mainActivity != null) {
launcherApps.startMainActivity(mainActivity.componentName, launcherApps.profiles[savedView[1].toInt()], null, null)
} else {
Toast.makeText(this, "Cannot launch app", Toast.LENGTH_SHORT).show()
}
}
selectedListeners(textView, savedView)
}
i.setOnLongClickListener {
binding.homeView.FadeOut()
adapter.menuMode = "shortcut"
adapter.shortcutTextView = i
binding.appView.slideInFromBottom()
return@setOnLongClickListener true
}}
gestureDetector = GestureDetector(this, GestureListener())
//Experimental
CoroutineScope(Dispatchers.Default).launch {
installedApps = appUtils.getInstalledApps(this@MainActivity)
recyclerView = findViewById(R.id.recycler_view)
val newApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
newApps.addAll(installedApps)
adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity)
withContext(Dispatchers.Main) {
recyclerView.adapter = adapter
recyclerView.scrollToPosition(0)
}
searchView = findViewById(R.id.searchView)
setupSearch()
}
}
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
binding.appView.slideOutToBottom()
binding.homeView.FadeIn()
private fun unselectedListeners(textView: TextView) {
textView.setOnClickListener {
Toast.makeText(this, "Long click to select an app", Toast.LENGTH_SHORT).show()
}
textView.setOnLongClickListener {
adapter.menuMode = "shortcut"
adapter.shortcutTextView = textView
showApps()
return@setOnLongClickListener true
}
}
private fun selectedListeners(textView: TextView, savedView: List<String>?) {
textView.setOnClickListener {
val mainActivity = launcherApps.getActivityList(savedView?.get(0).toString(), launcherApps.profiles[savedView?.get(1)!!.toInt()]).firstOrNull()
if (mainActivity != null) {
launcherApps.startMainActivity(mainActivity.componentName, launcherApps.profiles[savedView[1].toInt()], null, null)
} else {
Toast.makeText(this, "Cannot launch app", Toast.LENGTH_SHORT).show()
}
})
}
}
private fun setupSearch() {
@ -201,8 +236,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
}
override fun onNewIntent(intent: Intent?) {
binding.appView.slideOutToBottom()
binding.homeView.FadeIn()
showHome()
super.onNewIntent(intent)
}
@ -249,13 +283,25 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
velocityX: Float,
velocityY: Float
): Boolean {
// Detect swipe up gesture
if (e1 != null) {
val deltaY = e2.y - e1.y
val deltaX = e2.x - e1.x
// Detect swipe up
if (deltaY < -SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
openAppMenuActivity()
return true
}
// Detect swipe left
else if (deltaX < -SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD){
startActivity(phoneIntent)
}
// Detect swipe right
else if (deltaX > -SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
startActivity(cameraIntent)
}
}
return false
}
@ -307,7 +353,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
}
fun View.slideOutToBottom(duration: Long = 50) {
private fun View.slideOutToBottom(duration: Long = 50) {
if (visibility == View.VISIBLE) {
animate()
.translationY(height.toFloat() / 5)
@ -327,7 +373,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
}
}
private fun View.FadeIn(duration: Long = 100) {
private fun View.fadeIn(duration: Long = 100) {
if (visibility != View.VISIBLE) {
alpha = 0f
translationY = -height.toFloat()/100
@ -365,7 +411,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
}
}
fun View.FadeOut(duration: Long = 50) {
private fun View.fadeOut(duration: Long = 50) {
if (visibility == View.VISIBLE) {
animate()
.alpha(0f)
@ -378,11 +424,20 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
})}
}
fun showHome() {
binding.appView.slideOutToBottom()
binding.homeView.fadeIn()
}
private fun showApps() {
binding.homeView.fadeOut()
binding.appView.slideInFromBottom()
}
fun openAppMenuActivity() {
//AppMenuActivity.start(this, installedApps) {
//}
binding.homeView.FadeOut()
binding.appView.slideInFromBottom()
adapter.menuMode = "app"
showApps()
}
@ -418,8 +473,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
}
}
sharedPreferenceManager.setShortcut(this, shortcutView, appInfo.applicationInfo.packageName, userProfile)
binding.appView.slideOutToBottom()
binding.homeView.slideInFromBottom()
showHome()
}

View file

@ -1,6 +1,10 @@
package eu.ottop.yamlauncher
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.provider.MediaStore
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

View file

@ -48,7 +48,6 @@
android:padding="0dp"
android:requiresFadingEdge="vertical"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:stackFromEnd="true">
</androidx.recyclerview.widget.RecyclerView>