Android11.0 平板默认横屏且兼容重力传感器方案

本文涉及的产品
文本翻译,文本翻译 100万字符
语种识别,语种识别 100万字符
图片翻译,图片翻译 100张
简介: Android11.0 平板默认横屏且兼容重力传感器方案

前言


之前搞过的默认横屏大都都是强制性的,静止了自动旋转,那是因为本身也不带重力传感器。之前的改法过于暴力不太正统,


这次仔细研究了下 android 横竖屏控制相关的代码,整理了一套合适的修改方案。


大多情况下竖屏横用是常见的应用场景,这样做应该能节省成本,但给系统软件带来的麻烦不小。


而在安卓版本的迭代中,谷歌和 MTK 已经将屏幕旋转这块做的很完善了, 这里就以 MTK 平台 android11 最新源码分析。


1、Uboot Logo(开机第一屏)方向控制


MTK 的 UbootLogo 绘制在 lk 代码中,算是比较完善的框架了,不管版本怎么迭代,几乎没怎么变动。


代码路径如下


vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6765\mt_logo.c



void init_fb_screen()
{
  dprintf(INFO, "[lk logo: %s %d]\n",__FUNCTION__,__LINE__);
  unsigned int fb_size = mt_get_fb_size();
  logo_addr = mt_get_logo_db_addr();
  phical_screen.width = CFG_DISPLAY_WIDTH;
  phical_screen.height = CFG_DISPLAY_HEIGHT;
  phical_screen.fb_size = fb_size;
  phical_screen.fill_dst_bits = CFG_DISPLAY_BPP;
  phical_screen.bits_per_pixel = CFG_DISPLAY_BPP;
  // in JB2.MP need to allign width and height to 32 ,but jb5.mp needn't
  phical_screen.needAllign = 1;
  phical_screen.allignWidth = ALIGN_TO(CFG_DISPLAY_WIDTH, MTK_FB_ALIGNMENT);
  /* In GB, no need to adjust 180 showing logo ,for fb driver dealing the change */
  /* but in JB, need adjust it for screen 180 roration           */
  phical_screen.need180Adjust = 0;   // need sync with chip driver
  dprintf(INFO, "[lk logo: %s %d]MTK_LCM_PHYSICAL_ROTATION = %s\n",__FUNCTION__,__LINE__, MTK_LCM_PHYSICAL_ROTATION);
  if (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "270", 3)) {
    phical_screen.rotation = 270;
  } else if (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "90", 2)) {
    phical_screen.rotation = 90;
  } else if (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "180", 3) && (phical_screen.need180Adjust == 1)) {
    phical_screen.rotation = 180;
  } else {
    phical_screen.rotation = 0;
  }


看到核心方法 init_fb_screen(),其中根据 MTK_LCM_PHYSICAL_ROTATION 来设置 ubootlogo 旋转角度,说明 MTK 已经做好了兼容


我们只需找到 MTK_LCM_PHYSICAL_ROTATION 配置为想要的方向角度即可。


经过搜索在 project/$(PROJECT).mk 中找到定义


alps\vendor\mediatek\proprietary\bootable\bootloader\lk\project\k62v1_64_bsp.mk


MTK_LCM_PHYSICAL_ROTATION = 270


lk 的编译规则定义在 alps\vendor\mediatek\proprietary\bootable\bootloader\lk\makefile


include project/$(PROJECT).mk
include make/rat_config.mk
include target/$(TARGET)/rules.mk
include dev/rules.mk
include platform/$(PLATFORM)/rules.mk
ifeq ($(MTK_EMMC_SUPPORT), yes)


2、Kernel Logo(开机第二屏)方向控制


KernelLogo 的绘制在 libshowlogo 库中,调用 show_kernel_logo()

alps\vendor\mediatek\proprietary\external\charger\bootlogo.cpp

void bootlogo_show_kernel()
{
    KPOC_LOGI("[ChargingAnimation: %s %d] show  kernel logo \n",__FUNCTION__,__LINE__);
    show_kernel_logo();
}


alps\vendor\mediatek\proprietary\external\libshowlogo\charging_animation.cpp

