Optimizing your app

Modifying apps for Samsung DeX

This section provides an overview of how to optimize apps for Samsung DeX.

There are very few special implementation requirements needed to get an app ready for Samsung DeX. Generally, if an app adheres to the best practices of Android programming, it will successfully operate in Samsung DeX without any additional coding. Apps should follow the best practices for the following topics:

  • Enabling Multi-Window support

  • Enabling keyboard and mouse support

  • Handling runtime configuration changes

  • Implementing a responsive UI

The following section provides references to the most relevant Android coding practices for Samsung DeX.

Enabling Multi-Window support

To run in Samsung DeX, apps should support Android Multi-Window – this enables minimizing, maximizing and resizing. If Multi-Window is not supported, the app opens in a fixed-size window. Follow Androids guide on Multi-Window support for complete information on how to configure apps.

To enable Multi-Window for Samsung DeX, paste this code into the Manifest file:

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

The image below shows the different window resize options available when apps have Multi-Window enabled

Figure 6: Target SDK >= 24, android:resizeableActivity="true"

Enabling Mouse and keyboard

Do not explicitly declare the touchscreen support as it may disable the mouse and the keyboard interactions. This explicit declaration is shown in the code below.

<uses-configuration android:reqTouchScreen="finger" />

<uses-feature android:name="android.hardware.touchscreen" android:required="true" />

If you explicitly declare touchscreen support, the app won't launch in Desktop mode and will display the message below.

Figure 7: touchscreen disabled error

Handle runtime configuration changes

The switch between mobile and Samsung DeX mode causes runtime configuration changes – similar to an orientation change from portrait to landscape display. This may result in the application being forced to restart when switching between modes. To ensure that this does not happen, follow Android's guide for handling configuration changes; as well as Android's best practices for more information on building a responsive design.

8 runtime changes to support Samsung DeX:

  • Density change between xxxhdpi and mdpi.

  • Resolution change between WQHD and FHD.

  • Orientation change between portrait to landscape.

  • Screen layout change.

  • Screen size change.

  • Smallest screen size change.

  • UI mode change between mobile and desktop.

  • Color mode change*.

*This modification is only required for Samsung DeX Dual Mode support.

You can use the following qualifiers to provide alternative Android resources in Samsung DeX mode:

  • UI mode : desk

  • Screen layout : xlarge

  • Density : mdpi

  • Resolution : FHD (supports HD+ or WQHD when using Samsung DeX with SEP v9.0)

For example, this image below shows an example of using different resources for different screen densities.

Figure 8: Android resource folder

This image demonstrates the correct dpi handling for fonts on different screen sizes – when the Samsung DeX app is resized, the size and scale of font remains appropriate to the screen size.

Figure 9: Runtime configuration changes in Samsung DeX

If you choose to handle configuration changes yourself (not recommended), you need to provide the following declaration in the Manifest file. It will prevent the application from restarting when the screen density changes between mobile and Samsung DeX mode.

Detect Samsung DeX

If you want to check the current mode of your app, Samsung DeX or mobile mode, use the following code snippet.

Samsung DeX Mode

import android.content.res.Configuration;
import java.lang.reflect.Method;
import java.lang.Class;

Configuration config = getResources().getConfiguration();
try {
    Class configClass = config.getClass();
    if (configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
        == configClass.getField("semDesktopModeEnabled").getInt(config)) {
        // Samsung DeX mode enabled
    }
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
    // Device does not support Samsung DeX
}

1. Declare “keepalive” and set the android:configChanges attribute in the Android manifest.

 <application
  ...
  android:resizeableActivity="true">
  <activity android:name=".MainActivity"
      android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
     </activity>
   <!-- Comment out one of the below lines based on the DeX version you are developing for. -->  

   
   <!-- Version < 3.0. DeX Mode and Screen Mirroring support --> 
   <meta-data android:name="com.samsung.android.keepalive.density" android:value="true"/>
    <!-- Version >= 3.0. DeX Dual Mode support --> 
   <meta-data android:name="com.samsung.android.multidisplay.keep_process_alive" android:value="true"/>

</application>

2. See the code below to initialize your Samsung DeX app as well as how to implement the onConfigurationChanged()
API method.

import android.content.res.Configuration;
import java.lang.reflect.Method;
import java.lang.Class;

boolean desktopModeEnabled=false;
boolean currentDesktopMode = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // initialize the status for Samsung DeX mode enabled
    desktopModeEnabled = checkDeXEnabled();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    currentDesktopMode = checkDeXEnabled();
    if (desktopModeEnabled != currentDesktopMode) {
        desktopModeEnabled = currentDesktopMode;
        // Here, implement the working codes when switch the mode between mobile and Samsung DeX
    }
}

