Samsung Dex Applications

Overview

Samsung DeX extends the functionality of a mobile device into a desktop environment. One of the advantages it offers is that it does not require any SDKs to launch apps on DeX Mode. However, the apps must support mouse functionality and multi-density.

For better user experience in DeX mode, apps must adhere to a number of Android best practices especially on multi-window feature support. DeX mode includes all of the advanced multitasking features built in the Samsung DeX UI.

In this Code Lab, you can learn how to optimize your apps for next-gen Samsung devices with enhanced interaction, desktop mode capability, and an overall amplified in-app experience. Moreover, you can experience the new dual mode which allows you to use DeX mode while using your phone simultaneously.

Objective

  • Optimize a sample application and its input capabilities with Samsung DeX.

  • Provide features using Samsung DeX Dual mode.

Setup

The following software and tools are needed to optimize apps for Samsung DeX:

  • Android Studio for PC or Android IDE for mobile with Android Developer Tools

  • Android SDK 24 or higher

  • Samsung Galaxy flagship device (S8, S8+, S9, S9+, Note8, Note9 and Tab S4) that supports Samsung DeX

  • External monitor

  • Samsung DeX Accessory: DeX Station, DeX Pad or HDMI adaptor

  • Keyboard and Mouse connected to the mobile device via Bluetooth (or hardwired to the DeX Station (Pad) via USB port)

  • A Sample/Pre-built application

Application Development

1. Prepare the Sample App to be Optimized for Samsung DeX

You will be provided with a sample app that has the following features:

  1. a) Simple drawing function

  2. b) A floating button to open an e-mail app

  3. c) 5 buttons for various features

    • Add window on the other display that works in DeX mode

    • Change the brush size

    • Undo

    • Redo

    • Erase all drawing

Figure 1 Sample App

Figure 1 Sample App

2. Check the Default Mandatory Requirements

In order for the app functionality to work in Samsung DeX, you need to check these requirements:

  1. a) Apps must support Multi-Density for xxxhdpi (640 dpi) and mdpi (160 dpi).

  2. b) Apps must support mouse or trackpad interaction.

These are important in DeX mode since it only supports mdpi as a density. If apps block fake touch input in DeX mode while allowing touch input, you may not use apps with a mouse or touchpad. It is a best practice to check these requirements even though most of the available apps now can run on Samsung DeX in fixed-sized multi-window without making any code changes to apps.

Figure 2 Items to be checked in manifest

Figure 2 Items to be checked in manifest

3. Enable Support for Freeform on Multi-window for Apps

By default, apps run in multi-window mode. However, multi-window mode has a fixed-size. If an app supports a freeform multi-window functionality, it would provide you a desktop experience where you can resize windows.

Follow these steps to enable support for freeform:

  1. a) Apps should target at least SDK 24 or higher. Always check if the targetSdkVersion is set to 24 or higher at build.gradle file.

  2. b) Explicitly declare resizeableActivity to True in the manifest to run in a resizable window. After declaring and enabling resizeableActivity, the sample app can now run in a resizable window mode.

    <application
    ...
    <activity
    ...
    android:resizeableActivity="true"
    >
    ...
    </activity>
    </application>
    

4. Handling Runtime Configuration Changes

The switch between mobile phone and Samsung DeX mode causes runtime configuration changes, which happens similarly when changing the orientation from portrait to landscape display. This may result to force restart an application when switching between modes.

You need to follow the Android's guide for handling configuration changes and the best practices on building a responsive design to avoid any runtime configuration issues.

  1. a) To modify, add android:configChanges flag to activity in Manifest

    <application
    …
    <activity
    ...
        <!-- DeX TODO The android:configChanges="colorMode|orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden|keyboard"this used if you don't want android to kill and restart you activity when window is resized -->
    android:configChanges="colorMode|orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden|keyboard"
    >
           ...
        </activity>
    </application>
    

5. Set Additional Layout Options and Meta-data in the Manifest

