Sort out how sensor data streams and frames rotate the screen.

Source: Internet
Author: User
Tags image flip

This article describes the entire process of transferring sensor data from the driver to the application, as well as the problem of data correction.

How can an application set its own orientation to change with the device's inclination? Android: screenorientation in the androidmanifest. xml file. Here we will trace its internal mechanism.
First look at one of the most critical components:/frameworks/base/CORE/Java/Android/View/javasworientationlistener. Java
This interface registers an accelerator and converts the data of the accelerator to orientation. This API is not public to the application. When I read the source code of android2.3, I found that it is only used by phonewindowmanager.
/Frameworks/base/policy/../phonewindowmanager. Java
Phonwwindowmanager registers a javasworientationlistener to obtain the orientation of the current device asynchronously. The orientation of the application interface is managed based on the value set by the application in androidmanifest. xml. The following are two related code snippets in phonewindowmanager. java.

Public void onorientationchanged (INT rotation) {// send updates based on orientation value if (locallogv) log. V (TAG, "onorientationchanged, rotation changed to" + rotation); try {mwindowmanager. setrotation (rotation, false, mfancyrotationanimation);} catch (RemoteException e) {// ignore }}...... switch (orientation) {// This value is the orientation of the current device's screen rotation. Then, combine the Android: configchanges attribute value set by the application to determine the orientation of the application interface. The priority of the value set by the application is greater than that determined by the sensor. Case activityinfo. screen_orientation_portrait: // always return portrait if orientation set to portrait return mportraitrotation; Case activityinfo. screen_orientation_landscape: // always return landscape if orientation set to landscape return mlandscaperotation; Case activityinfo. screen_orientation_reverse_portrait: // always return portrait if orientation set to portrait return mupsidedownrotation; Case activityinfo. screen_orientation_reverse_landscape: // always return seascape if orientation set to reverse landscape return mseascaperotation; Case activityinfo. screen_orientation_sensor_landscape: // return either landscape rotation based on the sensor morientationlistener. setallow180rotation (islandscapeorseascape (surface. rotation_180); Return getcurrentlandscaperotation (lastrotation); Case Activityinfo. screen_orientation_sensor_portrait: morientationlistener. setallow180rotation (! Islandscapeorseascape (surface. rotation_180); Return getcurrentportraitrotation (lastrotation );}

Let the implementation principle of automatically rotating the application as the screen goes. I didn't have much effort to solve this step. I opened sensortest on the board, compared the data on the three axes of XYZ and milestone, and changed the positive and negative values. However, to solve the Z axis reversal problem during teeter operation, we have to dig deeper.

Phonewindowmanager. Java has the following sentence:
Mwindowmanager. setrotation (rotation, false, mfancyrotationanimation );
When phonewindowmanager learns that the screen direction changes through the listener worientationlistener, it will notify windowmanagerservice (/frameworks/base/service/../windowmanagerservice. Java)
Windowmanagerservice has a set of listeners: mrotationwatchers. To listen to the changes in the screen direction, a listener will be registered here. This is what sensormanager does. Then, use the Asynchronous Method below to obtain the current screen direction

public void onRotationChanged(int rotation) {        synchronized(sListeners) {            sRotation  = rotation;        }    }static int getRotation() {        synchronized(sListeners) {            return sRotation;        }    }

What is the role of sensormanager for this value? Let's see where sensormanager. getrotation () is used. There is only one method: mapsensordatatowindow
When an activity registers a sensor event listener, it always obtains the sensor event asynchronously through the interface. Here, new and old versions are differentiated. In old versions, before android1.5, before the sensor event is distributed to the listener (onsensorchanged), this method is always used. The new listening interface is sensoreventlistener, and there is no processing method before distribution. Let's take a look at this method. It was originally used to convert the coordinate system. When the interface direction of an application changes with the screen, the sensor data transmitted to it through the asynchronous distribution interface must also be converted from the sensor coordinate system to the application coordinate system. Assuming that the screen is in the vertical direction by default, the value in the sensorevent distributed to the screen is the same as the data read from Hal sensor. C on the frameworks layer. When the right side of the device is raised, the screen is switched to the horizontal screen, and the application interface is rotated 90 degrees. At this time, before distributing a sensorevent to an application, you need to rotate your coordinate system 90 degrees clockwise.
In the new version of the interface, sensor data is directly distributed to the application through the sensoreventlistener. In earlier versions of the interface, a coordinate system transformation is performed on sensor data based on the orientation of current device rotation before distribution. Everything is clear here. Mongoworientationlistener uses sensormanager's accelerator data to create the screen rotation direction, which is used by sensormanager to be compatible with earlier sensorlistener interfaces. It can be said that if you do not consider interfaces compatible with the old version, sensormanager does not need to register a listener in windowmanagerservice. Java to listen to the orientation of the current device's screen rotation, just distribute it directly. The sensormanager code may be reduced by more than half. The framework layer throws a series of Apis related to sensorlistener.
There is also a part between sensor. C and sensormanager at the Hal layer. This part integrates the sensor data read in sensor. c into a service for sensormanager. The API layer and Hal layer are well isolated. However, from the perspective of data processing, it only plays the role of a Data Transmitter without any changes to the data.
The above is finished, and the Hal layer is next. Different manufacturers have different writing methods. Some have formed their own framework to integrate all the sensors. Let's go through the Hal framework and directly go to sensor. C. Here I am most concerned about how to interact with the sensor driver and what information is passed to the upper layer. Another complicated frameworks is to encapsulate the interfaces provided by sensor. C. Sensor data has never been changed. Here is a brief description of sensor. the general function of C, more detailed analysis can refer to this article (http://blog.csdn.net/a345017062/article/details/6558401 ), I wrote a C ++ program that can run through the ADB or serial port to demonstrate how to control the driver and read data.
1. provide an interface for the upper layer to obtain the sensor list. This is written to sensor. C. When you port frameworks to a device, you need to modify the file according to the sensor on the device.
2. Provides a control interface for the upper layer: active/deactive a sensor. Set the delay value of a sensor (that is, the frequency of obtaining the sensor data. For example, if it is set to 200,000, the driver sends data up every 200 milliseconds ). Read the data of a sensor.
Now we know that when debugging the sensor, we can only start in two places:
1./frameworks/base/CORE/Java/Android/View/javasworientation. Java to correct the correspondence between accelerator data and screen rotation direction.
2./hardware/libhardware/modules/sensor. C. The data on the driver is preliminarily corrected. For this step, you can refer to the corrected machine (I use my milestone), and then run sensortest (some on the Internet, the data we read is the same as the data we read. As mentioned earlier, in the new version (1.5 and later) interface, the data stream passes through the sensor. c-> sensorservice-> sensormanager, which has never been changed throughout the process of being distributed to the application through onsensorchanged. As for the sensormanager correction in the old version, let him eat it. (I did a lot of work here at the beginning, and finally found that the deprecate interface has been deprecate since 1.5. Please allow me to repeat shit !!!).

Now, this article is finished. Don't worry about me. I will talk about the last time: the sensor data has been sensor. c reads data from the driver until the onsensorchanged interface is passed to the application. data has not been changed throughout the process. This is important because it means that the frameworks layer does not require sensor correction. We only need to find the two arrays (thresholds and rotate_to) in javasworientationlistener, and then adjust the orientation of the screen.

Supplement:

Problems with compatibility with sensor interfaces.
After the screen is rotated, the sensor data also needs to be changed to the coordinate system, which is different from the previous understanding and must be corrected.
After the sensor is debugged, the screen can be rotated normally, but the teeter of the HTC mobile phone is faulty. After tracking, we found that teeter is still using the old sensor listening interface onsensorchanged (INT sensor, float [] value ). Therefore, you need to modify/frameworks/base/CORE/Java/Android/hardware/sensormanager. the mapsensordatatowindow method in Java, which converts the raw data read by Hal to the data of the old interface (using onsensorchanged (int
Sensor, float [] value) received data ).
Currently, only the 0-degree and 90-degree directions are supported. Therefore, we have modified the descriworientationlistener so that all APIs can only rotate in these two directions:
1. The mallow180rotation variable is always set to false.
2. Modify the rotate_to array and change all 270 degrees to 90 degrees.

In addition, the poll data interface in sensor. C has an int type return value, indicating the number of sensors_event_t read. This value must be equal to the actual number. In my own program, an accelerator is actually read, but no matter how many are read, the number of current sensors is returned. In this way, when the application uses the old interface onsensorchanged (INT sensor, float [] value) to listen, in addition to a normal data, it will also read (sensor. the Return Value of the poll data function in C-1) redundant data with all 0 values.

Supplement: 2011.7.26

If you set the screen direction through androidmanifest. XML, the screen direction cannot be changed after installation, and the screen direction setting in the program does not have this restriction. Mainly rely on these two APIs: getrequestedorientation () and setrequestedorientation (activityinfo. screen_orientation_portrait)
After the two APIs are converted using activitymanagerservice. Java, the windowmanagerservice method is called with the same name. Each activity has an appwindowtoken on the windowmanagerservice side, and the screen direction information is stored here.

Phonewindowmanager automatically determines the screen direction based on the physical characteristics of the screen. See the code below:

if (mPortraitRotation < 0) {    // Initialize the rotation angles for each orientation once.    Display d = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))            .getDefaultDisplay();    if (d.getWidth() > d.getHeight()) {        mPortraitRotation = Surface.ROTATION_90;        mLandscapeRotation = Surface.ROTATION_0;        mUpsideDownRotation = Surface.ROTATION_270;        mSeascapeRotation = Surface.ROTATION_180;    } else {        mPortraitRotation = Surface.ROTATION_0;        mLandscapeRotation = Surface.ROTATION_90;        mUpsideDownRotation = Surface.ROTATION_180;        mSeascapeRotation = Surface.ROTATION_270;    }}

Here, D. getwidth () and D. getheight () obtain the width and height of the physical screen. In general, tablets and mobile phones are different. The flat panel is large in width to height (in Landscape mode when 0 degrees, right turn 90 degrees into porit mode), and the mobile phone is high in width to width (in porit mode when 0 degrees, turn right 90 degrees to enter Landscape mode ). If the application only cares about whether the current screen is a landscape screen or a portrait screen, and does not directly use sensors, there is no problem. If sensors are used directly like gravity sensing games, you need to convert the sensor data based on the coordinate system of the physical screen. Otherwise, the coordinate system will be chaotic.

Here I met the range thunder and teeter games. None of them passed the above d. getwidth () and D. getheight () is used to detect the physical screen of a device. It determines the landscape and porit modes, but directly assumes that the device is in the same mode as the mobile phone. Since the game runs in Landscape mode, they all turn sensor data right 90 degrees. In this way, there is no problem on the mobile phone, but it should not be converted on the tablet. This is because the Landscape mode is used by default when the width ratio of the physical screen is large.

Supplemented by 2011.8.2

After reading the questions raised by the readers on the first floor, I will add a solution to the problem I mentioned.
For the new interface, we can adjust the coordinate system before the sensorevent is passed out in the onsensorchangedlocked interface. However, after adjusting the game using the new interface correctly, it is found that the game using the old interface is messy, and the orientation of the screen is also messy.
We already know that mongoworientationlistener uses sensormanager to determine the orientation of screen rotation. sensormanager then converts the coordinate system of the sensor read from the bottom layer based on the orientation of rotation, and then transmits it to onsensorchanged (sensorevent event ). The old interface onsensorchanged (INT sensor, float [] value) uses the data of onsensorchanged (sensorevent event) to perform Coordinate System Conversion.
Therefore, you also need to convert the coordinates in the new interface in the old interface. In this way, the old interface can also be used for games. But what if the screen is not rotated? This problem falls into a strange circle. Now, let's record my solution:
First, we add an attribute for sensorevent to record the sensor's raw data:

/*** This annotation must be added, or you must update the API before compiling. * {@ Hide} */float [] originalvalue = new float [3];

It is used in three places. In onsensorchangedlocked, a coordinate system transformation is performed for the new interface, while the legacylistener. onsensorchanged interface performs a sit-System Conversion for the old interface, and the screen rotation direction is calculated based on sensor data in wiindoworientation.
The original data stored in originalvalue is used for calculation during the calculation in these three locations, and the calculation result is placed in sensorevent. value. In this way, which interface does not adjust which interface, because the original data is used, so it does not affect each other, and there will no longer be a task of pressing the hoist to crash.

Supplement:

However, if the developer is serious about the game, they should not simply consider the landscape/porit mode, but use the display. getrotation () is used to obtain the actual rotation angle of the screen to convert the gsensor data coordinate system. Then his program is tragic on our board:
Get the current screen rotation angle:

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display mDisplay = windowManager.getDefaultDisplay();mDisplay.getRotation();

Unfortunately, gallery3d does this for image flip effects. The following code snippet is located in the/packages/apps/gallery3d/src/COM/cooliris/Media/gridinputprocessor. Java file, and the image skew is calculated based on the rotation angle of the screen. We have to make it use the original data. The following are the pre-modification and post-modification codes.
Before modification:

    public void onSensorChanged(RenderView view, SensorEvent event, int state) {        if (mZoomGesture)            return;        switch (event.sensor.getType()) {        case Sensor.TYPE_ACCELEROMETER:            float[] values = event.values;            float valueToUse;            switch (mDisplay.getRotation()) {            case Surface.ROTATION_0:                valueToUse = values[0];                break;            case Surface.ROTATION_90:                valueToUse = -event.values[1];                break;            case Surface.ROTATION_180:                valueToUse = -event.values[0];                break;            case Surface.ROTATION_270:                valueToUse =  event.values[1];                break;            default:                valueToUse = 0.0f;            }... ...        }    }

Modified code:
 

   public void onSensorChanged(RenderView view, SensorEvent event, int state) {        if (mZoomGesture)            return;        switch (event.sensor.getType()) {        case Sensor.TYPE_ACCELEROMETER:            float[] values = event.original;            float valueToUse;            switch (mDisplay.getRotation()) {            case Surface.ROTATION_0:                valueToUse = values[0];                break;            case Surface.ROTATION_90:                valueToUse = -values[1];                break;            case Surface.ROTATION_180:                valueToUse = -values[0];                break;            case Surface.ROTATION_270:                valueToUse =  values[1];                break;            default:                valueToUse = 0.0f;            }           ... ...        }    }

We look forward to the early arrival of the tablet and mobile phone system.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.