13

I tried to implement a functionality that allows user to pick default Android's default Launcher Application. Also, I need to receive information which application has been chosen. But there is a problem with that approach.

To let user pick Launcher Application, we can simply start given intent:

val selector = Intent(Intent.ACTION_MAIN)
selector.addCategory(Intent.CATEGORY_HOME)
selector.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(selector)

It results in such dialog:

enter image description here

What I observed, if I use startActivity, the Launcher Application is set nicely and works as intended, but if I use startActivityForResult, then I will get some callback, but the Launcher Application will not be set at all. Also, there was nothing interesting in intent received in onActivityResult.

Then, I tried using IntentSender instead.

The code looks as follows:

val selector = Intent(Intent.ACTION_MAIN)
selector.addCategory(Intent.CATEGORY_HOME)
selector.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val receiver = Intent(this, MyBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(this, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT)
val chooser = Intent.createChooser(selector, "Select a Home app", pendingIntent.intentSender);
startActivity(chooser)

The receiver looks as follows:

class MyBroadcastReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val componentName = intent.extras.getParcelable<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)
        //com.example.myapp if my app was choosen
        val pkg = componentName.packageName
    }
}

This results in default chooser, without options "JUST ONCE" or "ALWAYS". I don't have exact picture, but it looks similar to this one:

enter image description here

This works in a way, in the receiver's onReceive method I get ComponenName object, that holds selected app packageName. The problem is - again - Launcher Application is not set!

So question is: How can I let user set Launcher Application, and also receive information which one has he chosen?

xinaiz
  • 6,915
  • 3
  • 26
  • 67

2 Answers2

7

Try using the following code:

PackageManager localPackageManager = getPackageManager();
Intent intent = new Intent("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
String launcherName = localPackageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
Log.e("Current launcher Package Name:",launcherName);
aanshu
  • 1,502
  • 10
  • 12
  • Where should I use it? – xinaiz Oct 29 '18 at 08:53
  • You can use it in onResume method, from where you are launching this intent – aanshu Oct 29 '18 at 08:57
  • But I need user to pick app first, if I call it right after creating chooser, this will be old home application info – xinaiz Oct 29 '18 at 08:59
  • have you tried onResume method? I believe it will work as you will be changing the launcher through another application once the launcher is changed it will come back to our application and will call onResume method of the activity from where you launched the intent. Correct / explain me if I am not understanding your pain. – aanshu Oct 29 '18 at 09:08
  • I get `Current launcher Package Name: android`, no matter if my app is set as Home or build-in application – xinaiz Oct 29 '18 at 09:16
  • are you testing it on emulator? – aanshu Oct 29 '18 at 09:20
  • If there is no selected "default" launcher it returns just "android". Not a common case, but it happens. – aanshu Oct 29 '18 at 09:21
  • Yes I'm testing it on emulator – xinaiz Oct 29 '18 at 09:27
  • test it on actual device & note it will return "android" for default launcher. – aanshu Oct 29 '18 at 09:29
  • Somewhat, but unfortunately onResume method is not called when I choose different application than current one – xinaiz Oct 29 '18 at 10:11
  • use startActivityForResult then – aanshu Oct 29 '18 at 10:15
  • Please let me know if it worked? If not would love to help with alternatives – aanshu Nov 03 '18 at 18:52
  • 2
    I ended up with declaring my application as `android:launchMode="singleTask"`. This way, I can detect the the change between my app and another app - If i choose my app as launcher, app will restart because of `singleTask`, and in `onResume` I can detect with your method (or any method at this point) if my app is launcher. Similarly, when my app *was* home app, and I choose another app, my app will close because the home app changes. When I open app again (via icon), again, in `onResume` I check if my app is launcher. It's far from perfect to what I wanted, but I ended up with such solution. – xinaiz Nov 03 '18 at 19:14
  • Will that be possible for you if you could edit the answer & mark as an answer with the proper solution you found to organize the post. – aanshu Nov 04 '18 at 07:39
  • I'm afraid my implementation is not a solution to the question. Original question required to listen to the change after it occurred, and that's not the case in what I described in my previous comment. It's only a workaround that just fit in what I had to implement. – xinaiz Nov 04 '18 at 09:32
6

With getPreferredActivities() you can retrieve all activities which are preferred by the user. This should include the launcher as well.

Then you could try to implement a getPreferredLauncher() function to get the current Launcher. But since there's no way to listen for this change, you'd have to query it proactively within a Service or whenever you'd assume the data could have changed:

fun PackageManager.getPreferredLauncher(): ComponentName? {
    val filters = mutableListOf<IntentFilter>()
    val components = mutableListOf<ComponentName>()
    getPreferredActivities(filters, components, null)
    filters.forEachIndexed { (i, it) ->
        if (it.hasAction(ACTION_MAIN) && it.hasCategory(CATEGORY_LAUNCHER))
            return@getPreferredLauncher components[i]
    }
    return null
}

Please consider this code a draft only, as I didn't have any setup to actually run it.

tynn
  • 33,607
  • 8
  • 80
  • 121
  • Thanks for your answer. It works as intended, but the problem is how to listen to actual change. – xinaiz Nov 04 '18 at 09:44
  • Like I said, there's no way to acutally listen for this information. So you have to query it occasionally from a service or within your activity. – tynn Nov 04 '18 at 13:02