We will explain in few easy steps how to keep your app’s icon hidden in the main menu, and how to open it only by dialing a specific code in the default android dialer. You can also let the user specify his own dialer number to open your app.

Intro

Sometimes you need to keep your app’s icon hidden from the main menu (not the apps list in settings) and only let the user to open its launcher activity (or any other specified activity) using the dialer by calling a specific code. This is something like the case when some users using the dialer codes to get some information about their device.

So in this tutorial we will create a simple app that has a hidden icon and can be accessed only by calling a dialer code, and you can go with creating a shared preference to let the user him/herself set this code.

Set up a Broadcastreciever

We will watch the outgoing called numbers in the dialer and start our MainActivity.java when our chosen code is dialed.

  1. We will create a new android project with an empty launcher activity (MainActivity.java) and its layout will only contains a welcome message.
  2. We create MyBroadCastReciever.java class the extends BroadCastReciever and will have an intent to open the MainAcitivty.java if the dialed code matches our one:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class MyBroadCastReciever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        String ourCode = "*1234#";
        String dialedNumber = getResultData();

        if (dialedNumber.equals(ourCode)){

            // My app will bring up, so cancel the dialer broadcast
            setResultData(null);

            //Intent to launch MainActivity
            Intent intent_to_mainActivity = new Intent(context, MainActivity.class);
            context.startActivity(intent_to_mainActivity);

        }
    }
}
  1. We announce our receiver in the application tag in AndroidManifest.xml

  2. And we need to use PROCESS_OUTGOING_CALLS permission:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="me.jagar.myspyapp">

    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".MyBroadCastReciever">
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
    </application>

</manifest>
  1. We also need to check the permission programmatically so we will check if the permission is granted in onCreate()
1
2
3
4
5
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.PROCESS_OUTGOING_CALLS)
!= PackageManager.PERMISSION_GRANTED){
    ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.PROCESS_OUTGOING_CALLS}, permission_calls);
}else{
}
  1. Then check the results of the permission request:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == permission_calls){
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
        }else{
            Toast.makeText(MainActivity.this, "Premission needed", Toast.LENGTH_LONG)
                    .show();
        }
    }
}

Hide the android app icon

  1. In this example I will hide the icon in onStop() of the MainActivity.java:
1
2
3
PackageManager packageManager = getPackageManager();
ComponentName componentName = new ComponentName(MainActivity.this, me.jagar.myspyapp.MainActivity.class);
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
  1. And I will unhide it before the intent in the MyBroadCastReciever otherwise we will get AppNotFoundException if we opened the activity without unhiding the app icon.
1
2
3
PackageManager packageManager = getPackageManager();
ComponentName componentName = new ComponentName(context, me.jagar.myspyapp.MainActivity.class); 
packageManager.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
  1. (Optionally) we can create a method that check if the icon is visible or not, and it could be useful in more complicated implementations:
1
2
3
4
5
private boolean isLauncherIconVisible() {
    ComponentName componentName = new ComponentName(context, me.jagar.myspyapp.MainActivity.class);
    int enabledSetting = getPackageManager().getComponentEnabledSetting(componentName);
    return enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}

Result

Notes

  • Hiding the icon in onStop() is really not a good idea and this should be customized depending on your app’s approach.
  • Hiding the icon is problematic in the debugging because android studio gives NotFoundException when the app icon is hided.