Search code examples
androidsmsdefault

How do I prompt a default sms app chooser to the user in android?


I have created a Messaging App and can be made default from settings. My goal is to prompt a sms app default chooser within my app.


Solution

  • I actually found a way to achieve my goal. For android 10, it's made possible by the RoleManager class:

    RoleManager roleManager;
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             roleManager = getApplicationContext().getSystemService(RoleManager.class);
             if (roleManager.isRoleAvailable(RoleManager.ROLE_SMS)) {
                 if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) {
                     Toast.makeText(getApplicationContext(), "PrismApp set as default.", Toast.LENGTH_SHORT).show();
                     Intent i = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
                     startActivity(i);
                 } else {
                     Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS);
                     startActivityForResult(roleRequestIntent, 2);
                 }
             }
         }
    

    Expected result: android 10

    For android versions prior to android 10, found two methods a simple one and a complex one but worth it. For the simple method:

    String myPackageName = getPackageName();
         Intent setSmsAppIntent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
         setSmsAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, myPackageName);
         startActivity(setSmsAppIntent);
    

    Expected result: simple method

    The second method's:

    private static  final int DEF_SMS_REQ = 0;
     private AppInfo selectedApp;
     private void selectDefaultSmsPackage() {
         @SuppressLint("QueryPermissionsNeeded") final List<ResolveInfo> receivers = getPackageManager().queryBroadcastReceivers(new
                 Intent(Telephony.Sms.Intents.SMS_DELIVER_ACTION), 0);
         final ArrayList<AppInfo> apps = new ArrayList<>();
         for (ResolveInfo info : receivers) {
             final String packageName = info.activityInfo.packageName;
             final String appName = getPackageManager().getApplicationLabel(info.activityInfo.applicationInfo).toString();
             final Drawable icon = getPackageManager().getApplicationIcon(info.activityInfo.applicationInfo);
             apps.add(new AppInfo(packageName, appName, icon));
         }
         apps.sort(new Comparator<AppInfo>() {
             @Override
             public int compare(AppInfo app1, AppInfo app2) {
                 return app1.appName.compareTo(app2.appName);
             }
         });
         new AppsDialog(this, apps).show();
     }
    
     public void onAppSelected(AppInfo selectedApp) {
         this.selectedApp = selectedApp;
         Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
         intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, selectedApp.packageName);
             startActivityForResult(intent, DEF_SMS_REQ);
     }
    
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         if (requestCode == DEF_SMS_REQ) {
             String currentDefault = Telephony.Sms.getDefaultSmsPackage(this);
             boolean isDefault = selectedApp.packageName.equals(currentDefault);
    
             String msg = selectedApp.appName + (isDefault ?
                     " successfully set as default" :
                     " not set as default");
    
             Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
         }
     }
    
     public static class AppInfo {
         String appName;
         String packageName;
         Drawable icon;
    
         public AppInfo(String packageName, String appName, Drawable icon) {
             this.packageName = packageName;
             this.appName = appName;
             this.icon = icon;
         }
    
         @NonNull
         @Override
         public String toString() {
             return appName;
         }
     }
    

    Also your Activity must implement OnAppSelectedListener interface.

    public class MainActivity extends AppCompatActivity implements AppsDialog.OnAppSelectedListener { ... }
    

    Next, create a class AppsDialog that extends Dialog:

    public class AppsDialog extends Dialog implements AdapterView.OnItemClickListener {
    public interface OnAppSelectedListener {
        void onAppSelected(MainActivity.AppInfo selectedApp);
    }
    
    private final Context context;
    private final List<MainActivity.AppInfo> apps;
    
    public AppsDialog(Context context, List<MainActivity.AppInfo> apps) {
        super(context);
    
        if (!(context instanceof OnAppSelectedListener)) {
            throw new IllegalArgumentException(
                    "Activity must implement OnAppSelectedListener interface");
        }
    
        this.context = context;
        this.apps = apps;
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setTitle("Select default SMS app");
    
        final ListView listView = new ListView(context);
        listView.setAdapter(new AppsAdapter(context, apps));
        listView.setOnItemClickListener(this);
        setContentView(listView);
    }
    
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        ((OnAppSelectedListener) context).onAppSelected(apps.get(position));
        dismiss();
    }
    
    private static class AppsAdapter extends ArrayAdapter<MainActivity.AppInfo> {
        public AppsAdapter(Context context, List<MainActivity.AppInfo> list) {
            super(context, R.layout.list_item, R.id.text, list);
        }
    
        public View getView(int position, View convertView, ViewGroup parent) {
            final MainActivity.AppInfo item = getItem(position);
    
            View v = super.getView(position, convertView, parent);
            ((ImageView) v.findViewById(R.id.icon)).setImageDrawable(item.icon);
    
            return v;
        }
    }
    

    }

    And a linear layout(list_item) for the AppsDialog class:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingTop="1dp"
    android:paddingBottom="1dp"
    android:paddingStart="8dp"
    android:paddingEnd="8dp">
    
    <ImageView android:id="@+id/icon"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:contentDescription="Icon Here" />
    
    <TextView android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:ellipsize="marquee" />
    

    That's pretty much it. All this was made possible by:

    For method2, Accepted answer

    For android 10,Accepted answer

    I've mutated my code to fit my needs.