赞
踩
参考相关:
private static boolean isHuaweiFold(Context context) { if (!DeviceTypeUtils.INSTANCE.isHuaweiPhone()) { return false; } return context.getPackageManager().hasSystemFeature("com.huawei.hardware.sensor.posture"); } private static boolean isOppoFoldPhone(Context context) throws Throwable { boolean isFold = false; boolean isVerticalFold = false; Class<?> cls = Class.forName("com.oplus.content.OplusFeatureConfigManager"); Method instance = cls.getMethod("getInstance"); Object configManager = instance.invoke(null); Method hasFeature = cls.getDeclaredMethod("hasFeature", String.class); Object object = hasFeature.invoke(configManager, FEATURE_FOLD); if (object instanceof Boolean) { isFold = (boolean) object; } Object object2 = hasFeature.invoke(configManager, FEATURE_VERTICAL_FOLD); if (object2 instanceof Boolean) { isVerticalFold = (boolean) object2; } // 是横向折叠屏的同时,不是竖向折叠屏 return isFold && !isVerticalFold; }
这里的判断,首次是不准的,请做存储
WindowManagerConfig.Companion.getInstance().isUnFoldStatus()
具体代码:
package com.haohua.demo import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.MutableLiveData import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.window.layout.FoldingFeature import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo import com.yunzhijia.logsdk.YZJLog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class WindowManagerConfig private constructor(){ var uiStatus: MutableLiveData<FoldState> = MutableLiveData<FoldState>() private object SingletonHolder { val instance = WindowManagerConfig() } companion object { val instance = SingletonHolder.instance } // 是否折叠状态,正常手机状态 fun isNormalPhoneStatus(): Boolean { return uiStatus.value == FoldState.NORMAL_PHONE } fun isUnFoldStatus(): Boolean { return !isNormalPhoneStatus() // return uiStatus.value == FoldState.FLAT || uiStatus.value == FoldState.HALF_OPENED } fun register(activity: AppCompatActivity) { activity.lifecycleScope.launch(Dispatchers.Main) { activity.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate(activity) .windowLayoutInfo(activity) .collect { newLayoutInfo -> uiStatus.value = getCurrentFoldState(newLayoutInfo) YZJLog.d("im-fold", "折叠屏状态变化 = ${uiStatus.value}, act = ${activity.javaClass.simpleName}") } } } } private fun getCurrentFoldState(layoutInfo: WindowLayoutInfo): FoldState { for (displayFeature in layoutInfo.displayFeatures) { val foldFeature = displayFeature as? FoldingFeature foldFeature?.let { return when (it.state) { FoldingFeature.State.FLAT -> { FoldState.FLAT } FoldingFeature.State.HALF_OPENED -> { FoldState.HALF_OPENED } else -> { FoldState.NORMAL_PHONE } } } } return FoldState.NORMAL_PHONE } class FoldState private constructor(private val description: String) { override fun toString(): String { return description } public companion object { @JvmField val FLAT: FoldState = FoldState("FLAT") @JvmField val HALF_OPENED: FoldState = FoldState("HALF_OPENED") @JvmField val NORMAL_PHONE: FoldState = FoldState("NORMAL_PHONE") } } }
折叠屏资料
target api 升级到29的官方改动
https://developer.android.com/about/versions/10/behavior-changes-10?hl=zh-cn#foldables
折叠屏适配介绍文档
https://developer.android.com/guide/topics/ui/foldables?hl=zh-cn
集成文档 WindowManager
https://developer.android.com/jetpack/androidx/releases/window?hl=zh-cn#groovy
识别折叠屏状态的参数说明
https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature
官方提供的折叠屏的demo
https://github.com/android/user-interface-samples/tree/main/WindowManager
继承折叠屏的步骤说明
https://developer.android.com/codelabs/android-window-manager-dual-screen-foldables?hl=zh-cn#6
三星适配指南:
http://samsung.smarterapps.cn/index.php?app=home&mod=Public&act=help&type=11&id=92
Android 折叠屏技术发展与适配
https://blog.51cto.com/u_15375308/5071743
public static boolean isFoldDisplay(Context context) {
final String KEY = “config_lidControlsDisplayFold”;
int id = context.getResources().getIdentifier(KEY, “bool”, “android”);
if (id > 0) {
return context.getResources().getBoolean(id);
}
return false;
}
/**
代码略。
判断代码(仅参考):
public static boolean isRealFoldPhone(Context context) {
// foldPhone 如果有值,直接返回,每次只读取一次配置,不需要反复读取
if (isRealFoldPhone != null && isRealFoldPhone) {
return isRealFoldPhone;
}
if (context == null) {
return false;
}
// 1、通过系统参数进行判断,是否是折叠屏手机 isRealFoldPhone = isFoldDisplay(context); if (!isRealFoldPhone) { boolean isFoldUiConfig = isFoldUIConfig(context); if (isFoldUiConfig) { isRealFoldPhone = true; } else { return false; } } YZJLog.d("im-fold", "是否是折叠屏:" + isRealFoldPhone); // 3、status补偿也不能完全确定,在折叠的情况下,获取到的值是异常的,最后在sp里面,看是否有记录 if (isRealFoldPhone) { // 是折叠屏手机,先记录下来,后续使用,每次进记录一次 if (FoldUIConfigUtils.isRealFoldPhone == null) { RouterManager.AppFold.getAppFoldService().setFoldPhone(true); } FoldUIConfigUtils.isFoldPhone = true; } else { // 非折叠屏手机,并且未赋值,从sp里面读值,可以直接拦截,不调用底层方法,暂时需要考虑默认配置,不处理 if (FoldUIConfigUtils.isFoldPhone == null) { FoldUIConfigUtils.isFoldPhone = RouterManager.AppFold.getAppFoldService().isFoldPhone(context instanceof Activity ? (Activity) context : null); } } return FoldUIConfigUtils.isFoldPhone;
}
ps:折叠屏默认是平板,也就是说,设备里面,是没有平板和折叠屏区分的,折叠屏是在平板的基础上,衍生而来的。
public static void initPadParam(Context context) {
if (padFlag == 1 || padFlag == 2 || context == null) {
return;
}
boolean ret = (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
padFlag = ret ? 1 : 2;
}
// 平板 FEATURE_TABLET oplus.hardware.type.tablet
// 折叠 FEATURE_FOLD oplus.hardware.type.fold
public static boolean isTablet(Context context) {
if (BuildConfig.DEBUG) {
YZJLog.d("im-device", "is pad = " + (context.getPackageManager().hasSystemFeature(FEATURE_TABLET)));
}
return context.getPackageManager().hasSystemFeature(FEATURE_TABLET);
}
public static boolean isFold(Context context) {
if (BuildConfig.DEBUG) {
YZJLog.d("im-device", "is pad = " + (context.getPackageManager().hasSystemFeature(FEATURE_FOLD)));
}
return context.getPackageManager().hasSystemFeature(FEATURE_FOLD);
}
//方式2:smallestDeviceWidth
private static boolean isTableBySmallWidth(Context context) {
Context applicationContext = context.getApplicationContext();
if (applicationContext != null && applicationContext.getResources().getConfiguration().smallestScreenWidthDp >= 600) {
return true;
}
return false;
}
public static class HuaweiPad { // 方式1 // public static boolean isTablet() { // return "tablet".equals(SystemPropertiesEx.get("ro.build.characteristics", "")); // } //方式2 public static boolean isTablet(Context context) { return context != null && (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; } public static Boolean isTablet3() { // return SystemProperties.getBoolean("lockscreen.rot_override", false); // return SystemProperties.get("lockscreen.rot_override", false); Boolean getBoolean = (Boolean) SystemProperties.get("getBoolean", Boolean.class, "lockscreen.rot_override", Boolean.class, false); return getBoolean; } // 折叠屏判断 // IS_FOLDABLE = (!SystemProperties.get("ro.config.hw_fold_disp").isEmpty() || !SystemProperties.get(HwFoldScreenState.DEBUG_HW_FOLD_DISP_PROP).isEmpty()); } public static class SemPad { // 平板判断: //方式1:screenLayout public static boolean isTablet(Context context) { return (context.getResources().getConfiguration().screenLayout & 15) >= 3; } //方式2:smallestDeviceWidth public static boolean isTablet2(Context context) { Context applicationContext = context.getApplicationContext(); if (applicationContext != null && applicationContext.getResources().getConfiguration().smallestScreenWidthDp >= 600) { return true; } return false; } // //方式3:ro.build.characteristics // private static boolean isTablet() { // String str = SemSystemProperties.get("ro.build.characteristics"); // return str != null && str.contains("tablet"); // } }
/**
* 是否是多窗口模式
* @param activity
* @return
*/
public static boolean isInMultiWindow(Activity activity) {
boolean isInMultiWindowMode = false;
if (activity != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
isInMultiWindowMode = activity.isInMultiWindowMode();
}
return isInMultiWindowMode;
}
/**
* 获取屏幕方向
*
* @param context
* @return true: 竖屏 false: 横屏
*/
public static boolean getScreenDirection(Context context) {
DisplayMetrics d = context.getResources().getDisplayMetrics();
if (d.heightPixels > d.widthPixels) {
return true;
}
return false;
}
String config = context.getResources().getConfiguration().toString();
boolean isInMagicWindow = config.contains("oplus-magic-windows");
context为Activity的context
说明:
部分Activity在平行视界下即使显示状态为全屏,其状态仍处于平行视界状态。
方案一:
activity + fragment 方式
方案二:
系统提供的方式
修改AndroidManifest文件
在“AndroidManifest.xml”文件的“application”中新增“meta-data”:
<meta-data android:name="EasyGoClient" android:value="true" />
新增Easygo.json配置文件
在“assets”目录下新建配置文件“easygo.json”,“easygo.json”文件模板和字段详细说明:
{ "easyGoVersion": "1.0", "client": "com.teamtalk.ims", "logicEntities": [ { "head": { "function": "magicwindow", "required": "true" }, "body": { "mode": "0", "defaultDualActivities": { //主页面Activity,可以有多个,分号隔开 展开态时冷启动应用打开此页面时,系统在右屏自动启动relatedPage页面 "mainPages": "com.kdweibo.android.ui.fragment.HomeMainFragmentActivity", "relatedPage": "com.yunzhijia.im.chat.ui.ChatActivity" }, "Activities": [ { "name": "com.kdweibo.android.ui.activity.StartActivity", "defaultFullScreen": "true" }, { "name": "com.kdweibo.android.ui.fragment.HomeMainFragmentActivity", "lockSide": "primary"//Activity锁定方式,当前仅支持锁定在primary侧 primary:锁定在主界面那一侧,锁定后,另一侧启动新的Activity时不会轻易平推窗口过来,除非推过来的窗口也是primary锁定窗口(典型场景:直播购物场景,将直播Activity配置成锁定)。 } ], "UX": { "supportDraggingToFullScreen": "true", "isDraggable": "true",//支持左右大小拖拽 } } } ] }
参数说明:
参数 | 限制 | 描述 |
---|---|---|
easyGoVersion | 1 | 协议版本,固定值为“1.0” |
client | 1 | 应用包名 |
logicEntities. head. function | 1 | 调用组件名,固定值“magicwindow” |
logicEntities. head. required | 1 | 预留字段,固定值“true” |
logicEntities.body.mode | 1 | 基础分屏模式0:购物模式,activityPairs 节点不生效1:自定义模式(包括导航栏模式) |
logicEntities.body. activityPairs | ? | 自定义模式参数,配置从 from 页面到 to 页面的分屏展示 |
logicEntities.body. activityPairs.from | * | 触发分屏的源 Activity |
logicEntities.body. activityPairs.to | * | 触发分屏的目标 Activity,“”表示任意 Activity自定义模式:[{”from” :”com.xxx. ActivityA”, “to” :”com.xxx. ActivityB”}] 表示 A 上启动 B,触发分屏(A 左 B 右)导航栏模式:[{”from” :”com.xxx. ActivityA”, “to” :””}] |
logicEntities.body. defaultDualActivities | ? | 应用冷启动默认打开首页双屏配置 |
logicEntities.body. defaultDualActivities.mainPages | 1 | 主页面 Activity,可以有多个,分号隔开展开态时冷启动应用打开此页面时,系统在右屏自动启动 relatedPage 页面 |
logicEntities.body.defaultDualActivities.relatedPage | ? | 右屏默认展示页面 ActivitymainPages 和 relatedPage 只能配置 1 对,需要具体的 Activity 名,不支持通配符如: [{“mainPages”:“com.xxx.MainActivity”,“relatedPage”:“com.xxx.EmptyActivity”}] |
logicEntities.body. transActivities | * | 过渡页面列表如[ “com.xxx.ActivityD”,“com.xxx.ActivityE”,“com.xxx.ActivityF”] |
logicEntities.body.Activities | ? | 应用关键 Activity 属性列表 |
logicEntities.body.Activities.name | 1 | Activity 组件名 |
logicEntities.body.Activities.defaultFullScreen | ? | Activity 是否支持默认以全屏启动true:支持false:不支持默认为 false |
logicEntities.body.Activities.lockSide | ? | Activity 锁定方式,当前仅支持锁定在 primary 侧primary:锁定在主界面那一侧,锁定后,另一侧启动新的 Activity 时不会轻易平推窗口过来,除非推过来的窗口也是 primary 锁定窗口(典型场景:直播购物场景,将直播 Activity 配置成锁定)。 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。