boolean checkDeXEnabled(){
    boolean enabled;
    Configuration config = getResources().getConfiguration();
    try {
        Class configClass = config.getClass();
        if (configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
                == configClass.getField("semDesktopModeEnabled").getInt(config)) {
            enabled = true;
        } else {
            enabled = false;
        }
        return enabled;
    } catch (NoSuchFieldException e) {
    } catch (IllegalAccessException e) {
    } catch (IllegalArgumentException e) {
    }
    return false;
}

This will allow your app to detect Samsung DeX once it is connected.

Samsung DeX Dual Mode

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
        Log.d(TAG, "isEnabled=" + isEnabled + ", 
isDualMode=" + isDualMode + ", isStandaloneMode=" + isStandaloneMode);
    } catch (NoSuchFieldException | NoSuchMethodException | 
IllegalAccessException | InvocationTargetException e) {
        // Device does not support DeX 3.0 
    }
} else {
    // Device does not support Samsung DeX or called too early on boot
}

The above code checks for the three states a device can be in.
isEnabled: True if the device is in DeX mode.
isDualMode: is used to determine if the device is in dual mode, isEnabled will also be set to true in this state.
isStandaloneMode: This is an alternative case to dual mode for a Tab S4 that launches Samsung DeX mode without
an external display.

A broadcast can be received when the device changes from mobile to DeX mode. Now with DeX Dual Mode you can
get additional information about the display types.

  • android.app.action.ENTER_KNOX_DESKTOP_MODE: Intent for entering DeX mode

  • android.app.action.EXIT_KNOX_DESKTOP_MODE: Intent for exiting DeX mode

  • android.app.extra.DISPLAY_TYPE: Intent for the different display types as described above

  • SemDesktopModeState.DISPLAY_TYPE_STANDALONE (101): Intent for the standalone display as described above

  • SemDesktopModeState.DISPLAY_TYPE_DUAL (102): Intent for the dual display as described above

In Samsung Dual Mode, 5 steps to run your app on a specific display

  • Modify the manifest file

  • Define a display target

  • Add a new window

  • Customize your window view

  • Set your app to appear on top

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

//Phone display
 targetDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);

If the main screen is the device screen and the target display is the external screen, implement the code as seen below.

//Desktop display
DisplayManager dm = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
Display[] displays = dm.getDisplays("com.samsung.android.hardware.display.category.DESKTOP");

if(displays.length > 0) {
  return displays[0];
}
//Create context of display
Context displayContext = mContext.createDisplayContext(targetDisplay);
mSecondaryWm = (WindowManager) displayContext.getSystemService(Context.WINDOW_SERVICE);

//Get the WindowManager of display and LayoutInflater through displayContext
LayoutInflater li = 
(LayoutInflater) displayContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

//Set up Layout and WindowParams
LinearLayout layout = new LinearLayout(displayContext);
mViewInSencondaryDisplay = 
li.inflate(R.layout.window_secondary, (LinearLayout) layout, attachToRoot false);
TextView configInfo = (TextView) mViewInSencondaryDisplay.findViewById(R.id.configuration_info);
configInfo.setText(mViewInSencondaryDisplay.getContext().getResources().getConfiguration().toString());

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;
params.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

  
//Add view in WindowManager
mSecondaryWm.addView(mViewInSencondaryDisplay, params);

※Ensure that you have properly managed the Activity lifecycle. Once an app is closed it must be removed from the
secondary window.

  1. Modify the manifest file: Add a permission, for the system alert window, to the manifest file.

  2. Define a display target: Before adding a new window, define the display it will go on
    If the current display is the device and the target window is an external display, implement the code below.

  3. Add a new window: Add a new display window for Samsung Dual mode to allow the second screen to display a new
    application.

  4. Customize your window view: Add a custom UI to your new window, created in step 3.

  5. Set apps to appear on top: Set the app to appear "in front" through device settings: Settings > Apps > DualMode
    app > Advanced > Appear on top (on).

Get the display

Once a display has been added using the steps above, there are two different ways to get the current display.

  1. Get the display from WindowManager
// create the windowManager with Context
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
  
Display display = wm.getDefaultDisplay();
  1. Get the display from decorView
public class MainActivity extends Activity {
    public void onCreate() {
        Display display = getWindow().getDecorView().getDisplay();
        //return ‘null’ as a display at this point because view is not attached to windowManager. 
    }
  
    public void onAttachedToWindow() {
        Display display = getWindow().getDecorView().getDisplay();
        // Here, already added the window and return a display. 
    }
}