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" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </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.AnimatorListenerAdapter
import android.animation.ArgbEvaluator import android.animation.ArgbEvaluator
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.content.BroadcastReceiver import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps import android.content.pm.LauncherApps
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.UserHandle import android.os.UserHandle
import android.provider.MediaStore
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.Log import android.util.Log
@ -32,6 +32,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import eu.ottop.yamlauncher.databinding.ActivityMainBinding import eu.ottop.yamlauncher.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -52,10 +53,14 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
private lateinit var searchView: EditText private lateinit var searchView: EditText
private lateinit var adapter: AppMenuAdapter private lateinit var adapter: AppMenuAdapter
private var job: Job? = null 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 sharedPreferenceManager = SharedPreferenceManager()
private val appUtils = AppUtils() private val appUtils = AppUtils()
private val appMenuLinearLayoutManager = AppMenuLinearLayoutManager(this@MainActivity)
private val appMenuEdgeFactory = AppMenuEdgeFactory(this@MainActivity)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -65,19 +70,70 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps 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) { for (i in findViewById<LinearLayout>(R.id.shortcuts).children) {
val textView = i as TextView val textView = i as TextView
textView.setOnTouchListener() {_, event ->
gestureDetector.onTouchEvent(event)
super.onTouchEvent(event)
}
val savedView = sharedPreferenceManager.getShortcut(this, textView) val savedView = sharedPreferenceManager.getShortcut(this, textView)
textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null) textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null)
textView.compoundDrawablePadding = 0 textView.compoundDrawablePadding = 0
unselectedListeners(textView)
i.setOnClickListener {
Toast.makeText(this, "Long click to select an app", Toast.LENGTH_SHORT).show()
}
if (savedView?.get(1) != "e") { if (savedView?.get(1) != "e") {
@ -88,6 +144,27 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null) textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, R.drawable.ic_empty, null),null,null,null)
} }
textView.text = savedView?.get(2) textView.text = savedView?.get(2)
selectedListeners(textView, savedView)
}
}
}
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 { textView.setOnClickListener {
val mainActivity = launcherApps.getActivityList(savedView?.get(0).toString(), launcherApps.profiles[savedView?.get(1)!!.toInt()]).firstOrNull() val mainActivity = launcherApps.getActivityList(savedView?.get(0).toString(), launcherApps.profiles[savedView?.get(1)!!.toInt()]).firstOrNull()
if (mainActivity != null) { if (mainActivity != null) {
@ -98,48 +175,6 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
} }
} }
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 setupSearch() { private fun setupSearch() {
binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom -> binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
if (bottom - top > oldBottom - oldTop) { if (bottom - top > oldBottom - oldTop) {
@ -201,8 +236,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
} }
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
binding.appView.slideOutToBottom() showHome()
binding.homeView.FadeIn()
super.onNewIntent(intent) super.onNewIntent(intent)
} }
@ -249,13 +283,25 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
velocityX: Float, velocityX: Float,
velocityY: Float velocityY: Float
): Boolean { ): Boolean {
// Detect swipe up gesture
if (e1 != null) { if (e1 != null) {
val deltaY = e2.y - e1.y val deltaY = e2.y - e1.y
val deltaX = e2.x - e1.x
// Detect swipe up
if (deltaY < -SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (deltaY < -SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
openAppMenuActivity() openAppMenuActivity()
return true 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 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) { if (visibility == View.VISIBLE) {
animate() animate()
.translationY(height.toFloat() / 5) .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) { if (visibility != View.VISIBLE) {
alpha = 0f alpha = 0f
translationY = -height.toFloat()/100 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) { if (visibility == View.VISIBLE) {
animate() animate()
.alpha(0f) .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() { fun openAppMenuActivity() {
//AppMenuActivity.start(this, installedApps) { //AppMenuActivity.start(this, installedApps) {
//} //}
binding.homeView.FadeOut() adapter.menuMode = "app"
binding.appView.slideInFromBottom() showApps()
} }
@ -418,8 +473,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
} }
} }
sharedPreferenceManager.setShortcut(this, shortcutView, appInfo.applicationInfo.packageName, userProfile) sharedPreferenceManager.setShortcut(this, shortcutView, appInfo.applicationInfo.packageName, userProfile)
binding.appView.slideOutToBottom() showHome()
binding.homeView.slideInFromBottom()
} }

View file

@ -1,6 +1,10 @@
package eu.ottop.yamlauncher package eu.ottop.yamlauncher
import android.content.Context 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 android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity

View file

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