文章目录
-
- 如何判断手机的水平和垂直屏幕?
- 采用加速度计实现
-
- 接口使用
- 使用加速度计和磁力计获得手机的倾斜角度
- 加速传感器在Android水平和垂直屏切换的应用
如何判断手机的水平和垂直屏幕?
这个问题取决于手机上的传感器 一般来说,手机和磁力计来判断手机的倾斜角度 若只判断横竖屏 只能使用加速度计 加速度计普及度高
采用加速度计实现
参考了AOSP的通用实现
public class AccelerometerListener {
// Device orientation public static final int ORIENTATION_UNKNOWN = 0; public static final int ORIENTATION_VERTICAL = 1; public static final int ORIENTATION_HORIZONTAL = 2; private static final String TAG = "AccelerometerListener"; private static final boolean DEBUG = true; private static final boolean VDEBUG = false; private static final int ORIENTATION_CHANGED = 1234; private static final int VERTICAL_DEBOUNCE = 100; private static final int HORIZONTAL_DEBOUNCE = 500; private static final double VERTICAL_ANGLE = 50.0; private SensorManager sensorManager; private Sensor sensor; // mOrientation is the orientation value most recently reported to the client. private int orientation; // mPendingOrientation is the latest orientation computed based on the sensor value. // This is sent to the client after a rebounce delay, at which point it is copied to // mOrientation. private int pendingOrientation;
private OrientationListener listener;
Handler handler =
new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ORIENTATION_CHANGED:
synchronized (this) {
orientation = pendingOrientation;
if (DEBUG) {
LogUtil.d(
TAG,
"orientation: "
+ (orientation == ORIENTATION_HORIZONTAL
? "horizontal"
: (orientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
}
if (listener != null) {
listener.orientationChanged(orientation);
}
}
break;
}
}
};
SensorEventListener sensorListener =
new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
onSensorEvent(event.values[0], event.values[1], event.values[2]);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
};
public AccelerometerListener(Context context) {
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
public void setListener(OrientationListener listener) {
this.listener = listener;
}
public void enable(boolean enable) {
if (DEBUG) {
LogUtil.d(TAG, "enable(" + enable + ")");
}
synchronized (this) {
if (enable) {
orientation = ORIENTATION_UNKNOWN;
pendingOrientation = ORIENTATION_UNKNOWN;
sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
} else {
sensorManager.unregisterListener(sensorListener);
handler.removeMessages(ORIENTATION_CHANGED);
}
}
}
private void setOrientation(int orientation) {
synchronized (this) {
if (pendingOrientation == orientation) {
// Pending orientation has not changed, so do nothing.
return;
}
// Cancel any pending messages.
// We will either start a new timer or cancel alltogether
// if the orientation has not changed.
handler.removeMessages(ORIENTATION_CHANGED);
if (this.orientation != orientation) {
// Set timer to send an event if the orientation has changed since its
// previously reported value.
pendingOrientation = orientation;
final Message m = handler.obtainMessage(ORIENTATION_CHANGED);
// set delay to our debounce timeout
int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);
handler.sendMessageDelayed(m, delay);
} else {
// no message is pending
pendingOrientation = ORIENTATION_UNKNOWN;
}
}
}
private void onSensorEvent(double x, double y, double z) {
if (VDEBUG) {
LogUtil.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
}
// If some values are exactly zero, then likely the sensor is not powered up yet.
// ignore these events to avoid false horizontal positives.
if (x == 0.0 || y == 0.0 || z == 0.0) {
return;
}
// magnitude of the acceleration vector projected onto XY plane
final double xy = Math.hypot(x, y);
// compute the vertical angle
double angle = Math.atan2(xy, z);
// convert to degrees
angle = angle * 180.0 / Math.PI;
final int orientation =
(angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
if (VDEBUG) {
LogUtil.d(TAG, "angle: " + angle + " orientation: " + orientation);
}
setOrientation(orientation);
}
public interface OrientationListener {
void orientationChanged(int orientation);
}
}
接口使用
public class ProximitySensor implements AccelerometerListener.OrientationListener, InCallStateListener, AudioModeListener { private static final String TAG = ProximitySensor.class.getSimpleName(); private final PowerManager powerManager; private final PowerManager.WakeLock proximityWakeLock; private final AudioModeProvider audioModeProvider; private final AccelerometerListener accelerometerListener; private final ProximityDisplayListener displayListener; private int orientation = AccelerometerListener.ORIENTATION_UNKNOWN; private boolean uiShowing = false; private boolean isPhoneOffhook = false; private boolean dialpadVisible; private boolean isAttemptingVideoCall; private boolean isVideoCall; private boolean isRttCall; public ProximitySensor( @NonNull Context context, @NonNull AudioModeProvider audioModeProvider, @NonNull AccelerometerListener accelerometerListener) { Trace.beginSection("ProximitySensor.Constructor"); powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { proximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG); } else { LogUtil.i("ProximitySensor.constructor", "Device does not support proximity wake lock."); proximityWakeLock = null; } this.accelerometerListener = accelerometerListener; this.accelerometerListener.setListener(this); displayListener = new ProximityDisplayListener( (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE)); displayListener.register(); this.audioModeProvider = audioModeProvider; this.audioModeProvider.addListener(this); Trace.endSection(); } public void tearDown() { audioModeProvider.removeListener(this); accelerometerListener.enable(false); displayListener.unregister(); turnOffProximitySensor(true); } /** Called to identify when the device is laid down flat. */ @Override public void orientationChanged(int orientation) { this.orientation = orientation; updateProximitySensorMode(); } /** Called to keep track of the overall UI state. */ @Override public void onStateChange(InCallState oldState, InCallState newState, CallList callList) { // We ignore incoming state because we do not want to enable proximity // sensor during incoming call screen. We check hasLiveCall() because a disconnected call // can also put the in-call screen in the INCALL state. boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall(); boolean isOffhook = InCallState.PENDING_OUTGOING == newState || InCallState.OUTGOING == newState || hasOngoingCall; DialerCall activeCall = callList.getActiveCall(); boolean isVideoCall = activeCall != null && activeCall.isVideoCall(); boolean isRttCall = activeCall != null && activeCall.isActiveRttCall(); if (isOffhook != isPhoneOffhook || this.isVideoCall != isVideoCall || this.isRttCall != isRttCall) { isPhoneOffhook = isOffhook; this.isVideoCall = isVideoCall; this.isRttCall = isRttCall; orientation = AccelerometerListener.ORIENTATION_UNKNOWN; accelerometerListener.enable(isPhoneOffhook); updateProximitySensorMode(); } } @Override public void onAudioStateChanged(CallAudioState audioState) { updateProximitySensorMode(); } public void onDialpadVisible(boolean visible) { dialpadVisible = visible; updateProximitySensorMode(); } public void setIsAttemptingVideoCall(boolean isAttemptingVideoCall) { LogUtil.i( "ProximitySensor.setIsAttemptingVideoCall", "isAttemptingVideoCall: %b", isAttemptingVideoCall); this.isAttemptingVideoCall = isAttemptingVideoCall; updateProximitySensorMode(); } /** Used to save when the UI goes in and out of the foreground. */ public void onInCallShowing(boolean showing) { if (showing) { uiShowing = true; // We only consider the UI not showing for instances where another app took the foreground. // If we stopped showing because the screen is off, we still consider that showing. } else if (powerManager.isScreenOn()) { uiShowing = false; } updateProximitySensorMode(); } void onDisplayStateChanged(boolean isDisplayOn) { LogUtil.i("ProximitySensor.onDisplayStateChanged", "isDisplayOn: %b", isDisplayOn); accelerometerListener.enable(isDisplayOn); } /** * TODO: There is no way to determine if a screen is off due to proximity or if it is legitimately * off, but if ever we can do that in the future, it would be useful here. Until then, this * function will simply return true of the screen is off. TODO: Investigate whether this can be * replaced with the ProximityDisplayListener. */ public boolean isScreenReallyOff() { return !powerManager.isScreenOn(); } private void turnOnProximitySensor() { if (proximityWakeLock != null) { if (!proximityWakeLock.isHeld()) { LogUtil.i("ProximitySensor.turnOnProximitySensor", "acquiring wake lock"); proximityWakeLock.acquire(); } else { LogUtil.i("ProximitySensor.turnOnProximitySensor", "wake lock already acquired"); } } } private void turnOffProximitySensor(boolean screenOnImmediately) { if (proximityWakeLock != null) { if (proximityWakeLock.isHeld()) { LogUtil.i("ProximitySensor.turnOffProximitySensor", "releasing wake lock"); int flags = (screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); proximityWakeLock.release(flags); } else { LogUtil.i("ProximitySensor.turnOffProximitySensor", "wake lock already released"); } } } /** * Updates the wake lock used to control proximity sensor behavior, based on the current state of * the phone. * * <p>On devices that have a proximity sensor, to avoid false touches during a call, we hold a * PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock whenever the phone is off hook. (When held, that wake * lock causes the screen to turn off automatically when the sensor detects an object close to the * screen.) * * <p>This method is a no-op for devices that don't have a proximity sensor. * * <p>Proximity wake lock will be released if any of the following conditions are true: the audio * is routed through bluetooth, a wired headset, or the speaker; the user requested, received a * request for, or is in a video call; or the phone is horizontal while in a call. */ private synchronized void updateProximitySensorMode() { Trace.beginSection("ProximitySensor.updateProximitySensorMode"); final int audioRoute = audioModeProvider.getAudioState().getRoute(); boolean screenOnImmediately = (CallAudioState.ROUTE_WIRED_HEADSET == audioRoute || CallAudioState.ROUTE_SPEAKER == audioRoute || CallAudioState.ROUTE_BLUETOOTH == audioRoute || isAttemptingVideoCall || isVideoCall || isRttCall); // We do not keep the screen off when the user is outside in-call screen and we are // horizontal, but we do not force it on when we become horizontal until the // proximity sensor goes negative. final boolean horizontal = (orientation == AccelerometerListener.ORIENTATION_HORIZONTAL); screenOnImmediately |= !uiShowing && horizontal; // We do not keep the screen off when dialpad is visible, we are horizontal, and // the in-call screen is being shown. // At that moment we're pretty sure users want to use it, instead of letting the // proximity sensor turn off the screen by their hands. screenOnImmediately |= dialpadVisible && horizontal; LogUtil.i( "ProximitySensor.updateProximitySensorMode", "screenOnImmediately: %b, dialPadVisible: %b, " + "offHook: %b, horizontal: %b, uiShowing: %b, audioRoute: %s", screenOnImmediately, dialpadVisible, isPhoneOffhook, orientation == AccelerometerListener.ORIENTATION_HORIZONTAL, uiShowing, CallAudioState.audioRouteToString(audioRoute)); if (isPhoneOffhook && !screenOnImmediately) { LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning on proximity sensor"); // Phone is in use! Arrange for the screen to turn off // automatically when the sensor detects a close object. turnOnProximitySensor(); } else { LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning off proximity sensor"); // Phone is either idle, or ringing. We don't want any special proximity sensor // behavior in either case. turnOffProximitySensor(screenOnImmediately); } Trace.endSection(); } /** * Implementation of a {@link DisplayListener} that maintains a binary state: Screen on vs screen * off. Used by the proximity sensor manager to decide whether or not it needs to listen to * accelerometer events. */ public class ProximityDisplayListener implements DisplayListener { private DisplayManager displayManager; private boolean isDisplayOn = true; ProximityDisplayListener(DisplayManager displayManager) { this.displayManager = displayManager; } void register() { displayManager.registerDisplayListener(this, null); } void unregister() { displayManager.unregisterDisplayListener(this); } @Override public void onDisplayRemoved(int displayId) { } @Override public 标签:
传感器has500360x95传感器opb620传感器225传感器opb615传感器