int anim_fb_init(void)
{
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]\n",__FUNCTION__,__LINE__);
    }
    fb_fd = open(FB_NODE_PATH, O_RDWR);
    if(fb_fd < 0)
    {
        if (MTK_LOG_ENABLE == 1) {
            SLOGE("[libshowlogo: %s %d]open dev file fail, errno = %d \n",__FUNCTION__,__LINE__ , errno);
        }
        close(fb_fd);
        error_flag = 1;
        return -1;
    }
  ......
    phical_screen.bits_per_pixel = vinfo.bits_per_pixel;
    phical_screen.fill_dst_bits = vinfo.bits_per_pixel;
    phical_screen.red_offset = vinfo.red.offset;
    phical_screen.blue_offset = vinfo.blue.offset;
    phical_screen.width = vinfo.xres;
    phical_screen.height = vinfo.yres;
    phical_screen.allignWidth = finfo.line_length/(vinfo.bits_per_pixel/8);
    phical_screen.needAllign = 1;
    phical_screen.need180Adjust = 1;
    phical_screen.fb_size = fb_size;
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]MTK_LCM_PHYSICAL_ROTATION = %s\n",__FUNCTION__,__LINE__, MTK_LCM_PHYSICAL_ROTATION);
    }
    int rotation = getRotation();
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]rotation = %d\n",__FUNCTION__,__LINE__, rotation);
    }
    if(ORIENTATION_270 == rotation){//270
        phical_screen.rotation = 270;
    } else if(ORIENTATION_90 == rotation){//90
        phical_screen.rotation = 90;
    } else if((ORIENTATION_180 == rotation) && (phical_screen.need180Adjust == 1)){//180
        phical_screen.rotation = 180;
    } else {
        phical_screen.rotation = 0;
    }
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo]phical_screen: width= %d,height= %d,bits_per_pixel =%d,needAllign = %d,allignWidth=%d rotation =%d ,need180Adjust = %d\n",
                phical_screen.width, phical_screen.height,
                phical_screen.bits_per_pixel, phical_screen.needAllign,
                phical_screen.allignWidth, phical_screen.rotation, phical_screen.need180Adjust);
        SLOGD("[libshowlogo: %s %d]show old animtion= 1, running show_animationm_ver %d\n",__FUNCTION__,__LINE__, show_animationm_ver);
        SLOGD("[libshowlogo: %s %d]draw_anim_mode = 1, running mode %d\n",__FUNCTION__,__LINE__, draw_anim_mode);
    }
    return 0;
}
void show_kernel_logo()
{
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]show kernel logo, index = 38 \n",__FUNCTION__,__LINE__);
    }
  if (error_flag == 0) {
    anim_show_logo(kernel_logo_position);
    }
}


在调用绘制之前进行了 fb_fd 初始化,核心方法为 anim_fb_init(void),其中根据 int rotation = getRotation();


来设置 kernellogo 旋转角度,找到 getRotation() 实现方法位于


alps\vendor\mediatek\proprietary\external\libshowlogo\utils.cpp

#include "utils.h"
#include <SurfaceFlingerProperties.sysprop.h>
int getRotation() {
    using RotationValues = android::sysprop::SurfaceFlingerProperties::primary_display_orientation_values;
    auto rotation_temp = android::sysprop::SurfaceFlingerProperties::primary_display_orientation();
    int rotation = Rotation::ORIENTATION_0;
    if (rotation_temp.has_value()) {
        switch (*rotation_temp) {
            case RotationValues::ORIENTATION_0:
                rotation = Rotation::ORIENTATION_0;
                break;
            case RotationValues::ORIENTATION_90:
                rotation = Rotation::ORIENTATION_90;
                break;
            case RotationValues::ORIENTATION_180:
                rotation = Rotation::ORIENTATION_180;
                break;
            case RotationValues::ORIENTATION_270:
                rotation = Rotation::ORIENTATION_270;
                break;
            default:
                break;
        }
    }
    return rotation;
}

从中读取 prop 属性 primary_display_orientation_values 对应值,继续寻找在哪里赋值


primary_display_orientation_values定义


位于 alps\device\mediatek\common\device.mk

ifneq ($(strip $(MTK_LCM_PHYSICAL_ROTATION)),)
  ifeq ($(strip $(MTK_LCM_PHYSICAL_ROTATION)), 90)
    PRODUCT_PROPERTY_OVERRIDES += ro.surface_flinger.primary_display_orientation=ORIENTATION_90
  else ifeq ($(strip $(MTK_LCM_PHYSICAL_ROTATION)), 180)
    PRODUCT_PROPERTY_OVERRIDES += ro.surface_flinger.primary_display_orientation=ORIENTATION_180
  else ifeq ($(strip $(MTK_LCM_PHYSICAL_ROTATION)), 270)
    PRODUCT_PROPERTY_OVERRIDES += ro.surface_flinger.primary_display_orientation=ORIENTATION_270
  else
    PRODUCT_PROPERTY_OVERRIDES += ro.surface_flinger.primary_display_orientation=ORIENTATION_0
  endif