In the manifest file, you can set the minimum window size and launch size of DeX mode. Moreover, you can set the app to be always active when switching from phone to DeX mode or vice versa to avoid restarting the app.

  1. a) To set the minimum window size when in DeX mode, follow the code snippet below.

    <application
    ...
    <activity
    ...
    <!--Layout minimum size set in DeX mode -->
    <layout  android:minWidth="320dp"   android:minHeight="700dp"/>
    <!--Layout minimum size set in DeX mode -->
    ...
    </activity>
    </application>
    
  2. b) Enable the ‘keep alive’ setting in the manifest file to avoid making the app restart when switching from phone to DeX mode or vice versa.

    <application
    …
    <!--Keep alive settings -->
    <meta-data
        android:name="com.samsung.android.keepalive.density"
        android:value="true" />
    
    <meta-data
        android:name="com.samsung.android.multidisplay.keep_process_alive"
        android:value="true" />
    <!--Keep alive settings -->
    …
    </application>
    
  3. c) Change the launch size of the app through the following:

    <application
    …
    <!-- app launch size set in DeX mode -->
    <meta-data
        android:name="com.samsung.android.dex.launchwidth"
        android:value="1024"/>
    <meta-data
        android:name="com.samsung.android.dex.launchheight"
        android:value="800"/>
    <!-- app launch size set in DeX mode-->
    …
    </application>
    

6. Detect DeX Mode

In this step, you will prevent the app from restarting whenever resizing the window. In addition, the handling of the runtime configuration can be changed through

