三方Camera应用的方向总结(一)

本文探讨了在不同Android系统版本中,如何针对MTK_LCM_PHYSICL_ROTATION设置为270度或横屏模式下,修复getCameraInfo方向问题、SensorManager方向调整、QQ视频通话方向翻转,以及锁定屏幕方向防止旋转的技术解决方案。涉及API修改、ActivityThread操作及屏幕锁定策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

竖屏横用:MTK_LCM_PHYSICL_ROTATION = 270或者纯横屏导致的问题。

(1)getCameraInfo方向问题

//frameworks/base/core/java/android/hardware/Camera.java
public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) {
		//通过native的Jni来获取cameraInfo信息,可以在这里修改cameraInfo中的orientation信息
        _getCameraInfo(cameraId, cameraInfo);

		//(1)强制修改cameraInfo.orientation,修改Preview方向
+		if(ActivityThread.currentOpPackageName().equals("com.tencent.mm")){
+			if(cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK){
+				cameraInfo.orientation = 270;
+			}else{
+				cameraInfo.orientation = 90;
+			}
+		}

		//(2)强制修改个别界面(二维码界面)的sensor orientation值
+		if(ActivityThread.currentActivityThread().currentActivityName().equals("com.tencent.mm.plugin.scanner.ui.BaseScanUI")){
+			cameraInfo.orientation = 180;
+		}

        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
        IAudioService audioService = IAudioService.Stub.asInterface(b);
        try {
            if (audioService.isCameraSoundForced()) {
                // Only set this when sound is forced; otherwise let native code
                cameraInfo.canDisableShutterSound = false;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Audio service is unavailable for queries");
        }
    }
    
private native static void _getCameraInfo(int cameraId, CameraInfo cameraInfo);

 public static class CameraInfo {
        public static final int CAMERA_FACING_BACK = 0;
        public static final int CAMERA_FACING_FRONT = 1;

        /**
         * The direction that the camera faces. It should be
         * CAMERA_FACING_BACK or CAMERA_FACING_FRONT.
         */
        public int facing;
        public int orientation;

        /**
         * <p>Whether the shutter sound can be disabled.</p>
         */
        public boolean canDisableShutterSound;
    };
//frameworks/base/core/java/android/app/ActivityThread.java

	public static ActivityThread currentActivityThread() {
        return sCurrentActivityThread;
    }

	public String currentActivityName(){
		String classname = null;
		ActivityManager manager = (ActivityManager)getSystemContext().getSystemService(Context.ACTIVITY_SERVICE);
		ActivityManager.RunningTaskInfo info = manager.getRunningTasks(1).get(0);
		//String shortClassName = info.topActivity.getShortClassName();类名
		classname = info.topActivity.getClassName();//完整类名
		return classname;
	}

    public static String currentOpPackageName() {
        ActivityThread am = currentActivityThread();
        return (am != null && am.getApplication() != null)
                ? am.getApplication().getOpPackageName() : null;
    }

    public static String currentPackageName() {
        ActivityThread am = currentActivityThread();
        return (am != null && am.mBoundApplication != null)
            ? am.mBoundApplication.appInfo.packageName : null;
    }

    public static String currentProcessName() {
        ActivityThread am = currentActivityThread();
        return (am != null && am.mBoundApplication != null)
            ? am.mBoundApplication.processName : null;
    }

    public static Application currentApplication() {
        ActivityThread am = currentActivityThread();
        return am != null ? am.mInitialApplication : null;
    }

	public ApplicationThread getApplicationThread()
    {
        return mAppThread;
    }

	public Instrumentation getInstrumentation()
    {
        return mInstrumentation;
    }

 public Application getApplication() {
        return mInitialApplication;
    }

    public String getProcessName() {
        return mBoundApplication.processName;
    }

    public ContextImpl getSystemContext() {
        synchronized (this) {
            if (mSystemContext == null) {
                mSystemContext = ContextImpl.createSystemContext(this);
            }
            return mSystemContext;
        }
    }

(2)SystemSensorManager方向问题(拍照/预览)

拍照方向问题可通过修改gensor尝试,具体实现是对App的XY轴进行交换,在这个dispatchSensorEvent函数中可以对整个App的gsensor交换,也可以根据包名类名来单独更改某个界面。

//frameworks/base/core/java/android/hardware/SystemSensorManager.java

static final class SensorEventQueue extends BaseEventQueue 
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
                long timestamp) {
	final Sensor sensor = mManager.mHandleToSensor.get(handle);
	if (sensor == null) {
		return;
	}

//add start
//重力传感器
+	if(sensor.getType() == Sensor.TYPE_ACCELEROMETER && mManager.mContext.getBasePackageName().equalsIgnoreCase("com.tencent.mm")){
+		float tmp;
+		Log.d("debug_SensorEventQueue","x = " + values[0] + ",y = " + values[1] + ",z = " + values[2]);
+		tmp = values[0];
+		values[0] = values[1];
+		values[1] = -tmp;
+	}

	//tmp = values[0];
	//values[0] = -values[1];
	//values[1] = tmp;

	//修改带陀螺仪的应用问题,这里以腾讯游戏和平精英作为测试
			if(ActivityThread.currentOpPackageName().contains("com.tencent.tmgp")){
			if(sensor.getType() == Sensor.TYPE_GYROSCOPE){
				Log.d("wdd222_SensorEventQueue_gyro","x = " + values[0] + ",y = " + values[1] + ",z = " + values[2]);
                                tmp_gyro = values[0];
                                values[0] = values[1];
                                values[1] = -tmp_gyro;
			}
           	}

	//add end

	SensorEvent t = null;
	synchronized (mSensorsEvents) {
		t = mSensorsEvents.get(handle);
	}

	if (t == null) {
		return;
	}
	// Copy from the values array.
	System.arraycopy(values, 0, t.values, 0, t.values.length);
	t.timestamp = timestamp;
	t.accuracy = inAccuracy;
	t.sensor = sensor;
}
//frameworks/base/core/java/android/app/ContextImpl.java

 @Override
    public String getPackageName() {
        if (mPackageInfo != null) {
            return mPackageInfo.getPackageName();
        }
        // No mPackageInfo means this is a Context for the system itself,
        // and this here is its name.
        return "android";
    }

    /** @hide */
    @Override
    public String getBasePackageName() {
        return mBasePackageName != null ? mBasePackageName : getPackageName();
    }

    /** @hide */
    @Override
    public String getOpPackageName() {
        return mOpPackageName != null ? mOpPackageName : getBasePackageName();
    }

    @Override
    public ApplicationInfo getApplicationInfo() {
        if (mPackageInfo != null) {
            return mPackageInfo.getApplicationInfo();
        }
        throw new RuntimeException("Not supported in system context");
    }

@Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

@Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }
        return null;
    }

@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

    @Override
    public String getSystemServiceName(Class<?> serviceClass) {
        return SystemServiceRegistry.getSystemServiceName(serviceClass);
    }

(3)QQ视频通话画面旋转90(对方画面)

//frameworks/base/core/java/android/view/OrientationEventListener.java

+	import android.app.ActivityThread;
public abstract class OrientationEventListener {

	private int mOrientation = ORIENTATION_UNKNOWN;
	private SensorManager mSensorManager;
	private Sensor mSensor;
	private SensorEventListener mSensorEventListener;

	if (mOldListener != null) {
		mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);
	}
	if (orientation != mOrientation) {
		mOrientation = orientation;
+		if(ActivityThread.currentOpPackageName().equals("com.tencent.mobileqq")){
+			orientation += 90;
+		}
		onOrientationChanged(orientation);
	}
}

//or
//QQ视频通话图像方向颠倒
//ro.qq.camera.sensor = 3;

(4)强制修改横/竖屏,防止屏幕旋转(9.0)

竖屏横用,微信视频通话显示角度异常。

(1)App设置:

xml配置:android:screenOrientation="portrait"
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

一旦锁住屏幕,当orientation方向改变的时候就不会触发onConfigurationChanged。

//frameworks/base/core/java/android/content/pm/ActivityInfo.java

public int launchMode;
//standard
public static final int LAUNCH_MULTIPLE = 0;
public static final int LAUNCH_SINGLE_TOP = 1;
public static final int LAUNCH_SINGLE_TASK = 2;
public static final int LAUNCH_SINGLE_INSTANCE = 3;

