feature: work profile

This commit is contained in:
Josia Pietsch 2024-08-02 22:32:35 +02:00
parent 7a60611ec5
commit 500062b29b
Signed by: jrpie
GPG key ID: E70B571D66986A2D
9 changed files with 100 additions and 29 deletions

View file

@ -11,6 +11,7 @@ This is a fork of [finnmglas's app Launcher][original-repo].
## Notable changes:
* Edge gestures: There is a setting to allow distinguishing swiping at the edges of the screen from swiping in the center.
* Compatible with [work profile](https://www.android.com/enterprise/work-profile/), so apps like [Shelter](https://gitea.angry.im/PeterCxy/Shelter) can be used.
### Visual
* This app uses the system wallpaper instead of a custom solution.

View file

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
<queries>

View file

@ -67,6 +67,8 @@ const val PREF_STARTED_TIME = "firstStartup"
const val PREF_VERSION = "version"
const val INVALID_USER = -1
/* Objects used by multiple activities */
val appsList: MutableList<AppInfo> = ArrayList()
@ -153,14 +155,15 @@ private fun getIntent(packageName: String, context: Context): Intent? {
}
fun launch(
data: String, activity: Activity,
data: String, user: Int?,
activity: Activity,
animationIn: Int = android.R.anim.fade_in, animationOut: Int = android.R.anim.fade_out
) {
if (LauncherAction.isOtherAction(data)) { // [type]:[info]
LauncherAction.byId(data)?.let {it.launch(activity) }
}
else launchApp(data, activity) // app
else launchApp(data, user, activity) // app
activity.overridePendingTransition(animationIn, animationOut)
}
@ -217,7 +220,20 @@ fun audioVolumeDown(activity: Activity) {
/* --- */
fun launchApp(packageName: String, context: Context) {
fun launchApp(packageName: String, user: Int?, context: Context) {
Log.i("Launcher", "Starting: " + packageName + " (user " +user.toString()+ ")")
if (user != null) {
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
userManager.userProfiles.firstOrNull { it.hashCode() == user }?.let {
userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()?.let {
app -> launcherApps.startMainActivity(app.componentName, userHandle, null, null)
return
}
}
}
val intent = getIntent(packageName, context)
if (intent != null) {
@ -327,25 +343,57 @@ fun openAppsList(activity: Activity){
activity.startActivity(intent)
}
fun getAppIcon(context: Context, packageName: String, user: Int?): Drawable {
if (user != null) {
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
userManager.userProfiles.firstOrNull { it.hashCode() == user }?.let {
userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()?.let {
app -> return app.getBadgedIcon(0)
}
}
}
return context.packageManager.getApplicationIcon(packageName)
}
/**
* [loadApps] is used to speed up the [AppsRecyclerAdapter] loading time,
* as it caches all the apps and allows for fast access to the data.
*/
fun loadApps(packageManager: PackageManager) {
fun loadApps(packageManager: PackageManager, context: Context) {
val loadList = mutableListOf<AppInfo>()
val i = Intent(Intent.ACTION_MAIN, null)
i.addCategory(Intent.CATEGORY_LAUNCHER)
val allApps = packageManager.queryIntentActivities(i, 0)
for (ri in allApps) {
val app = AppInfo()
app.label = ri.loadLabel(packageManager)
app.packageName = ri.activityInfo.packageName
app.icon = ri.activityInfo.loadIcon(packageManager)
loadList.add(app)
}
loadList.sortBy { it.label.toString() }
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
// TODO: shortcuts - launcherApps.getShortcuts()
val users = userManager.userProfiles
for(user in users) {
for (activityInfo in launcherApps.getActivityList(null,user)) {
val app = AppInfo()
app.label = activityInfo.label
app.packageName = activityInfo.applicationInfo.packageName
app.icon = activityInfo.getBadgedIcon(0)
app.user = user.hashCode()
loadList.add(app)
}
}
// fallback option
if(loadList.isEmpty()){
Log.i("Launcher", "using fallback option to load packages")
val i = Intent(Intent.ACTION_MAIN, null)
i.addCategory(Intent.CATEGORY_LAUNCHER)
val allApps = packageManager.queryIntentActivities(i, 0)
for (ri in allApps) {
val app = AppInfo()
app.label = ri.loadLabel(packageManager)
app.packageName = ri.activityInfo.packageName
app.icon = ri.activityInfo.loadIcon(packageManager)
loadList.add(app)
}
}
appsList.clear()
appsList.addAll(loadList)
}
@ -404,9 +452,12 @@ fun setWindowFlags(window: Window) {
// Used in Tutorial and Settings `ActivityOnResult`
fun saveListActivityChoice(context: Context, data: Intent?) {
val value = data?.getStringExtra("value")
var user = data?.getIntExtra("user", INVALID_USER)
user = user?.let{ if(it == INVALID_USER) null else it }
val forGesture = data?.getStringExtra("forGesture") ?: return
Gesture.byId(forGesture)?.setApp(context, value.toString())
Gesture.byId(forGesture)?.setApp(context, value.toString(), user)
loadSettings(context)
}

View file

@ -41,8 +41,12 @@ enum class Gesture (val id: String, private val labelResource: Int,
TOP, BOTTOM, LEFT, RIGHT
}
fun getApp(context: Context): String {
return getPreferences(context).getString(this.id, "")!!
fun getApp(context: Context): Pair<String, Int?> {
val preferences = getPreferences(context)
var packageName = preferences.getString(this.id, "")!!
var u: Int? = preferences.getInt(this.id + "_user", INVALID_USER)
u = if(u == INVALID_USER) null else u
return Pair(packageName,u)
}
fun removeApp(context: Context) {
@ -51,10 +55,15 @@ enum class Gesture (val id: String, private val labelResource: Int,
.apply()
}
fun setApp(context: Context, app: String) {
fun setApp(context: Context, app: String, user: Int?) {
getPreferences(context).edit()
.putString(this.id, app)
.apply()
val u = user?: INVALID_USER
getPreferences(context).edit()
.putInt(this.id + "_user", u)
.apply()
}
fun getLabel(context: Context): String {
@ -132,7 +141,8 @@ enum class Gesture (val id: String, private val labelResource: Int,
}
operator fun invoke(activity: Activity) {
launch(this.getApp(activity), activity, this.animationIn, this.animationOut)
val app = this.getApp(activity)
launch(app.first, app.second, activity, this.animationIn, this.animationOut)
}
companion object {

View file

@ -87,7 +87,7 @@ class HomeActivity: UIObject, AppCompatActivity(),
}
// Preload apps to speed up the Apps Recycler
AsyncTask.execute { loadApps(packageManager) }
AsyncTask.execute { loadApps(packageManager, applicationContext) }
// Initialise layout
binding = HomeBinding.inflate(layoutInflater)

View file

@ -8,6 +8,7 @@ import android.graphics.drawable.Drawable
* Represents an app installed on the users device.
*/
class AppInfo {
var user: Int? = null
var label: CharSequence? = null
var packageName: CharSequence? = null
var icon: Drawable? = null

View file

@ -21,6 +21,7 @@ import de.jrpie.android.launcher.appsList
import de.jrpie.android.launcher.getPreferences
import de.jrpie.android.launcher.getSavedTheme
import de.jrpie.android.launcher.launch
import de.jrpie.android.launcher.launchApp
import de.jrpie.android.launcher.list.ListActivity
import de.jrpie.android.launcher.list.intendedChoosePause
import de.jrpie.android.launcher.loadApps
@ -54,9 +55,10 @@ class AppsRecyclerAdapter(val activity: Activity,
val pos = adapterPosition
val context: Context = v.context
val appPackageName = appsListDisplayed[pos].packageName.toString()
val appUser = appsListDisplayed[pos].user
when (intention){
ListActivity.ListActivityIntention.VIEW -> {
launchApp(appPackageName, appUser, activity)
val launchIntent: Intent = context.packageManager
.getLaunchIntentForPackage(appPackageName)!!
context.startActivity(launchIntent)
@ -64,6 +66,7 @@ class AppsRecyclerAdapter(val activity: Activity,
ListActivity.ListActivityIntention.PICK -> {
val returnIntent = Intent()
returnIntent.putExtra("value", appPackageName)
appUser?.let{ returnIntent.putExtra("user", it) }
returnIntent.putExtra("forGesture", forGesture)
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
activity.finish()
@ -152,9 +155,9 @@ class AppsRecyclerAdapter(val activity: Activity,
init {
// Load the apps
if (appsList.size == 0)
loadApps(activity.packageManager)
loadApps(activity.packageManager, activity)
else {
AsyncTask.execute { loadApps(activity.packageManager) }
AsyncTask.execute { loadApps(activity.packageManager, activity) }
notifyDataSetChanged()
}
@ -193,7 +196,8 @@ class AppsRecyclerAdapter(val activity: Activity,
// modifiable at some later point.
if (appsListDisplayed.size == 1 && intention == ListActivity.ListActivityIntention.VIEW
&& getPreferences(activity).getBoolean(PREF_SEARCH_AUTO_LAUNCH, false)) {
launch(appsListDisplayed[0].packageName.toString(), activity)
val info = appsListDisplayed[0]
launch(info.packageName.toString(), info.user, activity)
val inputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(View(activity).windowToken, 0)

View file

@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import de.jrpie.android.launcher.INVALID_USER
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
import de.jrpie.android.launcher.list.forGesture
@ -56,10 +57,11 @@ class OtherRecyclerAdapter(val activity: Activity):
return ViewHolder(view)
}
private fun returnChoiceIntent(forApp: String, value: String) {
private fun returnChoiceIntent(forGesture: String, value: String) {
val returnIntent = Intent()
returnIntent.putExtra("value", value)
returnIntent.putExtra("forGesture", forApp)
returnIntent.putExtra("forGesture", forGesture)
returnIntent.putExtra("user", INVALID_USER)
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
activity.finish()
}

View file

@ -80,7 +80,8 @@ class ActionsRecyclerAdapter(val activity: Activity):
viewHolder.img
)
fun updateViewHolder() {
val content = gesture.getApp(activity)
val app = gesture.getApp(activity)
val content = app.first
if (content == ""){
viewHolder.img.visibility = View.INVISIBLE
viewHolder.removeAction.visibility = View.GONE
@ -93,7 +94,7 @@ class ActionsRecyclerAdapter(val activity: Activity):
} else {
// Set image icon (by packageName)
try {
viewHolder.img.setImageDrawable(activity.packageManager.getApplicationIcon(content))
viewHolder.img.setImageDrawable(getAppIcon(activity, content, app.second))
} catch (e : Exception) {
// the button is shown, user asked to select an action
viewHolder.img.visibility = View.INVISIBLE