onConfigurationChanged(). Moreover, the updated information will be displayed after resizing the window in DeX mode.

  1. a) Add the function below to check whether the current mode is on Samsung DeX or not.

    public class DeXUtils {
    …
    …
    @SuppressWarnings("unchecked")
    static boolean isDeXAvailable(Context context) {
        @SuppressLint("WrongConstant")
        Object desktopModeManager = context.getApplicationContext().getSystemService("desktopmode");
        if (desktopModeManager != null) {
            try {
                Method getDesktopModeStateMethod = desktopModeManager.getClass().getDeclaredMethod("getDesktopModeState");
                Object desktopModeState = getDesktopModeStateMethod.invoke(desktopModeManager);
    
                Class desktopModeStateClass = desktopModeState.getClass();
    
                Method getEnabledMethod = desktopModeStateClass.getDeclaredMethod("getEnabled");
                int enabled = (int) getEnabledMethod.invoke(desktopModeState);
                boolean isEnabled = enabled == desktopModeStateClass.getDeclaredField("ENABLED").getInt(desktopModeStateClass);
    
                Method getDisplayTypeMethod = desktopModeStateClass.getDeclaredMethod("getDisplayType");
                int displayType = (int) getDisplayTypeMethod.invoke(desktopModeState);
                boolean isDualMode = isEnabled && displayType == desktopModeStateClass.getDeclaredField("DISPLAY_TYPE_DUAL").getInt(desktopModeStateClass);
                boolean isStandaloneMode = isEnabled && displayType == desktopModeStateClass.getDeclaredField("DISPLAY_TYPE_STANDALONE").getInt(desktopModeStateClass);
    
                // Check isEnabled, isDualMode or isStandaloneMode as you want
                String msg = "DeX: isEnabled=" + isEnabled + ", isDualMode=" + isDualMode + ", isStandaloneMode=" + isStandaloneMode + ", isAppCurrentlyRunningInDeX=" + isAppCurrentlyRunningInDeX(context);
                Log.d(TAG, msg);
                //Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
                return isEnabled | isDualMode | isStandaloneMode | isAppCurrentlyRunningInDeX(context);
            } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                // Device does not support DeX 3.0
                return false;
            }
        } else {
            // Device does not support Samsung DeX or called too early on boot
            return false;
        }
    }
     …
    …
    }
    
  2. b) Implement onConfigurationChanged() and other functions to show updated information for density, orientation, width of window, height of window, and UI mode after resizing the window.

    public class MainActivity extends AppCompatActivity {
    …
    …
    //isSamsungDex : onConfigurationChanged
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG,"onConfigurationChanged");
    
        if (DeXUtils.isDeXAvailable(getApplication())) {
            hiddenLayout.setVisibility(View.VISIBLE);
            showTheValueChanged();
        } else {
            hiddenLayout.setVisibility(View.GONE);
        }
    }
    
    //Samsung DeX: Hidden Layout Needs below functions
    @SuppressLint("SetTextI18n")
    private void showTheValueChanged() {
        TextView valueText = findViewById(R.id.values);
        valueText.setText("Density : " + getDensity()
                + " | Orientation : " + getOrientation()
                + " | (width, height) : (" + Integer.toString(getResources().getConfiguration().screenWidthDp) + ", " + Integer.toString(getResources().getConfiguration().screenHeightDp) + ")"
                + " | UI Mode : " + getUImode());
    }
    
    private String getUImode()
    {
        int screenUimode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK;
    
        switch(screenUimode){
            case Configuration.UI_MODE_TYPE_DESK :
                return "DESK MODE";
            case Configuration.UI_MODE_TYPE_NORMAL :
                return "NORMAL MODE";
            default :
                return null;
        }
    }
    
    private String getOrientation()
    {
        int orientation = getResources().getConfiguration().orientation;
    
        if(Configuration.ORIENTATION_LANDSCAPE == orientation){
            return "Landscape";
        } else if(Configuration.ORIENTATION_PORTRAIT == orientation){
            return "Portrait";
        }
    
        return null;
    }
    
    private String getDensity()
    {
        float density = getResources().getDisplayMetrics().density;
    
        if(density >= 4.0){
            return "xxxhdpi";
        } else if(density >= 3.0){
            return "xxhdpi";
        } else if(density >= 2.0){
            return "xhdpi";
        } else if(density >= 1.5){
            return "hdpi";
        } else if(density >= 1.0){
            return "mdpi";
        } else {
            return "ldpi";
        }
    }
     …
    …
    }
    
  3. c) When the app detects DeX mode, it will be shown at the top side of the layout as implemented below.

    public class MainActivity extends AppCompatActivity {
    …
    …
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    …
    …
    //isSamsungDex : Hidden Layout
    //Checking the Samsung DeX and then you have to set up hiddenlayout
    hiddenLayout = findViewById(R.id.hidden);
    if (DeXUtils.isDeXAvailable(getApplication())) {
        CheckPermission();
        hiddenLayout.setVisibility(View.VISIBLE);
        showTheValueChanged();
    }
     …
    …
    }
    }
    
  4. d) To make the dual mode button show a window on the external monitor, use the following:

    public class MainActivity extends AppCompatActivity {
    …
    …
    //isSamsungDex : Share Button
    private void handleDrawingIconTouched(int itemId) {
        switch (itemId) {
            case R.id.action_delete:
                deleteDialog();
                break;
            case R.id.action_undo:
                mCustomView.onClickUndo();
                break;
            case R.id.action_redo:
                mCustomView.onClickRedo();
                break;
            case R.id.action_brush:
                brushSizePicker();
                break;
            case R.id.action_share:
                if (DeXUtils.isDeXAvailable(getApplication())) {
                    AddWindowAfterCheckPermission();
                } else {
                    Toast.makeText(getApplicationContext(), "This function will only work if Samsung's DeX mode is available.", Toast.LENGTH_LONG).show();
                }
                break;
        }
    }
    …
    …
    }
    

7. Implement Keyboard Features and Combination Keys

The keyboard interaction with Samsung DeX supports the following functions:

  1. Closing popups, Esc

  2. Switch between open apps , Alt+Tab

  3. Take screenshot, Print Screen

  4. Keyboard shortcuts:

    1. Undo, Ctrl-Z

    2. Redo, Ctrl-Y

    3. Cut, Ctrl-X

    4. Copy, Ctrl-C

    5. Paste, Ctrl-V

    6. Select all, Ctrl-A

