How to detect screen rotation without rotating/recreating the activity? - android

I want to be able to detect whether the phone is in landscape or portrait mode, but I don't want my activity and all my buttons to rotate along.
This code detects the rotation, but it also rotates the screen:
Display display = getWindowManager().getDefaultDisplay();
int rotation = display.getRotation();
rotVal.setText("Rotation: " + rotation);
I tried to lock the activity in portrait mode with this in the MainActivity, but then rotation value doesn't change. (Makes sense I guess)
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
I also tried android:screenOrientation="portrait" in the manifest file but the rotVal stays zero.
This answer have a rotating animation, and I don't want that. I want the screen to stay as it is even in landscape mode, no rotating, no animation, etc.
This answer doesn't work. When I rotate the phone, the rotVal stays zero.
Is there a way to detect the orientation without rotating/recreating the layout?

The second Stack Overflow answer you link to will work, but you can't inquire for the orientation like you do.
First, lock the app in portrait mode. The following code should work and does not include all the animation code.
private lateinit var orientationListener: OrientationListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Code here to do Activity-related stuff.
// Set up a listener to detect orientation changes.
orientationListener = OrientationListener(this)
}
override fun onStart() {
orientationListener.enable()
super.onStart()
}
override fun onStop() {
orientationListener.disable()
super.onStop()
}
private class OrientationListener(context: Context?) : OrientationEventListener(context) {
val ROTATION_O = 1 // portrait
val ROTATION_90 = 2 // landscape counter-clockwise
val ROTATION_180 = 3 // portrait inverted
val ROTATION_270 = 4 // landscape clockwise
private var rotation = 0
override fun onOrientationChanged(orientation: Int) {
if ((orientation < 35 || orientation > 325) && rotation != ROTATION_O) { // PORTRAIT
rotation = ROTATION_O
} else if (orientation > 145 && orientation < 215 && rotation != ROTATION_180) { // REVERSE PORTRAIT
rotation = ROTATION_180
} else if (orientation > 55 && orientation < 125 && rotation != ROTATION_270) { // REVERSE LANDSCAPE
rotation = ROTATION_270
} else if (orientation > 235 && orientation < 305 && rotation != ROTATION_90) { //LANDSCAPE
rotation = ROTATION_90
}
// We'll just display the value, but it should be stashed for further use or acted upon immediately.
Log.d("Applog", "rotation = $rotation")
}
}

Related

how to handle view rotations like youtube do on android?

