I am developing application where in a service class I use WindowManager to inflate a view. So normally the view layout changes its orientation according to devices orientation, but when I open my camera application, the orientation does not changes. As shown in picture below:-
So how to change the views orientation as per devices orientation.
Thanks
There is a difference between determining whether your device's natural orientation is in landscape/portrait, or whether your app's resources are being rendered in landscape/portrait. It sounds like you want the former:
int getNaturalOrientation() {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display defaultDisplay = windowManager.getDefaultDisplay();
int rotation = defaultDisplay.getRotation();
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
if (display.getDisplayHeight() >= display.getDisplayWidth()) {
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
} else {
if (display.getDisplayHeight() < display.getDisplayWidth()) {
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
}
}
~Inspired by code in Chromium src: https://chromium.googlesource.com/chromium/src/+/aca91b5d8a53d77e98021d560305a996864df5e8/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java
Related
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.
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.
Is there some differences between getRequestedOrientation and getResources().getConfiguration() to get the orientation of the android phone screen ??
if you are in activity then use
getResources().getConfiguration().orientation
else if not in activity then use your activity instance by passing it,
your_activity_instance.getResources().getConfiguration().orientation
Another way:
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int orientation = display.getOrientation();
if(myCurrentActivity.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_PORTRAIT)
{
// code to do for Portrait Mode
} else {
// code to do for Landscape Mode
}
You would think that there would be a straight forward solution. The Android docs state:
The orientation sensor was deprecated in Android 2.2 (API level 8).
Instead of using raw data from the orientation sensor, we recommend
that you use the getRotationMatrix() method in conjunction with the
getOrientation() method to compute orientation values.
Yet, they don't provide a solution on how to implement getOrientation() and getRotationMatrix(). I've spent several hours reading through posts here on developers using these methods but they all have partially pasted code or some weird implementation. Googling hasn't provided a tutorial. Can someone please paste a simple solution using these two methods to generate the orientation??
Here is the implementation for getOrientation():
public int getscrOrientation()
{
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = getOrient.getOrientation();
// Sometimes you may get undefined orientation Value is 0
// simple logic solves the problem compare the screen
// X,Y Co-ordinates and determine the Orientation in such cases
if(orientation==Configuration.ORIENTATION_UNDEFINED){
Configuration config = getResources().getConfiguration();
orientation = config.orientation;
if(orientation==Configuration.ORIENTATION_UNDEFINED){
//if height and widht of screen are equal then
// it is square orientation
if(getOrient.getWidth()==getOrient.getHeight()){
orientation = Configuration.ORIENTATION_SQUARE;
}else{ //if widht is less than height than it is portrait
if(getOrient.getWidth() < getOrient.getHeight()){
orientation = Configuration.ORIENTATION_PORTRAIT;
}else{ // if it is not any of the above it will definitely be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
return orientation; // return value 1 is portrait and 2 is Landscape Mode
}
And you can also refer this example which represent the use of both the methods:
getOrientation and getRotationMatrix
http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
public int getScreenOrientation() {
// Query what the orientation currently really is.
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
return 1; // Portrait Mode
}else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
return 2; // Landscape mode
}
return 0;
}
protected void onResume() {
// change the screen orientation
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
setContentView(R.layout.portrait);
} else if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
setContentView(R.layout.landscape);
} else {
setContentView(R.layout.oops);
}
}
In my project i am rendering pdf, in landscape mode i am fitting to pagewidth and in portrait mode i am fitting to pageheight mode. can anybody suggest me how to do scrolling in landscape mode only and swiping in both orientations.
thank you in advance..
I would say the easiest way of doing this, assuming the scrolling is defines in your XML, is to create different layouts for each. somename_land.xml in the layout-land folder would have scrolling defined while somename_port.xml in layout-port would not. Both would have swiping. You then select the context in your code with something like this:
WindowManager winMan = (WindowManager) getBaseContext().getSystemService(Context.WINDOW_SERVICE);
if (winMan != null) {
int orientation = winMan.getDefaultDisplay().getOrientation();
if (orientation == 0) {
// Portrait
setContentView(R.layout.somename_port.xml);
} else if (orientation == 1) {
// Landscape
setContentView(R.layout.somename_land.xml);
}
}