To add more keyboard functionalities to your app in DeX mode, use onKeyDown(), onKeyUp(), and the value of keyCode which is the Key Event code. In the sample app, add the feature to increase or decrease the brush size using the combination of the CTRL key and the mouse wheel.

  1. a) First, you add the CTRL keys using the code snippet below. The function added below checks when the key has been pressed or released.

    public class MainActivity extends AppCompatActivity {
    …
    …
    //Keyboard input functions
    //On key press
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_CTRL_LEFT:
            case KeyEvent.KEYCODE_CTRL_RIGHT:
                ctrlkeyPressed = true;
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
    
    //On key release
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_CTRL_LEFT:
            case KeyEvent.KEYCODE_CTRL_RIGHT:
                ctrlkeyPressed = false;
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
    //Key function
    …
    …
    }
    

    For more information on adding keyboard functionality, use the Android guide for handling keyboard event actions, in the following link: https://developer.android.com/training/keyboard-input/commands.html

  2. b) Enable the CTRL + Mouse-wheel combination to change brush size.

    public class MainActivity extends AppCompatActivity {
    …
    …
    
    protected void onCreate(Bundle savedInstanceState) {
    …
    …
    //Ctl key + mouse wheel : change the brush size
    mCustomView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
        @Override
        public boolean onGenericMotion(View view, MotionEvent motionEvent) {
            int action = motionEvent.getAction();
    
            if ((action == MotionEvent.ACTION_SCROLL) && ctrlkeyPressed) {
                float vScroll = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
                if (vScroll < 0) {
                    //wheel down
                    if (brushSizewithWheel > getResources().getInteger(R.integer.min_size))
                        brushSizewithWheel--;
                    else
                        brushSizewithWheel = getResources().getInteger(R.integer.min_size);
                } else {
                    //wheel up
                    if (brushSizewithWheel < getResources().getInteger(R.integer.max_size))
                        brushSizewithWheel++;
                    else
                        brushSizewithWheel = getResources().getInteger(R.integer.max_size);
                }
                Toast.makeText(getApplicationContext(), "Brush Size : " + brushSizewithWheel, Toast.LENGTH_LONG).show();
                mCustomView.setBrushSize(brushSizewithWheel);
                mCustomView.setLastBrushSize(brushSizewithWheel);
            }
            return true;
        }
    });
    //Ctl key + mouse wheel : change the brush size
     …
    }
    …
    …
    }
    
  3. c) Add the redo and undo function with combination of the CTRL and ‘Z’ or ‘Y’ keys.

    public class MainActivity extends AppCompatActivity {
    …
    …
    //Key function
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_Y:
                if (ctrlkeyPressed)
                    mCustomView.onClickRedo();
                break;
            case KeyEvent.KEYCODE_Z:
                if (ctrlkeyPressed)
                    mCustomView.onClickUndo();
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
    //Key function
    …
    …
    }
    

8. Creating Context Menu

Using the right-click button on the app will show the context menu. This menu provides additional options for easier access to the dual mode, brush size, undo, and redo. Enable this on the right-click of the mouse by using the onTouchEvent() and MotionEvent.BUTTON_SECONDARY. In the sample app, you can add the context menu on the CustomView.

  1. a) Initially, you have to register the context menu in the CustomView.

    public class MainActivity extends AppCompatActivity {
    …
    …
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    …
    …
    //Mouse Right Click
    //Register context menu for mouse right click
    mCustomView = findViewById(R.id.custom_view);
    registerForContextMenu(mCustomView);
    …
    …
    }
    …
    …
    }
    
  2. b) Using the code below, implement the context menu on the right-click of the mouse.

    public class MainActivity extends AppCompatActivity {
    …
    …
    //Mouse Right Click : When mouse right click, context menu will appear.
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        Log.d(TAG, "onCreate Context menu :" + v.getId() + " : " + R.id.custom_view);
        if (v.getId() == R.id.custom_view) {
            menu.setHeaderTitle("Drawing Option");
            getMenuInflater().inflate(R.menu.menu_drawing, menu);
        }
        super.onCreateContextMenu(menu, v, menuInfo);
    }
    
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_delete:
                deleteDialog();
                break;
            case R.id.action_undo:
                mCustomView.onClickUndo();
                break;
            case R.id.action_redo:
                mCustomView.onClickRedo();
                break;
            case R.id.action_brush:
                brushSizePicker();
                break;
            case R.id.action_share:
                if (DeXUtils.isDeXAvailable(getApplication()))
                    AddWindowAfterCheckPermission();
                else
                    Toast.makeText(getApplicationContext(), "This function will only work if it is connected to an external monitor.", Toast.LENGTH_LONG).show();
                break;
            default:
                return super.onContextItemSelected(item);
        }
          return super.onContextItemSelected(item);
    }
    //Mouse right click
     …
    …
    }
    