I want to achieve the way youtube handles its video orientation.
There are a few things to have in mind before just saying that I have to use onConfigurationChanged.
THINGS TO CONSIDER
The phone orientation. This means the way you hold de phone. It could be PORTRAIT or LANDSCAPE
The screen orientation. This means the way the view is displayed. It could be PORTRAIT or LANDSCAPE regardless of the orientation of the phone
HOW YOUTUBE ROTATION WORKS?
If the phone is in PORTRAIT mode (using OS quick settings)
The screen will keep in PORTRAIT. It doesn't matter if you rotate the phone
The only way to rotate de screen is by pressing the rotate button.
The screen will keep in the last orientation chooses, and it will not change even if you rotate the phone. PORTRAIT will remain PORTRAIT, and LANDSCAPE will remain LANDSCAPE.
If the phone is in AUTO-ROTATE mode
if you rotate the phone, then the screen rotation follows it. I mean, if the phone is in PORTRAIT, the screen will be PORTRAIT. And the same happens with LANDSCAPE.
if the rotate button is pressed, the screen orientation will change regardless of the phone orientation. In this case, the only way to listen to the sensor orientation is to rotate the phone to the same side of the screen rotation and go back. This is to let the phone sensor have control of the orientation again.
WHAT I HAVE?
With onConfigurationChanged I could know when I rotate the phone (if AUTO-ROTATE is enabled) and define the layout:
override fun onConfigurationChanged(newConfig: Configuration) {
when (newConfig.orientation) {
Configuration.ORIENTATION_PORTRAIT -> buildPortrait()
Configuration.ORIENTATION_LANDSCAPE -> buildLandscape()
}
super.onConfigurationChanged(newConfig)
}
With the rotation button, I can handle screen orientation (when the phone is in PORTRAIT BLOCKED from OS)
fun rotate() {
val orientation = requireActivity().requestedOrientation
val isRotationOn = Settings.System.getInt(requireActivity().contentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0) == 1
requireActivity().requestedOrientation = if (isRotationOn) {
// AUTO-ROTATE enabled
TODO()
} else
// PORTRAIT BLOCKED
if (orientation == PORTRAIT) {
buildLandscape()
LANDSCAPE
} else {
buildPortrait()
PORTRAIT
}
}
WHAT I CAN'T ACHIEVE?
I don't know how to handle screen rotation when I have the AUTO-ROTATE enabled that handles the rotation by the sensor, and at the same time, there is a rotate button that does a rotation regardless of the AUTO-ROTATE.
IMPORTANT: I can't find any example with the solution anywhere.
You need to use OrientationEventListener to listen to orientation change and set requestedOrientation back to ActivityInfo.SCREEN_ORIENTATION_USER when orientation changed.
The orientation value passed in onOrientationChanged is from 0 to 359 and -1 means flat. You need to map it into 4 directions according to the following code.
orientationEventListener = object: OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) {
val newOrientation = when (orientation) {
in 0 .. 44 -> 0
in 45 .. 134 -> 1
in 135 .. 224 -> 2
in 225 .. 314 -> 3
in 315 .. 359 -> 0
else -> ORIENTATION_UNKNOWN
}
if (newOrientation != previousOrientation) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
}
previousOrientation = newOrientation
}
}
And remember to only enable the above listener if auto-rotate setting is on
val autoRotationOn = Settings.System.getInt(contentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0) == 1
if (autoRotationOn) {
orientationEventListener.enable()
} else {
orientationEventListener.disable()
}
As the auto-rotate setting could be changed within the lifecycle of current activity, you have to listen to the value change and enable/disable the orientationEventListener.
private val contentObserver = object:ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
setupOrientationEventListener()
}
}
val settingUri = Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)
contentResolver.registerContentObserver(settingUri, false, contentObserver)

How to determine correct device orientation in Android N multi-window mode?

