mirror of
https://github.com/jrpie/Launcher.git
synced 2025-02-23 06:21:31 +01:00
feature: work profile
This commit is contained in:
parent
7a60611ec5
commit
500062b29b
9 changed files with 100 additions and 29 deletions
|
@ -11,6 +11,7 @@ This is a fork of [finnmglas's app Launcher][original-repo].
|
||||||
## Notable changes:
|
## Notable changes:
|
||||||
|
|
||||||
* Edge gestures: There is a setting to allow distinguishing swiping at the edges of the screen from swiping in the center.
|
* 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
|
### Visual
|
||||||
* This app uses the system wallpaper instead of a custom solution.
|
* This app uses the system wallpaper instead of a custom solution.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
||||||
|
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
|
|
|
@ -67,6 +67,8 @@ const val PREF_STARTED_TIME = "firstStartup"
|
||||||
|
|
||||||
const val PREF_VERSION = "version"
|
const val PREF_VERSION = "version"
|
||||||
|
|
||||||
|
const val INVALID_USER = -1
|
||||||
|
|
||||||
/* Objects used by multiple activities */
|
/* Objects used by multiple activities */
|
||||||
val appsList: MutableList<AppInfo> = ArrayList()
|
val appsList: MutableList<AppInfo> = ArrayList()
|
||||||
|
|
||||||
|
@ -153,14 +155,15 @@ private fun getIntent(packageName: String, context: Context): Intent? {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launch(
|
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
|
animationIn: Int = android.R.anim.fade_in, animationOut: Int = android.R.anim.fade_out
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (LauncherAction.isOtherAction(data)) { // [type]:[info]
|
if (LauncherAction.isOtherAction(data)) { // [type]:[info]
|
||||||
LauncherAction.byId(data)?.let {it.launch(activity) }
|
LauncherAction.byId(data)?.let {it.launch(activity) }
|
||||||
}
|
}
|
||||||
else launchApp(data, activity) // app
|
else launchApp(data, user, activity) // app
|
||||||
|
|
||||||
activity.overridePendingTransition(animationIn, animationOut)
|
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)
|
val intent = getIntent(packageName, context)
|
||||||
|
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
|
@ -327,13 +343,46 @@ fun openAppsList(activity: Activity){
|
||||||
activity.startActivity(intent)
|
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,
|
* [loadApps] is used to speed up the [AppsRecyclerAdapter] loading time,
|
||||||
* as it caches all the apps and allows for fast access to the data.
|
* 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 loadList = mutableListOf<AppInfo>()
|
||||||
|
|
||||||
|
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)
|
val i = Intent(Intent.ACTION_MAIN, null)
|
||||||
i.addCategory(Intent.CATEGORY_LAUNCHER)
|
i.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
val allApps = packageManager.queryIntentActivities(i, 0)
|
val allApps = packageManager.queryIntentActivities(i, 0)
|
||||||
|
@ -344,8 +393,7 @@ fun loadApps(packageManager: PackageManager) {
|
||||||
app.icon = ri.activityInfo.loadIcon(packageManager)
|
app.icon = ri.activityInfo.loadIcon(packageManager)
|
||||||
loadList.add(app)
|
loadList.add(app)
|
||||||
}
|
}
|
||||||
loadList.sortBy { it.label.toString() }
|
}
|
||||||
|
|
||||||
appsList.clear()
|
appsList.clear()
|
||||||
appsList.addAll(loadList)
|
appsList.addAll(loadList)
|
||||||
}
|
}
|
||||||
|
@ -404,9 +452,12 @@ fun setWindowFlags(window: Window) {
|
||||||
// Used in Tutorial and Settings `ActivityOnResult`
|
// Used in Tutorial and Settings `ActivityOnResult`
|
||||||
fun saveListActivityChoice(context: Context, data: Intent?) {
|
fun saveListActivityChoice(context: Context, data: Intent?) {
|
||||||
val value = data?.getStringExtra("value")
|
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
|
val forGesture = data?.getStringExtra("forGesture") ?: return
|
||||||
|
|
||||||
Gesture.byId(forGesture)?.setApp(context, value.toString())
|
Gesture.byId(forGesture)?.setApp(context, value.toString(), user)
|
||||||
|
|
||||||
loadSettings(context)
|
loadSettings(context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,12 @@ enum class Gesture (val id: String, private val labelResource: Int,
|
||||||
TOP, BOTTOM, LEFT, RIGHT
|
TOP, BOTTOM, LEFT, RIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getApp(context: Context): String {
|
fun getApp(context: Context): Pair<String, Int?> {
|
||||||
return getPreferences(context).getString(this.id, "")!!
|
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) {
|
fun removeApp(context: Context) {
|
||||||
|
@ -51,10 +55,15 @@ enum class Gesture (val id: String, private val labelResource: Int,
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setApp(context: Context, app: String) {
|
fun setApp(context: Context, app: String, user: Int?) {
|
||||||
getPreferences(context).edit()
|
getPreferences(context).edit()
|
||||||
.putString(this.id, app)
|
.putString(this.id, app)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
|
val u = user?: INVALID_USER
|
||||||
|
getPreferences(context).edit()
|
||||||
|
.putInt(this.id + "_user", u)
|
||||||
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLabel(context: Context): String {
|
fun getLabel(context: Context): String {
|
||||||
|
@ -132,7 +141,8 @@ enum class Gesture (val id: String, private val labelResource: Int,
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(activity: Activity) {
|
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 {
|
companion object {
|
||||||
|
|
|
@ -87,7 +87,7 @@ class HomeActivity: UIObject, AppCompatActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload apps to speed up the Apps Recycler
|
// Preload apps to speed up the Apps Recycler
|
||||||
AsyncTask.execute { loadApps(packageManager) }
|
AsyncTask.execute { loadApps(packageManager, applicationContext) }
|
||||||
|
|
||||||
// Initialise layout
|
// Initialise layout
|
||||||
binding = HomeBinding.inflate(layoutInflater)
|
binding = HomeBinding.inflate(layoutInflater)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.graphics.drawable.Drawable
|
||||||
* Represents an app installed on the users device.
|
* Represents an app installed on the users device.
|
||||||
*/
|
*/
|
||||||
class AppInfo {
|
class AppInfo {
|
||||||
|
var user: Int? = null
|
||||||
var label: CharSequence? = null
|
var label: CharSequence? = null
|
||||||
var packageName: CharSequence? = null
|
var packageName: CharSequence? = null
|
||||||
var icon: Drawable? = null
|
var icon: Drawable? = null
|
||||||
|
|
|
@ -21,6 +21,7 @@ import de.jrpie.android.launcher.appsList
|
||||||
import de.jrpie.android.launcher.getPreferences
|
import de.jrpie.android.launcher.getPreferences
|
||||||
import de.jrpie.android.launcher.getSavedTheme
|
import de.jrpie.android.launcher.getSavedTheme
|
||||||
import de.jrpie.android.launcher.launch
|
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.ListActivity
|
||||||
import de.jrpie.android.launcher.list.intendedChoosePause
|
import de.jrpie.android.launcher.list.intendedChoosePause
|
||||||
import de.jrpie.android.launcher.loadApps
|
import de.jrpie.android.launcher.loadApps
|
||||||
|
@ -54,9 +55,10 @@ class AppsRecyclerAdapter(val activity: Activity,
|
||||||
val pos = adapterPosition
|
val pos = adapterPosition
|
||||||
val context: Context = v.context
|
val context: Context = v.context
|
||||||
val appPackageName = appsListDisplayed[pos].packageName.toString()
|
val appPackageName = appsListDisplayed[pos].packageName.toString()
|
||||||
|
val appUser = appsListDisplayed[pos].user
|
||||||
when (intention){
|
when (intention){
|
||||||
ListActivity.ListActivityIntention.VIEW -> {
|
ListActivity.ListActivityIntention.VIEW -> {
|
||||||
|
launchApp(appPackageName, appUser, activity)
|
||||||
val launchIntent: Intent = context.packageManager
|
val launchIntent: Intent = context.packageManager
|
||||||
.getLaunchIntentForPackage(appPackageName)!!
|
.getLaunchIntentForPackage(appPackageName)!!
|
||||||
context.startActivity(launchIntent)
|
context.startActivity(launchIntent)
|
||||||
|
@ -64,6 +66,7 @@ class AppsRecyclerAdapter(val activity: Activity,
|
||||||
ListActivity.ListActivityIntention.PICK -> {
|
ListActivity.ListActivityIntention.PICK -> {
|
||||||
val returnIntent = Intent()
|
val returnIntent = Intent()
|
||||||
returnIntent.putExtra("value", appPackageName)
|
returnIntent.putExtra("value", appPackageName)
|
||||||
|
appUser?.let{ returnIntent.putExtra("user", it) }
|
||||||
returnIntent.putExtra("forGesture", forGesture)
|
returnIntent.putExtra("forGesture", forGesture)
|
||||||
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
|
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
|
||||||
activity.finish()
|
activity.finish()
|
||||||
|
@ -152,9 +155,9 @@ class AppsRecyclerAdapter(val activity: Activity,
|
||||||
init {
|
init {
|
||||||
// Load the apps
|
// Load the apps
|
||||||
if (appsList.size == 0)
|
if (appsList.size == 0)
|
||||||
loadApps(activity.packageManager)
|
loadApps(activity.packageManager, activity)
|
||||||
else {
|
else {
|
||||||
AsyncTask.execute { loadApps(activity.packageManager) }
|
AsyncTask.execute { loadApps(activity.packageManager, activity) }
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +196,8 @@ class AppsRecyclerAdapter(val activity: Activity,
|
||||||
// modifiable at some later point.
|
// modifiable at some later point.
|
||||||
if (appsListDisplayed.size == 1 && intention == ListActivity.ListActivityIntention.VIEW
|
if (appsListDisplayed.size == 1 && intention == ListActivity.ListActivityIntention.VIEW
|
||||||
&& getPreferences(activity).getBoolean(PREF_SEARCH_AUTO_LAUNCH, false)) {
|
&& 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
|
val inputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
inputMethodManager.hideSoftInputFromWindow(View(activity).windowToken, 0)
|
inputMethodManager.hideSoftInputFromWindow(View(activity).windowToken, 0)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import de.jrpie.android.launcher.INVALID_USER
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
|
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
|
||||||
import de.jrpie.android.launcher.list.forGesture
|
import de.jrpie.android.launcher.list.forGesture
|
||||||
|
@ -56,10 +57,11 @@ class OtherRecyclerAdapter(val activity: Activity):
|
||||||
return ViewHolder(view)
|
return ViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun returnChoiceIntent(forApp: String, value: String) {
|
private fun returnChoiceIntent(forGesture: String, value: String) {
|
||||||
val returnIntent = Intent()
|
val returnIntent = Intent()
|
||||||
returnIntent.putExtra("value", value)
|
returnIntent.putExtra("value", value)
|
||||||
returnIntent.putExtra("forGesture", forApp)
|
returnIntent.putExtra("forGesture", forGesture)
|
||||||
|
returnIntent.putExtra("user", INVALID_USER)
|
||||||
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
|
activity.setResult(REQUEST_CHOOSE_APP, returnIntent)
|
||||||
activity.finish()
|
activity.finish()
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,8 @@ class ActionsRecyclerAdapter(val activity: Activity):
|
||||||
viewHolder.img
|
viewHolder.img
|
||||||
)
|
)
|
||||||
fun updateViewHolder() {
|
fun updateViewHolder() {
|
||||||
val content = gesture.getApp(activity)
|
val app = gesture.getApp(activity)
|
||||||
|
val content = app.first
|
||||||
if (content == ""){
|
if (content == ""){
|
||||||
viewHolder.img.visibility = View.INVISIBLE
|
viewHolder.img.visibility = View.INVISIBLE
|
||||||
viewHolder.removeAction.visibility = View.GONE
|
viewHolder.removeAction.visibility = View.GONE
|
||||||
|
@ -93,7 +94,7 @@ class ActionsRecyclerAdapter(val activity: Activity):
|
||||||
} else {
|
} else {
|
||||||
// Set image icon (by packageName)
|
// Set image icon (by packageName)
|
||||||
try {
|
try {
|
||||||
viewHolder.img.setImageDrawable(activity.packageManager.getApplicationIcon(content))
|
viewHolder.img.setImageDrawable(getAppIcon(activity, content, app.second))
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
// the button is shown, user asked to select an action
|
// the button is shown, user asked to select an action
|
||||||
viewHolder.img.visibility = View.INVISIBLE
|
viewHolder.img.visibility = View.INVISIBLE
|
||||||
|
|
Loading…
Add table
Reference in a new issue