endif


不难发现 primary_display_orientation 值由宏定义 MTK_LCM_PHYSICAL_ROTATION 决定


alps\device\mediateksample\k62v1_64_bsp\ProjectConfig.mk

MTK_LCM_PHYSICAL_ROTATION = 270

3、关机充电 Logo 方向控制


关机充电 Logo 绘制代码也在 libshowlogo 中


alps\vendor\mediatek\proprietary\external\libshowlogo\show_animation_common.c

void init_charging_animation_ui_dimension() {
    int lcm_width, lcm_height;
    struct fb_var_screeninfo vinfo;
    display_fd = open("/dev/graphics/fb0", O_RDONLY);
    if (display_fd < 0) {
      SLOGD("[show_animation_common: %s %d]open mtkfb fail...\n",__FUNCTION__,__LINE__);
    }
    if (ioctl(display_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
      close(display_fd);
      SLOGD("[show_animation_common: %s %d]ioctl FBIOGET_VSCREENINFO failed\n",__FUNCTION__,__LINE__);
    }
    close(display_fd);
    lcm_width = vinfo.xres;
    lcm_height = vinfo.yres;
    int rotation = getRotation();
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]rotation = %d\n",__FUNCTION__,__LINE__, rotation);
    }
    if ((ORIENTATION_270 == rotation)|| (ORIENTATION_90 == rotation)){
        lcm_width = vinfo.yres;
        lcm_height = vinfo.xres;
    }
    SLOGD("[show_animation_common: %s %d] lcm_width and lcm_height= %d , %d \n",__FUNCTION__,__LINE__,lcm_width,lcm_height);
  .......


也是根据 int rotation = getRotation(); 来决定旋转角度,和上面的 kernel logo 一样由 MTK_LCM_PHYSICAL_ROTATION 决定。


4、开机动画方向控制


开机动画播放代码位置如下

alps\frameworks\base\cmds\bootanimation\BootAnimation.cpp


status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();
    mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
    if (mDisplayToken == nullptr)
        return NAME_NOT_FOUND;
    DisplayConfig displayConfig;
    const status_t error =
            SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
    if (error != NO_ERROR)
        return error;
    mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
    mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
    ui::Size resolution = displayConfig.resolution;
    resolution = limitSurfaceSize(resolution.width, resolution.height);
    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);
    SurfaceComposerClient::Transaction t;
+    Rect destRect(resolution.getWidth(), resolution.getHeight());
+    t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, destRect, destRect);
    // this guest property specifies multi-display IDs to show the boot animation
    // multiple ids can be set with comma (,) as separator, for example:
    // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
    Vector<uint64_t> physicalDisplayIds;
    char displayValue[PROPERTY_VALUE_MAX] = "";
    property_get(DISPLAYS_PROP_NAME, displayValue, "");
  ......


播放绘制核心方法为 BootAnimation::readyToRun(),其中可通过 Transaction t 的 displayProjection


来决定方向。 ui::ROTATION_0 ui::ROTATION_90 ui::ROTATION_180 ui::ROTATION_270


这里添加的是 ui::ROTATION_0 为了解决播放动画后半段有一半显示不完整问题。


5、RecoveryUI 方向控制


RecoveryUI 绘制代码位于,alps\bootable\recovery\minui\graphics.cpp

谷歌已经为我们提供了兼容旋转显示

int gr_init() {
  ......
  std::string rotation_str =
      android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
  if (rotation_str == "ROTATION_RIGHT") {
    gr_rotate(GRRotation::RIGHT);
  } else if (rotation_str == "ROTATION_DOWN") {
    gr_rotate(GRRotation::DOWN);
  } else if (rotation_str == "ROTATION_LEFT") {
    gr_rotate(GRRotation::LEFT);
  } else {  // "ROTATION_NONE" or unknown string
    gr_rotate(GRRotation::NONE);
  }

核心方法 gr_init() 中读取 prop ro.minui.default_rotation 值决定绘制方向


搜索后发现并没有定义赋值的地方,那我们就自己加一个赋值为横屏 ROTATION_LEFT


alps\device\mediateksample\k62v1_64_bsp\device.mk

PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.minui.default_rotation=ROTATION_LEFT


6、系统 TP 触控 x y 方向控制


核心控制逻辑如下

alps\kernel-4.19\drivers\input\touchscreen\mtk_tpd.c


  #ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
  if (strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) == 0
    || strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3) == 0) {
#ifdef CONFIG_MTK_FB
/*Fix build errors,as some projects  cannot support these apis while bring up*/
    TPD_RES_Y = DISP_GetScreenWidth();
    TPD_RES_X = DISP_GetScreenHeight();
#endif
  } else
    #endif
  {

alps\kernel-4.19\drivers\input\touchscreen\GT911\gt9xx_driver.c

static void tpd_down(s32 x, s32 y, s32 size, s32 id)
{
  if ((!size) && (!id)) {
    input_report_abs(tpd->dev, ABS_MT_PRESSURE, 100);
    input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 100);
  } else {
    input_report_abs(tpd->dev, ABS_MT_PRESSURE, size);
    input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, size);
    /* track id Start 0 */
    input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, id);
  }
  input_report_key(tpd->dev, BTN_TOUCH, 1);
