fix: popup menu for work profile apps

This commit is contained in:
Josia Pietsch 2024-08-05 15:08:41 +02:00
parent bf4298ea58
commit 5e841a9106
Signed by: jrpie
GPG key ID: E70B571D66986A2D
6 changed files with 77 additions and 57 deletions

View file

@ -4,6 +4,14 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-08-03T23:51:52.680101889Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/jrpie/.config/.android/avd/Medium_Phone_API_35.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

View file

@ -1,12 +1,15 @@
package de.jrpie.android.launcher package de.jrpie.android.launcher
import android.app.Activity import android.app.Activity
import android.app.ActivityOptions
import android.app.AlertDialog import android.app.AlertDialog
import android.app.Service import android.app.Service
import android.app.role.RoleManager import android.app.role.RoleManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps import android.content.pm.LauncherApps
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.BlendMode import android.graphics.BlendMode
@ -15,12 +18,14 @@ import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter import android.graphics.ColorMatrixColorFilter
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.os.UserHandle
import android.os.UserManager import android.os.UserManager
import android.provider.Settings import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
@ -39,10 +44,12 @@ import android.widget.Toast
import de.jrpie.android.launcher.list.ListActivity import de.jrpie.android.launcher.list.ListActivity
import de.jrpie.android.launcher.list.apps.AppInfo import de.jrpie.android.launcher.list.apps.AppInfo
import de.jrpie.android.launcher.list.apps.AppsRecyclerAdapter import de.jrpie.android.launcher.list.apps.AppsRecyclerAdapter
import de.jrpie.android.launcher.list.intendedChoosePause
import de.jrpie.android.launcher.list.other.LauncherAction import de.jrpie.android.launcher.list.other.LauncherAction
import de.jrpie.android.launcher.settings.SettingsActivity import de.jrpie.android.launcher.settings.SettingsActivity
import de.jrpie.android.launcher.settings.intendedSettingsPause import de.jrpie.android.launcher.settings.intendedSettingsPause
import de.jrpie.android.launcher.tutorial.TutorialActivity import de.jrpie.android.launcher.tutorial.TutorialActivity
import kotlin.contracts.contract
/* Preference Key Constants */ /* Preference Key Constants */
@ -220,17 +227,37 @@ fun audioVolumeDown(activity: Activity) {
/* --- */ /* --- */
fun launchApp(packageName: String, user: Int?, context: Context) { fun getUserFromId(user: Int?, context: Context): UserHandle? {
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager
return userManager.userProfiles.firstOrNull { it.hashCode() == user }
}
fun getLauncherActivityInfo(packageName: String, user: Int?, context: Context): LauncherActivityInfo? {
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
return getUserFromId(user,context)?.let {
userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()
}
}
fun uninstallApp(packageName: String, user: Int?, activity: Activity) {
Log.i("Launcher", "uninstalling $packageName ($user)")
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE)
intent.data = Uri.parse("package:$packageName")
getUserFromId(user, activity)?.let {
user -> intent.putExtra(Intent.EXTRA_USER, user)
}
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
activity.startActivityForResult(intent,
REQUEST_UNINSTALL
)
}
fun launchApp(packageName: String, user: Int?, context: Context, rect: Rect? = null) {
Log.i("Launcher", "Starting: " + packageName + " (user " +user.toString()+ ")") Log.i("Launcher", "Starting: " + packageName + " (user " +user.toString()+ ")")
if (user != null) { if (user != null) {
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager getLauncherActivityInfo(packageName,user,context)?.let {
userManager.userProfiles.firstOrNull { it.hashCode() == user }?.let { app -> launcherApps.startMainActivity(app.componentName, app.user, rect, null)
userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()?.let { return
app -> launcherApps.startMainActivity(app.componentName, userHandle, null, null)
return
}
} }
} }
@ -251,6 +278,7 @@ fun launchApp(packageName: String, user: Int?, context: Context) {
) { _, _ -> ) { _, _ ->
openAppSettings( openAppSettings(
packageName, packageName,
user,
context context
) )
} }
@ -322,10 +350,11 @@ fun resetToDarkTheme(activity: Activity) {
} }
fun openAppSettings(pkg: String, context: Context) { fun openAppSettings(packageName: String, user: Int?, context: Context, sourceBounds: Rect? = null, opts: Bundle? = null) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
intent.data = Uri.parse("package:$pkg") getLauncherActivityInfo(packageName, user, context)?.let {
context.startActivity(intent) app -> launcherApps.startAppDetailsActivity(app.componentName, app.user, sourceBounds, opts)
}
} }
fun openSettings(activity: Activity) { fun openSettings(activity: Activity) {
@ -346,8 +375,7 @@ fun openAppsList(activity: Activity){
fun getAppIcon(context: Context, packageName: String, user: Int?): Drawable { fun getAppIcon(context: Context, packageName: String, user: Int?): Drawable {
if (user != null) { if (user != null) {
val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps val launcherApps = context.getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val userManager = context.getSystemService(Service.USER_SERVICE) as UserManager getUserFromId(user,context)?.let {
userManager.userProfiles.firstOrNull { it.hashCode() == user }?.let {
userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()?.let { userHandle -> launcherApps.getActivityList(packageName, userHandle).firstOrNull()?.let {
app -> return app.getBadgedIcon(0) app -> return app.getBadgedIcon(0)
} }

View file

@ -3,6 +3,7 @@ package de.jrpie.android.launcher.list.apps
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.AsyncTask import android.os.AsyncTask
import android.view.LayoutInflater import android.view.LayoutInflater
@ -27,6 +28,7 @@ import de.jrpie.android.launcher.list.intendedChoosePause
import de.jrpie.android.launcher.loadApps import de.jrpie.android.launcher.loadApps
import de.jrpie.android.launcher.openAppSettings import de.jrpie.android.launcher.openAppSettings
import de.jrpie.android.launcher.transformGrayscale import de.jrpie.android.launcher.transformGrayscale
import de.jrpie.android.launcher.uninstallApp
import java.util.* import java.util.*
/** /**
@ -58,8 +60,9 @@ class AppsRecyclerAdapter(val activity: Activity,
val appUser = appsListDisplayed[pos].user val appUser = appsListDisplayed[pos].user
when (intention){ when (intention){
ListActivity.ListActivityIntention.VIEW -> { ListActivity.ListActivityIntention.VIEW -> {
launchApp(appPackageName, appUser, activity) val rect = Rect()
img.getGlobalVisibleRect(rect)
launchApp(appPackageName, appUser, activity, rect)
} }
ListActivity.ListActivityIntention.PICK -> { ListActivity.ListActivityIntention.PICK -> {
val returnIntent = Intent() val returnIntent = Intent()
@ -78,6 +81,7 @@ class AppsRecyclerAdapter(val activity: Activity,
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val appLabel = appsListDisplayed[i].label.toString() val appLabel = appsListDisplayed[i].label.toString()
val appPackageName = appsListDisplayed[i].packageName.toString() val appPackageName = appsListDisplayed[i].packageName.toString()
val appUser = appsListDisplayed[i].user
val appIcon = appsListDisplayed[i].icon val appIcon = appsListDisplayed[i].icon
val isSystemApp = appsListDisplayed[i].isSystemApp val isSystemApp = appsListDisplayed[i].isSystemApp
@ -95,10 +99,10 @@ class AppsRecyclerAdapter(val activity: Activity,
else { else {
viewHolder.menuDots.visibility = View.VISIBLE viewHolder.menuDots.visibility = View.VISIBLE
viewHolder.menuDots.setOnClickListener{ showOptionsPopup(viewHolder, appPackageName) } viewHolder.menuDots.setOnClickListener{ showOptionsPopup(viewHolder, appPackageName, appUser) }
viewHolder.menuDots.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName) } viewHolder.menuDots.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName, appUser) }
viewHolder.textView.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName) } viewHolder.textView.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName, appUser) }
viewHolder.img.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName) } viewHolder.img.setOnLongClickListener{ showOptionsPopup(viewHolder, appPackageName, appUser) }
// ensure onClicks are actually caught // ensure onClicks are actually caught
viewHolder.textView.setOnClickListener{ viewHolder.onClick(viewHolder.textView) } viewHolder.textView.setOnClickListener{ viewHolder.onClick(viewHolder.textView) }
@ -106,9 +110,8 @@ class AppsRecyclerAdapter(val activity: Activity,
} }
} }
// TODO fixme: handle work profile apps
@Suppress("SameReturnValue") @Suppress("SameReturnValue")
private fun showOptionsPopup(viewHolder: ViewHolder, appPackageName: String): Boolean { private fun showOptionsPopup(viewHolder: ViewHolder, appPackageName: String, user: Int?): Boolean {
//create the popup menu //create the popup menu
val popup = PopupMenu(activity, viewHolder.menuDots) val popup = PopupMenu(activity, viewHolder.menuDots)
@ -116,23 +119,14 @@ class AppsRecyclerAdapter(val activity: Activity,
popup.setOnMenuItemClickListener { popup.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.app_menu_delete -> { // delete R.id.app_menu_delete -> {
intendedChoosePause = true intendedChoosePause = true
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE) uninstallApp(appPackageName, user, activity)
intent.data = Uri.parse("package:$appPackageName")
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
activity.startActivityForResult(intent,
REQUEST_UNINSTALL
)
true true
} }
R.id.app_menu_info -> { // open app settings R.id.app_menu_info -> {
intendedChoosePause = true intendedChoosePause = true
openAppSettings( openAppSettings(appPackageName, user, activity)
appPackageName,
activity
)
true true
} }
else -> false else -> false

View file

@ -2,7 +2,6 @@ package de.jrpie.android.launcher.settings.actions
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
@ -11,10 +10,14 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import de.jrpie.android.launcher.* import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.UIObject
import de.jrpie.android.launcher.databinding.SettingsActionsBinding import de.jrpie.android.launcher.databinding.SettingsActionsBinding
import de.jrpie.android.launcher.getPreferences
import de.jrpie.android.launcher.list.ListActivity import de.jrpie.android.launcher.list.ListActivity
import de.jrpie.android.launcher.setButtonColor
import de.jrpie.android.launcher.settings.intendedSettingsPause import de.jrpie.android.launcher.settings.intendedSettingsPause
import de.jrpie.android.launcher.vibrantColor
/** /**
@ -25,7 +28,8 @@ import de.jrpie.android.launcher.settings.intendedSettingsPause
* It also allows the user to view all apps ([ListActivity]) or install new ones. * It also allows the user to view all apps ([ListActivity]) or install new ones.
*/ */
class SettingsFragmentActions : Fragment(), UIObject { class
SettingsFragmentActions : Fragment(), UIObject {
private var binding: SettingsActionsBinding? = null private var binding: SettingsActionsBinding? = null

View file

@ -50,7 +50,7 @@ class SettingsFragmentMeta : Fragment(), UIObject {
private fun rateIntentForUrl(url: String): Intent { private fun rateIntentForUrl(url: String): Intent {
val intent = Intent( val intent = Intent(
Intent.ACTION_VIEW, Intent.ACTION_VIEW,
Uri.parse(String.format("%s?id=%s", url, this.context!!.packageName)) Uri.parse(String.format("%s?id=%s", url, this.requireContext().packageName))
) )
var flags = Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_MULTIPLE_TASK var flags = Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
flags = flags or Intent.FLAG_ACTIVITY_NEW_DOCUMENT flags = flags or Intent.FLAG_ACTIVITY_NEW_DOCUMENT
@ -59,7 +59,6 @@ class SettingsFragmentMeta : Fragment(), UIObject {
} }
override fun applyTheme() { override fun applyTheme() {
setButtonColor(binding.settingsMetaButtonSelectLauncher, vibrantColor)
setButtonColor(binding.settingsMetaButtonViewTutorial, vibrantColor) setButtonColor(binding.settingsMetaButtonViewTutorial, vibrantColor)
setButtonColor(binding.settingsMetaButtonResetSettings, vibrantColor) setButtonColor(binding.settingsMetaButtonResetSettings, vibrantColor)
setButtonColor(binding.settingsMetaButtonReportBug, vibrantColor) setButtonColor(binding.settingsMetaButtonReportBug, vibrantColor)
@ -70,12 +69,6 @@ class SettingsFragmentMeta : Fragment(), UIObject {
override fun setOnClicks() { override fun setOnClicks() {
binding.settingsMetaButtonSelectLauncher.setOnClickListener {
intendedSettingsPause = true
val callHomeSettingIntent = Intent(Settings.ACTION_HOME_SETTINGS)
startActivity(callHomeSettingIntent)
}
binding.settingsMetaButtonViewTutorial.setOnClickListener { binding.settingsMetaButtonViewTutorial.setOnClickListener {
intendedSettingsPause = true intendedSettingsPause = true
startActivity(Intent(this.context, TutorialActivity::class.java)) startActivity(Intent(this.context, TutorialActivity::class.java))
@ -102,7 +95,7 @@ class SettingsFragmentMeta : Fragment(), UIObject {
intendedSettingsPause = true intendedSettingsPause = true
openNewTabWindow( openNewTabWindow(
getString(R.string.settings_meta_report_bug_link), getString(R.string.settings_meta_report_bug_link),
context!! requireContext()
) )
} }
@ -113,7 +106,7 @@ class SettingsFragmentMeta : Fragment(), UIObject {
intendedSettingsPause = true intendedSettingsPause = true
openNewTabWindow( openNewTabWindow(
getString(R.string.settings_meta_contact_url), getString(R.string.settings_meta_contact_url),
context!! requireContext()
) )
} }
@ -122,7 +115,7 @@ class SettingsFragmentMeta : Fragment(), UIObject {
intendedSettingsPause = true intendedSettingsPause = true
openNewTabWindow( openNewTabWindow(
getString(R.string.settings_meta_fork_contact_url), getString(R.string.settings_meta_fork_contact_url),
context!! requireContext()
) )
} }
@ -131,7 +124,7 @@ class SettingsFragmentMeta : Fragment(), UIObject {
intendedSettingsPause = true intendedSettingsPause = true
openNewTabWindow( openNewTabWindow(
getString(R.string.settings_meta_privacy_url), getString(R.string.settings_meta_privacy_url),
context!! requireContext()
) )
} }

View file

@ -13,13 +13,6 @@
android:paddingRight="32sp" android:paddingRight="32sp"
tools:context=".settings.meta.SettingsFragmentMeta"> tools:context=".settings.meta.SettingsFragmentMeta">
<Button
android:id="@+id/settings_meta_button_select_launcher"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_meta_select_launcher"
android:textAllCaps="false" />
<Button <Button
android:id="@+id/settings_meta_button_view_tutorial" android:id="@+id/settings_meta_button_view_tutorial"
android:layout_width="match_parent" android:layout_width="match_parent"