9. Utilizing DeX Dual Mode

Dual mode is a new feature of Samsung DeX where you can set up DeX mode on the monitor while allowing you to use different apps on your phone simultaneously.

In this step, you can experience dual mode. That is, the e-mail app can run and be displayed on the external monitor while allowing you to use the phone independently from the DeX mode.

  1. a) If you click the e-mail app button, it should launch the app on the external display that is on DeX mode.

    public class MainActivity extends AppCompatActivity {
    …
    …
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    …
    …
    FloatingActionButton fab = findViewById(R.id.email_fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Display currentDisplay = getWindow().getDecorView().getDisplay();
    
            ActivityOptions options = ActivityOptions.makeBasic();
            options.setLaunchDisplayId(DeXUtils.getOtherDisplayId(getApplication(), currentDisplay));
    
            Intent intent = getPackageManager().getLaunchIntentForPackage("com.google.android.gm");
            assert intent != null;
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent, options.toBundle());
        }
    });
     …
    …
    }
    …
    …
    }
    
  2. b) To add window on the external monitor, click the dual view button on the app.

    public class MainActivity extends AppCompatActivity {
    …
    …
    //isSamsungDex : Share Button
    private void handleDrawingIconTouched(int itemId) {
        switch (itemId){
    …
    case R.id.action_share:
        if (DeXUtils.isDeXAvailable(getApplication())) {
            AddWindowAfterCheckPermission();
        } else {
            Toast.makeText(getApplicationContext(), "This function will only work if Samsung's DeX mode is available.", Toast.LENGTH_LONG).show();
        }
        break;
        }
    }
    @Override
    public boolean onContextItemSelected(MenuItem item){
        switch (item.getItemId()){
             …
            case R.id.action_share:
                 if (DeXUtils.isDeXAvailable(getApplication()))
                     AddWindowAfterCheckPermission();
               else
                     Toast.makeText(getApplicationContext(), "This function will only work if it is connected to an external monitor.", Toast.LENGTH_LONG).show();
           break;
         default:
              return super.onContextItemSelected(item);
    
        }
          return super.onContextItemSelected(item);
    }
    //Mouse right click
    
    //Add Windown on external mointor
    void addWindowInSecondaryDisplay() {
    
        if (mViewInSencondaryDisplay == null) {
            Display currentDisplay = getWindow().getDecorView().getDisplay();
    
            //Get the Secondary window manager
            Context displayContext = mContext.createDisplayContext(DeXUtils.getOtherDisplay(getApplication(), getWindowManager(), currentDisplay));
            mSecondaryWm = (WindowManager) displayContext.getSystemService(Context.WINDOW_SERVICE);
    
            //Get the Layout for secondary window
            LayoutInflater li = (LayoutInflater) displayContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            LinearLayout layout = new LinearLayout(displayContext);
            mViewInSencondaryDisplay = Objects.requireNonNull(li).inflate(R.layout.activity_dualview, layout, false);
    
            //Setting the window layout
            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.height = WindowManager.LayoutParams.MATCH_PARENT;
            params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    
            mViewInSencondaryDisplay.setBackgroundColor(Color.WHITE);
    
            //Immersive mode setting : Full Screen
            DeXUtils.immersiveMode(mViewInSencondaryDisplay);
            mViewInSencondaryDisplay.setOnSystemUiVisibilityChangeListener(
                    new View.OnSystemUiVisibilityChangeListener() {
                        @Override
                        public void onSystemUiVisibilityChange(int visibility) {
                            if (mViewInSencondaryDisplay != null) {
                                DeXUtils.immersiveMode(mViewInSencondaryDisplay);
                            }
                        }
                    });
    
            getWindow().setNavigationBarColor(Color.WHITE);
    
            imgbtn_window_close = (ImageView) mViewInSencondaryDisplay.findViewById(R.id.dualView_Image);
            imgbtn_window_close.setOnClickListener(mBtnClickLisner);
    
    
            //Add window display configurations to window manager
            mSecondaryWm.addView(mViewInSencondaryDisplay, params);
            Log.d(TAG, "Secondary Window Added");
            //Toast.makeText(getApplicationContext(),"Secondary Window Added",Toast.LENGTH_LONG).show();
        }
    }
    
    //When click the image button, Secondary window will be closed.
    View.OnClickListener mBtnClickLisner = new View.OnClickListener(){
    
        @Override
            public void onClick(View view) {
    
                switch (view.getId()) {
                    case R.id.dualView_Image:
                        removeWindowInSecondaryDisplay();
                    break;
    
                    default:
                    break;
                }
            }
    };
    
    void removeWindowInSecondaryDisplay() {
        if(mViewInSencondaryDisplay != null) {
            mSecondaryWm.removeView(mViewInSencondaryDisplay);
            mViewInSencondaryDisplay = null;
            mSecondaryWm = null;
        }
    
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy" );
        removeWindowInSecondaryDisplay();
    }
    //Add Windown on external mointor
    …
    …
    }
    public class DeXUtils {
    …
    …
    static int getOtherDisplayId(Context context, Display currentDisplay){
    
        DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
        Display[] displays = Objects.requireNonNull(dm).getDisplays("com.samsung.android.hardware.display.category.DESKTOP");
        int targetDisplayId;
    
        if(currentDisplay.getDisplayId() == Display.DEFAULT_DISPLAY){
            targetDisplayId = displays[0].getDisplayId();
        }else {
            targetDisplayId = Display.DEFAULT_DISPLAY;
        }
        return targetDisplayId;
    }
    
    static Display getOtherDisplay(Context context, WindowManager wm, Display currentDisplay){
        Display targetDisplay;
    
        //Get the target Display.
        if(currentDisplay.getDisplayId() == Display.DEFAULT_DISPLAY) {
            targetDisplay = getExternalDisplay(context, wm);
        } else {
            DisplayManager dm = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
            targetDisplay = Objects.requireNonNull(dm).getDisplay(Display.DEFAULT_DISPLAY);
        }
    
        return targetDisplay;
    }
    
    private static Display getExternalDisplay(Context context, WindowManager wm) {
        DisplayManager dm = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        Display[] displays = Objects.requireNonNull(dm).getDisplays("com.samsung.android.hardware.display.category.DESKTOP");
    
        if(displays.length == 0) {
            displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
        }
    
        int count = displays.length;
        Log.d(TAG, "count " + count);
        for(int i = 0; i < count; i++) {
            Log.d(TAG, "Display " + i +  " " + displays[i]);
        }
    
        if(displays.length > 0) {
            return displays[0];
        }
    
        return wm.getDefaultDisplay();
    }
    
    static void immersiveMode(View mViewInSencondaryDisplay){
    
        //Immersive mode setting : Full Screen
        final int flags = View.SYSTEM_UI_FLAG_LOW_PROFILE
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                // Set the content to appear under the system bars so that the
                // content doesn't resize when the system bars hide and show.
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                // Hide the nav bar and status bar
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    
    
        mViewInSencondaryDisplay.setSystemUiVisibility( flags );
    }
    …
    …
    }
    
    
  3. c) You have to enable overlay permission in the device settings whenever adding window on the display. Follow the sample code below to enable and directly call ‘overlay permission dialog’.

    public class MainActivity extends AppCompatActivity {
    …
    …
    
    @SuppressWarnings("StatementWithEmptyBody")
    @TargetApi(Build.VERSION_CODES.M)
    @Override
    //Overay permission check
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
            if (!Settings.canDrawOverlays(this)) {
                AddWindowAfterCheckPermission();
            } else {
                // Do as per your logic
            }
        }
    }
    
    //Overay permission
    @SuppressLint("ObsoleteSdkInt")
    private void AddWindowAfterCheckPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
            } else {
                // Add window
                if (mViewInSencondaryDisplay == null) {
                    addWindowInSecondaryDisplay();
                } else {
                    removeWindowInSecondaryDisplay();
                }
            }
        }
    }
    //Add Windown on external mointor
    …
    …
    }
    

    By doing these steps, you have optimized your app to work with Samsung DeX. Adding other features is available on Samsung DeX. You can modify your app without any restrictions by making your app have a desktop-like experience.