#if 0 
  input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
  input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
#else
  input_report_abs(tpd->dev, ABS_MT_POSITION_X, 1280-y);
  input_report_abs(tpd->dev, ABS_MT_POSITION_Y, x);
#endif


得到由宏定义 CONFIG_MTK_LCM_PHYSICAL_ROTATION 决定 x y 坐标


alps\kernel-4.19\arch\arm64\configs\k62v1_64_bsp_defconfig

CONFIG_MTK_LCM_PHYSICAL_ROTATION="270"


7、开机默认横屏显示


修改完上面的步骤后开机已经是横屏了,具体和 MTK_LCM_PHYSICAL_ROTATION

所决定 ro.surface_flinger.primary_display_orientation 值有关系

核心代码如下

alps\hardware\interfaces\configstore\1.1\default\surfaceflinger.mk


ifneq ($(SF_PRIMARY_DISPLAY_ORIENTATION),)
    LOCAL_CFLAGS += -DPRIMARY_DISPLAY_ORIENTATION=$(SF_PRIMARY_DISPLAY_ORIENTATION)
endif



alps\hardware\interfaces\configstore\1.1\default\SurfaceFlingerConfigs.cpp

Return<void> SurfaceFlingerConfigs::primaryDisplayOrientation(
        primaryDisplayOrientation_cb _hidl_cb) {
    using ::android::hardware::configstore::V1_1::DisplayOrientation;
    bool specified = false;
    DisplayOrientation value = DisplayOrientation::ORIENTATION_0;
    int orientation = 0;
#ifdef PRIMARY_DISPLAY_ORIENTATION
    specified = true;
    orientation = PRIMARY_DISPLAY_ORIENTATION;
#endif
    switch (orientation) {
        case 0: {
            value = DisplayOrientation::ORIENTATION_0;
            break;
        }
        case 90: {
            value = DisplayOrientation::ORIENTATION_90;
            break;
        }
        case 180: {
            value = DisplayOrientation::ORIENTATION_180;
            break;
        }
        case 270: {
            value = DisplayOrientation::ORIENTATION_270;
            break;
        }
        default: {
            // statically checked above -> memory corruption
            LOG_ALWAYS_FATAL("Invalid orientation %d", orientation);
        }
    }
    _hidl_cb({specified, value});
    return Void();
}

alps\frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp

SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
    ALOGI("SurfaceFlinger is starting");
    hasSyncFramework = running_without_sync_framework(true);
  ......
    wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
    defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
            default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
    wideColorGamutCompositionPixelFormat =
            static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
    mColorSpaceAgnosticDataspace =
            static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
    useContextPriority = use_context_priority(true);
    using Values = SurfaceFlingerProperties::primary_display_orientation_values;
    switch (primary_display_orientation(Values::ORIENTATION_0)) {
        case Values::ORIENTATION_0:
            break;
        case Values::ORIENTATION_90:
            internalDisplayOrientation = ui::ROTATION_90;
            break;
        case Values::ORIENTATION_180:
            internalDisplayOrientation = ui::ROTATION_180;
            break;
        case Values::ORIENTATION_270:
            internalDisplayOrientation = ui::ROTATION_270;
            break;
    }
    ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation));
    mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
    // debugging stuff...
    char value[PROPERTY_VALUE_MAX];
    property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
    mGpuToCpuSupported = !atoi(value);

alps\frameworks\native\services\surfaceflinger\SurfaceFlingerProperties.cpp

SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
        SurfaceFlingerProperties::primary_display_orientation_values defaultValue) {
    auto temp = SurfaceFlingerProperties::primary_display_orientation();
    if (temp.has_value()) {
        return *temp;
    }
    auto configDefault = DisplayOrientation::ORIENTATION_0;
    switch (defaultValue) {
        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
            configDefault = DisplayOrientation::ORIENTATION_90;
            break;
        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
            configDefault = DisplayOrientation::ORIENTATION_180;
            break;
        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
            configDefault = DisplayOrientation::ORIENTATION_270;
            break;
        default:
            configDefault = DisplayOrientation::ORIENTATION_0;
            break;
    }
    DisplayOrientation result =
            getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
                                  &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
                    configDefault);
    switch (result) {
        case DisplayOrientation::ORIENTATION_90:
            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90;
        case DisplayOrientation::ORIENTATION_180:
            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180;
        case DisplayOrientation::ORIENTATION_270:
            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270;
        default:
            break;
    }
    return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
}

8、重力传感器驱动调试


参考这篇

MTK 9.0平台调试 gsensor


9、默认横屏兼容重力传感器自动旋转


打开系统自动旋转功能按钮,屏幕可跟随当前方向旋转显示


关闭系统自动旋转功能按钮,强制所有 APP 默认横屏显示,不管 portrait 属性


alps\frameworks\base\services\core\java\com\android\server\wm\DisplayRotation.java

 @Override
       boolean updateRotationUnchecked(boolean forceUpdate) {
        //add
    int flag = android.provider.Settings.System.getInt(mContext.getContentResolver(),
         "accelerometer_rotation", 0);
        if (flag == 0) {
            return true;
        }//end
        final int displayId = mDisplayContent.getDisplayId();
        if (!forceUpdate) {
            if (mDeferredRotationPauseCount > 0) {
                // Rotation updates have been paused temporarily. Defer the update until updates
                // have been resumed.
                ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
                return false;
            }

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


    @ScreenOrientation
    @Override
    int getOrientation() {
     //add
    int flag = android.provider.Settings.System.getInt(mContext.getContentResolver(),
         "accelerometer_rotation", 0);
        if (flag == 0) {
           return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        }//end
        mLastOrientationSource = null;
        if (mIgnoreRotationForApps) {
            return SCREEN_ORIENTATION_USER;
        }
        if (mWmService.mDisplayFrozen) {
            if (mWmService.mPolicy.isKeyguardLocked()) {
                // Use the last orientation the while the display is frozen with the keyguard
                // locked. This could be the keyguard forced orientation or from a SHOW_WHEN_LOCKED
                // window. We don't want to check the show when locked window directly though as
                // things aren't stable while the display is frozen, for example the window could be
                // momentarily unavailable due to activity relaunch.
                ProtoLog.v(WM_DEBUG_ORIENTATION,
                        "Display id=%d is frozen while keyguard locked, return %d",
                        mDisplayId, getLastOrientation());
                return getLastOrientation();
            }
        }
        final int rootOrientation = mRootDisplayArea.getOrientation();
        mLastOrientationSource = mRootDisplayArea.getLastOrientationSource();
        return rootOrientation;
    }


10、参考资料


Android 屏幕显示方向分析

目录
相关文章
|
2月前
|
Ubuntu 网络协议 Java
【Android平板编程】远程Ubuntu服务器code-server编程写代码
【Android平板编程】远程Ubuntu服务器code-server编程写代码
|
2月前
|
Ubuntu Android开发 数据安全/隐私保护
【Android平板编程】远程Ubuntu服务器Code-Server编程写代码
【Android平板编程】远程Ubuntu服务器Code-Server编程写代码
|
2月前
|
Android开发
Android 新建一个lunch项(全志方案)
Android 新建一个lunch项(全志方案)
41 0
|
2月前
|
传感器 Android开发
Android MediaTek 添加LPS22HB压力传感器驱动
Android MediaTek 添加LPS22HB压力传感器驱动
29 0
|
2月前
|
Ubuntu 网络协议 Java
在Android平板上使用code-server公网远程Ubuntu服务器编程
在Android平板上使用code-server公网远程Ubuntu服务器编程
|
2月前
|
Ubuntu 网络协议 Linux
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
95 0
|
2月前
|
传感器 物联网 Android开发
【Android App】物联网中查看手机支持的传感器及实现摇一摇功能-加速度传感器(附源码和演示 超详细)
【Android App】物联网中查看手机支持的传感器及实现摇一摇功能-加速度传感器(附源码和演示 超详细)
107 1
|
10月前
|
存储 缓存 前端开发
Android Github 上面优秀的两种阴影方案,完美兼容高低版本问题
Android Github 上面优秀的两种阴影方案,完美兼容高低版本问题
|
11月前
|
传感器 Android开发
Android 加速度传感器的使用详解
Android 加速度传感器的使用详解
242 0
|
11月前
|
传感器 Android开发
Android 中磁场传感器的使用详解
Android 中磁场传感器的使用详解
154 0