From Multi-Window documentation:
Disabled features in multi-window mode
Certain features are disabled or ignored when a device is in multi-window mode, because they don’t make sense for an activity which may be sharing the device screen with other activities or apps. Such features include:
Some System UI customization options are disabled; for example, apps cannot hide the status bar if they are not running in full-screen mode.
The system ignores changes to the android:screenOrientation attribute.
I get that for most apps it doesn't make sense to distinct between portrait and landscape modes, however I am working on SDK which contains camera view which user can put on any activity they wish - including activity that supports multi-window mode. The problem is that camera view contains SurfaceView/TextureView which displays the camera preview and in order to display preview correctly in all activity orientations, knowledge about correct activity orientation is required so that camera preview can be correctly rotated.
The problem is that my code which calculates correct activity orientation by examining current configuration orientation (portrait or landscape) and current screen rotation. The problem is that in multi-window mode current configuration orientation does not reflect the real activity orientation. This then results with camera preview being rotated by 90 degrees because Android reports different configuration than orientation.
My current workaround is to check for requested activity orientation and use that as a basis, but there are two problems with that:
the requested activity orientation does not have to reflect actual activity orientation (i.e. request may still not be fulfilled)
the requested activity orientation can be 'behind', 'sensor', 'user', etc. which does not reveal any information about current activity orientation.
According to documentation, screen orientation is actually ignored in multi-window mode, so 1. and 2. just won't work
Is there any way to robustly calculate correct activity orientation even in multi-window configuration?
Here is my code that I currently use (see comments for problematic parts):
protected int calculateHostScreenOrientation() {
int hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int rotation = getDisplayOrientation(wm);
boolean activityInPortrait;
if ( !isInMultiWindowMode() ) {
activityInPortrait = (mConfigurationOrientation == Configuration.ORIENTATION_PORTRAIT);
} else {
// in multi-window mode configuration orientation can be landscape even if activity is actually in portrait and vice versa
// Try determining from requested orientation (not entirely correct, because the requested orientation does not have to
// be the same as actual orientation (when they differ, this means that OS will soon rotate activity into requested orientation)
// Also not correct because, according to https://developer.android.com/guide/topics/ui/multi-window.html#running this orientation
// is actually ignored.
int requestedOrientation = getHostActivity().getRequestedOrientation();
if ( requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT ) {
activityInPortrait = true;
} else if ( requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE ||
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE ) {
activityInPortrait = false;
} else {
// what to do when requested orientation is 'behind', 'sensor', 'user', etc. ?!?
activityInPortrait = true; // just guess
}
}
if ( activityInPortrait ) {
Log.d(this, "Activity is in portrait");
if (rotation == Surface.ROTATION_0) {
Log.d(this, "Screen orientation is 0");
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
} else if (rotation == Surface.ROTATION_180) {
Log.d(this, "Screen orientation is 180");
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
} else if (rotation == Surface.ROTATION_270) {
Log.d(this, "Screen orientation is 270");
// natural display rotation is landscape (tablet)
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
} else {
Log.d(this, "Screen orientation is 90");
// natural display rotation is landscape (tablet)
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
} else {
Log.d(this, "Activity is in landscape");
if (rotation == Surface.ROTATION_90) {
Log.d(this, "Screen orientation is 90");
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
} else if (rotation == Surface.ROTATION_270) {
Log.d(this, "Screen orientation is 270");
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
} else if (rotation == Surface.ROTATION_0) {
Log.d(this, "Screen orientation is 0");
// natural display rotation is landscape (tablet)
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
} else {
Log.d(this, "Screen orientation is 180");
// natural display rotation is landscape (tablet)
hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
}
}
return hostScreenOrientation;
}
private int getDisplayOrientation(WindowManager wm) {
if (DeviceManager.getSdkVersion() < 8) {
return wm.getDefaultDisplay().getOrientation();
}
return wm.getDefaultDisplay().getRotation();
}
private boolean isInMultiWindowMode() {
return Build.VERSION.SDK_INT >= 24 && getHostActivity().isInMultiWindowMode();
}
protected Activity getHostActivity() {
Context context = getContext();
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
return null;
}
EDIT: I've reported this also to Android issue tracker.
I don’t know if this should be considered a solution or just a workaround.
As you say, your problems come with Android N and its multi-window mode. When the app is in multi window, your Activity is not tied to the full display dimensions. This redefines the concept of Activity orientation. Quoting Ian Lake:
Turns out: “portrait” really just means the height is greater than the
width and “landscape” means the width is greater than the height. So
it certainly makes sense, with that definition in mind, that your app
could transition from one to the other while being resized.
So there is no link anymore between Activity orientation changing and device physically being rotated. (I think the only reasonable use of Activity orientation changes now is to update your resources.)
Since you are interested in device dimensions, just get its DisplayMetrics. Quoting docs,
If requested from non-Activity context
metrics will report the size of the entire display based on current
rotation and with subtracted system decoration areas.
So the solution is:
final Context app = context.getApplicationContext();
WindowManager manager = (WindowManager) app.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
boolean portrait = height >= width;
Width and height values will be swapped (more or less) when the device is tilted.
If this works, I would personally run it every time, deleting the isInMultiWindowMode() branch, because
it’s not expensive
our assumptions stand also in the non-multi-window mode
it will presumably work well with any other future kinds of modes
you avoid the race condition of isInMultiWindowMode() described by CommonsWare
I thought you could utilise the accelerometer to detect where's "down" - and thus the orientation of the phone. The Engineer Guy explains that that's the way the phone itself does it.
I searched here on SO for a way to do that and found this answer. Basically you need to check which of the 3 accelerometers detect the most significant component of the gravitational pull, which you know is roughly 9.8m/s² near the ground of the earth. Here's the code snippet from it:
private boolean isLandscape;
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER),1000000);
private final SensorEventListener mSensorListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent mSensorEvent) {
float X_Axis = mSensorEvent.values[0];
float Y_Axis = mSensorEvent.values[1];
if((X_Axis <= 6 && X_Axis >= -6) && Y_Axis > 5){
isLandscape = false;
}
else if(X_Axis >= 6 || X_Axis <= -6){
isLandscape = true;
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
Be careful as this code might not work in every situation, as you need to take into account scenarios like being on a stopping/accelerating train or moving the phone fast in a game - here's the orders of magnitude page on wiki to get you started. It looks like you're safe with the values Khalil put in his code (in his answer), but I would take extra caution and research into what values might be generated in the different scenarios.
It's not a flawless idea, but I think as long as the API is built the way it is - whithout allowing you to get their calculated orientation - I think it's a beneficial workaround.

Different screen orientation on different devices when landscape mode

I am using
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
for my Activity and I am trying to use OrientationEventListener to find the orientation of the device. On some devices my landscape orientation is showing values around 270 and other devices shows values around 0. On every device where the Home button is placed the orientation value shows 0. I want to have same values when holding the all devices in landscape mode. Is there a flag that can show me which device is showing 270 or 0 for landscape mode as default orientation.
This is my OrientationEventListener
mOrientationListener = new OrientationEventListener(this,
SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
Log.v("Orientation changed to ", String.valueOf(orientation));
}
};
Is there a trick that can help to resolve the issue?
I have found the solution.
With this code I am getting the default display rotation:
int rotation = getWindowManager().getDefaultDisplay().getRotation();
in the body of my onOrientationChanged method I can make this:
if (rotation == 0) {
if (orientation >= 90 && orientation <= 270) {
}
} else if (rotation == 1) {
if (orientation < 180 && orientation > 0 ) {
}
}
This will enter the if only if my device is not in the correct landscape position. If my device is on 90+ angle I can give the user a message.

How to rotate views on orientation change without recreating layout?

This question has been asked before here but its answer is incorrect. I would like to rotate some views on screen orientation change, but I want to keep the layout unchanged. They should be rotated 90, 180, 270 or 360 degrees according to the current orientation (SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_PORTRAIT, SCREEN_ORIENTATION_REVERSE_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_PORTRAIT).
This is what I want to achieve:
The answer in the link I mentioned stated that I should create a new different layout in layout-land. Clearly, this is not what I want. I don't want to recreate the activity or change layout orientation. I only want to rotate some views, and keep other views unchanged on orientation change.
There is a huge difference between rotating specific views and changing or recreating the whole layout (both on orientation change).
Using the answer in this link, I will be able to get the current screen orientation with this method:
public static int getScreenOrientation(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
int rotation = windowManager.getDefaultDisplay().getRotation();
DisplayMetrics dm = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
int orientation;
// if the device's natural orientation is portrait:
if ((rotation == Surface.ROTATION_0
|| rotation == Surface.ROTATION_180) && height > width ||
(rotation == Surface.ROTATION_90
|| rotation == Surface.ROTATION_270) && width > height) {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_180:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
case Surface.ROTATION_270:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
default:
Log.e("ScreenOrientation", "Unknown screen orientation. Defaulting to " + "portrait.");
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
}
}
// if the device's natural orientation is landscape or if the device
// is square:
else {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_180:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
case Surface.ROTATION_270:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
default:
Log.e("ScreenOrientation", "Unknown screen orientation. Defaulting to " + "landscape.");
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
}
}
return orientation;
}
On orientation change, I would like to do something simple like this:
RotateAnimation rotateAnimation = new RotateAnimation(0, getScreenOrientation(getContext()));
rotateAnimation.setDuration(2000);
for (int i = 0; i < 63; i++) {
Button button = (Button) rootView.findViewById(i);
button.startAnimation(rotateAnimation);
}
Another way to rephrase my question would be "Is there any way to detect orientation change in onConfigurationChanged() method without changing the layout?". The problem is that it will not be able to detect any orientation change if I already disable layout orientation change.
Anyone knows how it is done? I might have totally gone through wrong steps, and I think I will have to use Accelerometer Sensor or something similar to that to achieve what I want, so please guide me through.
Try to use OrientationEventListener. You don't need to use onConfigurationChanged and android:configChanges="orientation|keyboardHidden|screenSize". You need set android:screenOrientation="portrait" for the activity in AndroidManifest.xml. Here is my solution with OrientationEventListener:
public class MyActivity extends Activity{
private ImageButton menuButton;
private Animation toLandAnim, toPortAnim;
private OrientationListener orientationListener;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_image_ruler);
menuButton=(ImageButton)findViewById(R.id.menu_button);
toLandAnim= AnimationUtils.loadAnimation(this, R.anim.menubutton_to_landscape);
toPortAnim= AnimationUtils.loadAnimation(this, R.anim.menubutton_to_portrait);
orientationListener = new OrientationListener(this);
}
#Override protected void onStart() {
orientationListener.enable();
super.onStart();
}
#Override protected void onStop() {
orientationListener.disable();
super.onStop();
}
private class OrientationListener extends OrientationEventListener{
final int ROTATION_O = 1;
final int ROTATION_90 = 2;
final int ROTATION_180 = 3;
final int ROTATION_270 = 4;
private int rotation = 0;
public OrientationListener(Context context) { super(context); }
#Override public void onOrientationChanged(int orientation) {
if( (orientation < 35 || orientation > 325) && rotation!= ROTATION_O){ // PORTRAIT
rotation = ROTATION_O;
menuButton.startAnimation(toPortAnim);
}
else if( orientation > 145 && orientation < 215 && rotation!=ROTATION_180){ // REVERSE PORTRAIT
rotation = ROTATION_180;
menuButton.startAnimation(toPortAnim);
}
else if(orientation > 55 && orientation < 125 && rotation!=ROTATION_270){ // REVERSE LANDSCAPE
rotation = ROTATION_270;
menuButton.startAnimation(toLandAnim);
}
else if(orientation > 235 && orientation < 305 && rotation!=ROTATION_90){ //LANDSCAPE
rotation = ROTATION_90;
menuButton.startAnimation(toLandAnim);
}
}
}
}
This also prevents from too frequent rotations when orientation is about 45, 135... etc.
Hope it helps.
The basics are actually a lot easier. Have a look at Handling Runtime Changes.
First things first, by setting
android:configChanges="orientation|keyboardHidden|screenSize"
in your Manifest on your activity tag you can handle the orientation change yourself. (orientation should be enough, but there are sometimes issues where the event does not fire with that alone.)
You then skip onCreate and instead onConfigurationChanged gets called. Overwrite this method and apply your layout changes here. Whether you change your linearLayouts orientation here or have a custom view handling layout for different screens itself is up to you and depends on your implementation.
Animating will be a bit trickier, if it is even possilbe. A quick search says it is not.
Update for comment "I only want to rotate some views themselves rather than rotating the layout"
In theory it is possible to create your own layout and handle the drawing of your child views. I just tried it but could not produce any results in an appropriate time, but what you would need to do:
keep your last measured values use tags on the view or similar approaches to keep the last measurements and layouts, so that after the orientation change you can diff
await orientation change: trigger rotated drawing - rotate the canvas, layout the views with the previous dimensions, and draw the child views where they would have been before, and
start an animation interpolate from the last to the new values, rotating the canvas from the last to the new layout
This is how I would do it.

