blob: b2a504b629d6427c8171731882131fb6fc2c255c [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugins.camera;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.provider.Settings;
import android.view.Display;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.WindowManager;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
class DeviceOrientationManager {
private static final IntentFilter orientationIntentFilter =
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
private final Activity activity;
private final DartMessenger messenger;
private final boolean isFrontFacing;
private final int sensorOrientation;
private PlatformChannel.DeviceOrientation lastOrientation;
private OrientationEventListener orientationEventListener;
private BroadcastReceiver broadcastReceiver;
public DeviceOrientationManager(
Activity activity, DartMessenger messenger, boolean isFrontFacing, int sensorOrientation) {
this.activity = activity;
this.messenger = messenger;
this.isFrontFacing = isFrontFacing;
this.sensorOrientation = sensorOrientation;
}
public void start() {
startSensorListener();
startUIListener();
}
public void stop() {
stopSensorListener();
stopUIListener();
}
public int getMediaOrientation() {
return this.getMediaOrientation(this.lastOrientation);
}
public int getMediaOrientation(PlatformChannel.DeviceOrientation orientation) {
int angle = 0;
// Fallback to device orientation when the orientation value is null
if (orientation == null) {
orientation = getUIOrientation();
}
switch (orientation) {
case PORTRAIT_UP:
angle = 0;
break;
case PORTRAIT_DOWN:
angle = 180;
break;
case LANDSCAPE_LEFT:
angle = 90;
break;
case LANDSCAPE_RIGHT:
angle = 270;
break;
}
if (isFrontFacing) angle *= -1;
return (angle + sensorOrientation + 360) % 360;
}
private void startSensorListener() {
if (orientationEventListener != null) return;
orientationEventListener =
new OrientationEventListener(activity, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int angle) {
if (!isSystemAutoRotationLocked()) {
PlatformChannel.DeviceOrientation newOrientation = calculateSensorOrientation(angle);
if (!newOrientation.equals(lastOrientation)) {
lastOrientation = newOrientation;
messenger.sendDeviceOrientationChangeEvent(newOrientation);
}
}
}
};
if (orientationEventListener.canDetectOrientation()) {
orientationEventListener.enable();
}
}
private void startUIListener() {
if (broadcastReceiver != null) return;
broadcastReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (isSystemAutoRotationLocked()) {
PlatformChannel.DeviceOrientation orientation = getUIOrientation();
if (!orientation.equals(lastOrientation)) {
lastOrientation = orientation;
messenger.sendDeviceOrientationChangeEvent(orientation);
}
}
}
};
activity.registerReceiver(broadcastReceiver, orientationIntentFilter);
broadcastReceiver.onReceive(activity, null);
}
private void stopSensorListener() {
if (orientationEventListener == null) return;
orientationEventListener.disable();
orientationEventListener = null;
}
private void stopUIListener() {
if (broadcastReceiver == null) return;
activity.unregisterReceiver(broadcastReceiver);
broadcastReceiver = null;
}
private boolean isSystemAutoRotationLocked() {
return android.provider.Settings.System.getInt(
activity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0)
!= 1;
}
private PlatformChannel.DeviceOrientation getUIOrientation() {
final int rotation = getDisplay().getRotation();
final int orientation = activity.getResources().getConfiguration().orientation;
switch (orientation) {
case Configuration.ORIENTATION_PORTRAIT:
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
return PlatformChannel.DeviceOrientation.PORTRAIT_UP;
} else {
return PlatformChannel.DeviceOrientation.PORTRAIT_DOWN;
}
case Configuration.ORIENTATION_LANDSCAPE:
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
return PlatformChannel.DeviceOrientation.LANDSCAPE_LEFT;
} else {
return PlatformChannel.DeviceOrientation.LANDSCAPE_RIGHT;
}
default:
return PlatformChannel.DeviceOrientation.PORTRAIT_UP;
}
}
private PlatformChannel.DeviceOrientation calculateSensorOrientation(int angle) {
final int tolerance = 45;
angle += tolerance;
// Orientation is 0 in the default orientation mode. This is portait-mode for phones
// and landscape for tablets. We have to compensate for this by calculating the default
// orientation, and apply an offset accordingly.
int defaultDeviceOrientation = getDeviceDefaultOrientation();
if (defaultDeviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
angle += 90;
}
// Determine the orientation
angle = angle % 360;
return new PlatformChannel.DeviceOrientation[] {
PlatformChannel.DeviceOrientation.PORTRAIT_UP,
PlatformChannel.DeviceOrientation.LANDSCAPE_LEFT,
PlatformChannel.DeviceOrientation.PORTRAIT_DOWN,
PlatformChannel.DeviceOrientation.LANDSCAPE_RIGHT,
}
[angle / 90];
}
private int getDeviceDefaultOrientation() {
Configuration config = activity.getResources().getConfiguration();
int rotation = getDisplay().getRotation();
if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
&& config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|| ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)
&& config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
return Configuration.ORIENTATION_LANDSCAPE;
} else {
return Configuration.ORIENTATION_PORTRAIT;
}
}
@SuppressWarnings("deprecation")
private Display getDisplay() {
return ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
}
}