Screen Pixel Density and Orientation Issues on Android Build

I’m facing a peculiar issue with screen pixel density and screen orientation in Cocos Creator on Android, and I’m seeking some guidance from the community.

The Problem:
I’ve been using screen.devicePixelRatio and view.devicePixelRatio to obtain the device pixel ratio (DPR) value of the screen. It’s been working perfectly on iOS, returning accurate values. However, for Android builds, these methods consistently return a DPR of 1, even though my Android device’s screen has a DPR of 2.75. Additionally, I’ve noticed that view.getVisibleSize() doesn’t return the actual screen resolution. For instance, my Android device has a screen resolution of 393px x 873px, but view.getVisibleSize() returns 1080px x 2400px.

Environment:
Cocos Creator Version: 3.7.3
Device: Android (iOS works correctly)

Example:

const payload = JSON.stringify({
    width: view.getVisibleSize().width, // Android returns 1080, correct is 393.
    density: screen.devicePixelRatio,   // Android returns 1, correct is 2.75.
    height: screen.devicePixelRatio.height // Android returns 2400, correct is 873.
});

On iOS, it returns the expected values:

{
    width: 375, // Correct width of device: 375.
    density: 2, // Correct density of device: 2.
    height: 667 // Correct height of device: 667.
}

Locking Screen Orientation:
I’m also looking to lock the screen orientation at runtime. While Cocos Creator’s view.setOrientation(macro.ORIENTATION_PORTRAIT) works for setting a fixed orientation, it’s mentioned in the documentation that in native builds, we have to use the native project settings. However, my use case requires runtime orientation locking. Here’s an example of what I’ve attempted:

if (this.isShowingInterstitial) {
    if (view.getOrientation() === macro.ORIENTATION_PORTRAIT) {
        view.setOrientation(macro.ORIENTATION_PORTRAIT);
    }
    if (view.getOrientation() === macro.ORIENTATION_LANDSCAPE) {
        view.setOrientation(macro.ORIENTATION_LANDSCAPE);
    }
} else {
    view.setOrientation(macro.ORIENTATION_AUTO);
}

Conclusion:
I would greatly appreciate any insights, tips, or guidance on resolving these issues. If you’ve encountered similar challenges or have any suggestions, please share your expertise. Your assistance would be immensely valuable.

You can use var resolution = view.getVisibleSizeInPixel(); to get the device resolution. Currently, device DPR is only effective on the web platform, and the default output is 1 on Android platforms.

I appreciate the clarification that DPR values are primarily effective on the web platform. However, I’m encountering an issue specific to the Android platform. On iOS, I’m able to obtain the correct DPR value, but on Android, it consistently returns a default value of 1, even though my Android device has a higher DPR.

Regarding the setOrientation method, I’ve noticed that it doesn’t seem to work as expected on either Android or iOS. Is there any alternative methods or approaches that can be employed to achieve dynamic orientation locking that would work seamlessly on both Android and iOS platforms? Your guidance on this matter would be greatly appreciated.

You can refer to this solution: https://github.com/cocos/cocos-awesome-tech-solutions/tree/3.7.x-release/demo/Creator3.7.3_2D_ScreenSwitch

Unfortunately the solution is not working for me, as in the solution it’s guiding to add a setOrientation function on appActivity.java file for android, that I have added and the code as mentioned below, but it’s not working for the orientation lock functionality that I want to add here.

public class AppActivity extends CocosActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // DO OTHER INITIALIZATION BELOW
        SDKWrapper.shared().init(this);

    }

    @Override
    protected void onResume() {
        super.onResume();
        SDKWrapper.shared().onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        SDKWrapper.shared().onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
        if (!isTaskRoot()) {
            return;
        }
        SDKWrapper.shared().onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        SDKWrapper.shared().onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        SDKWrapper.shared().onNewIntent(intent);
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        SDKWrapper.shared().onRestart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        SDKWrapper.shared().onStop();
    }

    @Override
    public void onBackPressed() {
        SDKWrapper.shared().onBackPressed();
        super.onBackPressed();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        SDKWrapper.shared().onConfigurationChanged(newConfig);
        super.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        SDKWrapper.shared().onRestoreInstanceState(savedInstanceState);
        super.onRestoreInstanceState(savedInstanceState);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        SDKWrapper.shared().onSaveInstanceState(outState);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onStart() {
        SDKWrapper.shared().onStart();
        super.onStart();
    }

    @Override
    public void onLowMemory() {
        SDKWrapper.shared().onLowMemory();
        super.onLowMemory();
    }

    public static void setOrientation(String dir) {
            GlobalObject.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
    }
}

Here is the code for my ts file in which I have added the setOrientation method.

checkOrientation() {
          if (this.isShowingInterstitial) {
            if (this.currentOrientation === 'portrait') {
              view.setOrientation(macro.ORIENTATION_PORTRAIT);
            }

            if (this.currentOrientation === 'landscape') {
              view.setOrientation(macro.ORIENTATION_LANDSCAPE);
            }

            return;
          }

}

You also need to execute these codes:

view.setOrientation only works for web games

Thank you for providing the solution. It works perfectly on Android. However, when attempting to implement it on iOS, an error occurs, specifically, “No member named ‘EventDispatcher’ in namespace 'cc,” in the following code segment, at the last line of code: cc::EventDispatcher::dispatchOrientationChangeEvent((int) orientation);

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    cc::Device::Orientation orientation = _lastOrientation;
    // reference: https://developer.apple.com/documentation/uikit/uiinterfaceorientation?language=objc
    // UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
    // UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight
    switch ([UIDevice currentDevice].orientation) {
        case UIDeviceOrientationPortrait:
            orientation = cc::Device::Orientation::PORTRAIT;
            break;
        case UIDeviceOrientationLandscapeRight:
            orientation = cc::Device::Orientation::LANDSCAPE_LEFT;
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            orientation = cc::Device::Orientation::PORTRAIT_UPSIDE_DOWN;
            break;
        case UIDeviceOrientationLandscapeLeft:
            orientation = cc::Device::Orientation::LANDSCAPE_RIGHT;
            break;
        default:
            break;
    }
    if (_lastOrientation != orientation) {
        cc::EventDispatcher::dispatchOrientationChangeEvent((int) orientation);
        _lastOrientation = orientation;
    }
}

Any suggestion to fix this?

Although I need suggestion of any alternative if available to get dpr values on android build, as mentioned above that the screen.devicePixelRatio is working on iOS build but not on the android build.

ViewController.mm does not need to be modified, only the code in AppDelegate.mm needs to be changed.

Firstly, thank you for the solution it’s working for both platforms, but I also need the settings to set the sensor to decide the orientation.

When we apply the solution for iOS and set orientation to portrait and after that I want to set AUTO mode or want to roll back the changes what we did previously, Is there a way to do this.

For example, I did for Android

 public static void setOrientation(String dir) {
     if (dir.equals("portrait"))
         GlobalObject.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
     else if (dir.equals("landscape"))
         GlobalObject.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
     else
         GlobalObject.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
 }

I need similar type of functionality in iOS.