Toggling fullscreen & orientation like YouTube

I am trying to mimic the behavior of the YouTube Android app when the "fullscreen" button is clicked in the video player:
If device is currently in portrait, immediately rotate to landscape (even if user is still holding the device in portrait) and remain in landscape until user rotates device to landscape and then rotates back to portrait
If device is currently in landscape, immediately rotate to portrait (Even if user is still holding the device in portrait) and remain in portrait until the user rotates the device to portrait and then rotates back to landscape.
At anytime, allow the user to manually rotate their device to the desired orientation.
It seems that if I force the rotation to landscape or portrait using:
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
or
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
... I immediately lose the ability to detect sensor orientation changes (i.e. once the user is in landscape, and they want to manually rotate the device back to portrait).
If I change the requested orientation to unspecified or sensor in onConfigurationChanged, the orientation briefly flips to landscape/portrait (whatever I requested from above) and then snaps back to the orientation that matches how the device is held.
Any thoughts on how to achieve my goals above?
I had the excact same problem. What I ended up with was using an OrientationListener to detect when the user had actually tilted the phone to landscape and then setting the orientation to SCREEN_ORIENTATION_SENSOR.
OrientationEventListener orientationEventListener =
new OrientationEventListener(getActivity()) {
#Override
public void onOrientationChanged(int orientation) {
int epsilon = 10;
int leftLandscape = 90;
int rightLandscape = 270;
if(epsilonCheck(orientation, leftLandscape, epsilon) ||
epsilonCheck(orientation, rightLandscape, epsilon)){
getMainActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}
private boolean epsilonCheck(int a, int b, int epsilon) {
return a > b - epsilon && a < b + epsilon;
}
};
orientationEventListener.enable();
Here is the documentation for OrientationEventListener : Documentation
You would also need to add checks for portrait , because you described needing that in your original post.
Big tnx to havch
It is gold, I was stuck on that for 5 hours. Here is my kotlin piece of code to deal with it.
orientationEventListener = object: OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) {
val isPortrait = orientation > 300 || orientation < 60 || orientation in 120..240
if ((requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT && isPortrait) ||
(requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && !isPortrait)){
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
}
orientationEventListener.enable()
This modification of Alexander code is working better for me
object : OrientationEventListener(requireContext()) {
override fun onOrientationChanged(orientation: Int) {
val isPortrait = orientation > 345 || orientation < 15 || orientation in 165..195
val isLandscape = orientation in 255..285 || orientation in 75..105
if (
(requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT && isPortrait) ||
(requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && isLandscape)
) {
lifecycleScope.launch {
// adding a delay to avoid orientation change glitch
delay(200)
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
}
}
/**
* -1 -> Unknown
* 1 -> Portrait
* 0 -> Landscape
*/
var previousOrientation = -1
val orientationEventListener: OrientationEventListener =
object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) {
val isPortrait = (orientation > 340 || orientation < 20 || orientation in 160..200) &&
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
val isLandscape = (orientation in 250..290 || orientation in 70..110) &&
requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
if (isPortrait || isLandscape) {
lifecycleScope.launch {
if (previousOrientation == -1) {
previousOrientation = if (isPortrait) 1 else 0
}
delay(700)
val currentOrientation = if (isPortrait) 1 else 0
if (previousOrientation == currentOrientation) {
previousOrientation = -1
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
}
}
}
orientationEventListener.enable()
This one handles the orientation glitch more properly.
this code is improved version of what #Buntupana posted
Try to setRequestedOrientation to SCREEN_ORIENTATION_SENSOR when exit from fullscreen !
setting OrientationEventListener may cause some unexpected resources usage.

Categories

Resources