主頁 > 移動端開發 > Android11.0 平板默認橫屏且兼容重力傳感器方案

Android11.0 平板默認橫屏且兼容重力傳感器方案

2021-06-12 08:46:24 移動端開發

前言

之前搞過的默認橫屏大都都是強制性的,靜止了自動旋轉,那是因為本身也不帶重力傳感器,之前的改法過于暴力不太正統,

這次仔細研究了下 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 螢屏顯示方向分析

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/286799.html

標籤:其他

上一篇:Android 基礎知識總結(四)Activity

下一篇:Android筆記---藍牙開發BT和BLE

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more