public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
public static final int SCREEN_ORIENTATION_UNSET = -2;
public static final int SCREEN_ORIENTATION_UNSPECIFIED = -1;//默认值,由系统决定
public static final int SCREEN_ORIENTATION_LANDSCAPE = 0;//强制横屏
public static final int SCREEN_ORIENTATION_PORTRAIT = 1;//强制竖屏
public static final int SCREEN_ORIENTATION_USER = 2;//用户当前的首选方向
public static final int SCREEN_ORIENTATION_BEHIND = 3;//与前一个activity方向相同
public static final int SCREEN_ORIENTATION_SENSOR = 4;//根据物理传感器方向转动,用户90度、180度、270度旋转手机方向,activity都更着变化
public static final int SCREEN_ORIENTATION_NOSENSOR = 5;//旋转设备时候,界面不会跟着旋转。初始化界面方向由系统控制
public static final int SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6;//(横屏的旋转,不会出现竖屏的现象)根据传感器定位方向,旋转手机180度界面旋转。一般横屏游戏会是这个属性。
public static final int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7;//(竖屏的旋转,不会出现横屏的现象)根据传感器定位方向,旋转手机180度界面会旋转。
public static final int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
public static final int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10;
public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11;
public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12;
public static final int SCREEN_ORIENTATION_FULL_USER = 13;
public static final int SCREEN_ORIENTATION_LOCKED = 14;

设置屏幕方向的示例代码:

requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置成全屏模式

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE););//强制为横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏

//动态更改屏幕方向
	if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)  
	{  
    	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  
    }  
 	else if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)  
	{  
	    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
	}  

(2)在framework层设置req,即固定屏幕方向:

(A)Android 9

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
boolean updateOrientationFromAppTokensLocked(int displayId, boolean forceUpdate) {
        long ident = Binder.clearCallingIdentity();
        try {
            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-           final int req = dc.getOrientation();
+           int req = dc.getOrientation();
+			for(WindowState win1 : mWindowMap.values()){
+				if(win1.getWindowTag().toString().contains("xxxx") && win1.isVisibleNow() && win1.isOnScreen()){
+					req = 0;
+					break;
+				}
+			}
            if (req != dc.getLastOrientation() || forceUpdate) {
                dc.setLastOrientation(req);
                if (dc.isDefaultDisplay) {
                    mPolicy.setCurrentOrientationLw(req);
                }
                return dc.updateRotationUnchecked(forceUpdate);
            }
            return false;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

(B)Android 10

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

+	public WindowHashMap getWindowHashMap(){
+		return mWindowMap;
+	}

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

//(1)
	private boolean mUpdateImeTarget;
	private boolean mTmpInitial;
	private int mMaxUiWidth;
+ 	private WindowHashMap mWindowMap = new WindowHashMap();

//(2)
DisplayContent(Display display, WindowManagerService service,
            WallpaperController wallpaperController, DisplayWindowController controller) {
        super(service);
        setController(controller);
        if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
            throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                    + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
                    + " new=" + display);
        }

+		mWindowMap = service.getWindowHashMap();
        mDisplay = display;
        mDisplayId = display.getDisplayId();
        mWallpaperController = wallpaperController;
        display.getDisplayInfo(mDisplayInfo);


//(3)
	private boolean updateOrientationFromAppTokens(boolean forceUpdate) {
-           final int req = dc.getOrientation();
+           int req = dc.getOrientation();
+ 			if(mWindowMap != null){
+				for(WindowState win1 : mWindowMap.values()){
+					if(win1.getWindowTag().toString().contains("com.whatsapp") && win1.isVisibleNow() && win1.isOnScreen()){
+						req = 0;
+						break;
+					}
+				}
+		 	}
            if (req != dc.getLastOrientation() || forceUpdate) 
    }

(C)Android 11

可通过修改updateOrientation(boolean forceUpdate)来固定方向

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

	private boolean updateOrientation(boolean forceUpdate){
		final int orientation = getOrientation();
		//可以通过包名和类名来修改此处的orientation值,从而达到固定横竖屏的作用

		return mDisplayRotation.updateOrientation(orientation, forceUpdate);
	}

//或者

//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    int rotationForOrientation(@ScreenOrientation int orientation,
            @Surface.Rotation int lastRotation) {
    
		switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                // Return portrait unless overridden.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
				
				//add start
                if (SystemProperties.getBoolean("ro.camera.spin", false)) {
                    if(ActivityThread.currentActivityThread().currentActivityName().contains("com.android.incallui.InCallActivity")){
                        mPortraitRotation = 0;
                    } else {
                        mPortraitRotation = 3;
                    }
                }
                //add end

                return mPortraitRotation;
}

以上修改可通过获取当前应用 包名和类名来进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪舞飞影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值