国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

這篇具有很好參考價(jià)值的文章主要介紹了Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1.Launcher簡介

Launcher是安卓系統(tǒng)中的桌面啟動(dòng)器,安卓系統(tǒng)的桌面UI統(tǒng)稱為Launcher。Launcher是安卓系統(tǒng)中的主要程序組件之一,安卓系統(tǒng)中如果沒有Launcher就無法啟動(dòng)安卓桌面。作為車機(jī)開機(jī)后用戶接觸到的第一個(gè)帶有界面的系統(tǒng)級APP,和普通APP一樣,它的界面也是在Activity上繪制出來的。

車機(jī)上Launcher一般分為兩個(gè)界面,首頁和應(yīng)用列表界面。

首頁一般包括用戶信息、常用應(yīng)用快捷方式、3D車模和widget卡片,widget卡片有:地圖、天氣、音樂播放器、時(shí)鐘等;

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

應(yīng)用列表界面就是啟動(dòng)APP的列表界面,單擊APP的Icon可進(jìn)入App,長按APP的Icon可以進(jìn)入編輯模式,編輯模式下APP可以進(jìn)行拖拽、合并文件夾、刪除等功能。

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

(ps:頂部狀態(tài)欄status bar和底部導(dǎo)航欄navigation bar屬于System UI,中間才屬于Launcher部分)

2.Widget概述

參考資料:應(yīng)用微件概覽

Widget,又稱為微件或者小部件。我們可以把它當(dāng)作是一個(gè)微型應(yīng)用程序視圖,用以嵌入到其他應(yīng)用程序中(一般來說就是桌面Launcher)并接收周期性的更新。這樣用戶就可以方便查看應(yīng)用程序的重點(diǎn)信息或者進(jìn)行應(yīng)用程序的快捷控制。

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

Widget類型官方分為信息微件、集合微件、控制微件和混合微件。開發(fā)Widget是由各自應(yīng)用程序(如天氣、導(dǎo)航、音樂)開發(fā)人員開發(fā),不是本篇的重點(diǎn)內(nèi)容,網(wǎng)上有很多關(guān)于Widget開發(fā)的例子。如何使車載Launcher具有擺放Widget的能力,是我們關(guān)注的重點(diǎn)!

3.Launcher開發(fā)如何顯示W(wǎng)idget

3.1 使Launcher App成為系統(tǒng)級App

  • Q:為什么在顯示W(wǎng)idget的時(shí)候要把Launcher App聲明為系統(tǒng)級的App呢?

  • A:開發(fā)Launcher App時(shí)肯定會聲明其為系統(tǒng)級App。而顯示W(wǎng)idget時(shí)需要App是系統(tǒng)級的原因是:Widget顯示需要我們獲取到AppWidgetManager對象并調(diào)用**public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider)**方法,而此方法返回值要為true就需要App是系統(tǒng)級App。

private AppWidgetProviderInfo createAppWidgetInfo(ComponentName component) {
    //分配新的widgetId
    int widgetId = LauncherApplication.getContext().getWidgetHost().allocateAppWidgetId();
    //將widgetId和ComponentName綁定
    boolean isBindAppWidgetIdIfAllowed = LauncherApplication.getContext()
            .getWidgetManager().bindAppWidgetIdIfAllowed(widgetId, component);
    LogUtil.info(TAG, "createAppWidgetInfo bindAppWidgetIdIfAllowed = "
            + isBindAppWidgetIdIfAllowed);
    //獲取AppWidgetProviderInfo
    AppWidgetProviderInfo appWidgetInfo = LauncherApplication.getContext()
            .getWidgetManager().getAppWidgetInfo(widgetId);
    //存儲widgetId、包名、類名到數(shù)據(jù)庫
    WidgetInfoEntity entity = new WidgetInfoEntity(widgetId, component.getPackageName(),
            component.getClassName(), checkWidgetDisplay(component.getPackageName()));
    saveWidgetInfo(entity);
    return appWidgetInfo;
}

Launcher未聲明為系統(tǒng)級App時(shí)截取的Log:

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

將App聲明為系統(tǒng)級App的步驟:

  1. 將車機(jī)系統(tǒng)簽名放到項(xiàng)目中,創(chuàng)建一個(gè)keystore目錄放置簽名文件:

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

  1. app目錄下的build.gradle文件配置簽名文件,在android{}內(nèi)加上簽名文件的配置信息,然后sync一下:
android {
    ...
    signingConfigs {
        config {
            storeFile file('../keystore/platform.jks')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 
'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
        debug {
            signingConfig signingConfigs.config
        }
    }
    ...
}

3.在AndroidManifest.xml文件添加android:sharedUserId=”android.uid.system”,讓程序運(yùn)行在系統(tǒng)進(jìn)程中。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yx.yxlauncher"
    android:sharedUserId="android.uid.system">
    //定義查詢權(quán)限,查詢系統(tǒng)中的所有widget廣播需要
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />
    ...
</manifest>

通過以上步驟,相當(dāng)于把我們自己的開發(fā)的Launcher聲明成系統(tǒng)級App了。

3.2 定義并初始化AppWidgetHost對象

定義類繼承Application,在Application初始化的時(shí)候定義好AppWidgetHost對象并且調(diào)用**startListening()**方法

public class YxApplication extends Application {
    private static final String TAG = "Yx_YxApplication";

    private AppWidgetHost mWidgetHost;
    //自定義一個(gè)APPWIDGET_HOST_ID
    private static final int APPWIDGET_HOST_ID = 0x300;
    private static YxApplication sApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
        sApplication = this;
        initWidgetHost();
    }

    private void initWidgetHost() {
        //初始化WidgetHost并且開始接收onAppWidgetChanged()的回調(diào)
        mWidgetHost = new AppWidgetHost(YxApplication.getContext(), APPWIDGET_HOST_ID);
        mWidgetHost.startListening();

        //初始化數(shù)據(jù)庫里存儲的widget信息列表,后面會介紹數(shù)據(jù)庫存儲的內(nèi)容
        WidgetInfoManager.getInstance().initializeWidget();
        //初始化Widget廣播的ResolveInfo列表
        WidgetInfoManager.getInstance().initializeWidgetResolveInfo();
    }

    public static YxApplication getContext() {
        return sApplication;
    }

    public static Context getDirectBootContext() {
        return getContext().getBaseContext().createDeviceProtectedStorageContext();
    }

    public AppWidgetManager getWidgetManager() {
        return AppWidgetManager.getInstance(YxApplication.getContext());
    }

    public AppWidgetHost getWidgetHost() {
        return mWidgetHost;
    }

3.3 數(shù)據(jù)庫存儲widgetId

有了我們的AppWidgetHost,我們就可以調(diào)用**allocateAppWidgetId()**方法獲取widgetId,并且將其存入數(shù)據(jù)庫,定義實(shí)體類WidgetInfoEntity,我用的是room數(shù)據(jù)庫,存儲了widgetId、包名、類名:

@Entity(tableName = "widget_info")
public class WidgetInfoEntity {
    @PrimaryKey
    @ColumnInfo(name = "widgetId")
    private int widgetId;

    @ColumnInfo(name = "packageName")
    private String packageName;

    @ColumnInfo(name = "className")
    private String className;

    /**
     * Construction method.
     */
    public WidgetInfoEntity(int widgetId, String packageName, String className) {
        this.widgetId = widgetId;
        this.packageName = packageName;
        this.className = className;
    }

    public int getWidgetId() {
        return widgetId;
    }

    public String getPackageName() {
        return packageName;
    }

    public String getClassName() {
        return className;
    }

    @Override
    public String toString() {
        return "WidgetInfoEntity{" +
                "widgetId=" + widgetId +
                ", packageName='" + packageName + '\'' +
                ", className='" + className + '\'' +
                '}';
    }
}

dao層定義,將訪問數(shù)據(jù)庫里的widget信息的代碼封裝起來:

@Dao
public interface WidgetInfoDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertWidgetInfo(WidgetInfoEntity... infoEntity);

    @Query("SELECT * FROM " + "widget_info" + " ORDER BY " + "widgetId" + " ASC")
    List<WidgetInfoEntity> queryAllWidgetInfos();

    @Delete
    void deleteWidgetInfo(WidgetInfoEntity entity);
}

db定義,數(shù)據(jù)庫工具類,包含創(chuàng)建數(shù)據(jù)庫、打開數(shù)據(jù)庫、數(shù)據(jù)庫操作的對外方法等:

@Database(entities = {WidgetInfoEntity.class}, version = 1, exportSchema = false)
public abstract class DatabaseUtil extends RoomDatabase {
    private static final String TAG = "Yx_DatabaseUtil";

    private static DatabaseUtil sInstance;

    private final ExecutorService mExecutor;

    private final WidgetInfoDao mWidgetInfoDao;

    public DatabaseUtil() {
        mExecutor = Executors.newSingleThreadExecutor();
        mWidgetInfoDao = widgetInfoDao();
    }

    /**
     * get DatabaseUtil Singleton.
     *
     * @return DatabaseUtil
     */
    public static DatabaseUtil getInstance() {
        if (sInstance == null) {
            synchronized (DatabaseUtil.class) {
                create();
            }
        }
        return sInstance;
    }

    private static void create() {
        Log.i(TAG, "create: ");
        sInstance = Room.databaseBuilder(YxApplication.getDirectBootContext(),
                        DatabaseUtil.class, "yx_launcher_db")
                       .addCallback(new RoomDatabase.Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        Log.d(TAG, "onCreate database: " + db.getPath());
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {
                        super.onOpen(db);
                        Log.d(TAG, "onOpen database: " + db.getPath());
                    }
                }).allowMainThreadQueries()
                .fallbackToDestructiveMigration()
                .build();
    }

    /**
     * Create instance of WidgetInfoDao.
     *
     * @return WidgetInfoDao.
     */
    public abstract WidgetInfoDao widgetInfoDao();
    /**
     * Query all widgetInfo.
     *
     * @return widgetInfos
     */
    public List<WidgetInfoEntity> queryAllWidgetInfos() {
        Log.d(TAG, "queryAllWidgetInfos: ");
        return mWidgetInfoDao.queryAllWidgetInfos();
    }

    /**
     * insert WidgetInfoEntity.
     *
     * @param infoEntity WidgetInfoEntity
     */
    public void insertWidgetInfos(WidgetInfoEntity infoEntity) {
        Log.d(TAG, "insertWidgetInfos: infoEntity = " + infoEntity.toString());
        mExecutor.execute(() -> mWidgetInfoDao.insertWidgetInfo(infoEntity));
    }

    /**
     * Delete WidgetInfo.
     *
     * @param entity WidgetInfoEntity
     */
    public void deleteWidgetInfo(WidgetInfoEntity entity) {
        Log.d(TAG, "deleteWidgetInfo: entity = " + entity);
        mExecutor.execute(() -> mWidgetInfoDao.deleteWidgetInfo(entity));
    }
}

3.4 定義WidgetInfoManager類處理widget

包含的內(nèi)容:

  1. 查詢系統(tǒng)里所有Widget廣播的ResolveInfo列表用于獲取其ComponentName
  2. 根據(jù)存儲的widgetId獲取或者新創(chuàng)建AppWidgetProviderInfo
  3. 保存新創(chuàng)建的widgetId到數(shù)據(jù)庫,刪除數(shù)據(jù)庫里數(shù)據(jù)或者去重

其實(shí),這個(gè)類最主要的目的就是拿到AppWidgetProviderInfo對象,有了這個(gè)對象才能獲取AppWidgetHostView用于顯示:

public class WidgetInfoManager {

    private static final String TAG = "Yx_WidgetInfoManager";
    private static final long RELOAD_DELAY = 100;

    private final List<WidgetInfoEntity> mWidgetInfoList = new ArrayList<>();
    private final List<ResolveInfo> mAllWidgetResolveInfo = new ArrayList<>();
    private final Handler mHandler = new Handler(YxApplication.getContext().getMainLooper());

    private final Runnable mReloadWidgetResolveInfoRunnable
            = this::initializeWidgetResolveInfo;

    private static class SingletonHolder {
        // Static initializer, thread safety is guaranteed by JVM
        private static WidgetInfoManager instance = new WidgetInfoManager();
    }

    /**
     * Privatization construction method.
     */
    private WidgetInfoManager() {
    }

    /**
     * getInstance.
     *
     * @return WidgetInfoManager
     */
    public static WidgetInfoManager getInstance() {
        return SingletonHolder.instance;
    }

    /**
     * initializeWidgetResolveInfo.
     */
    @SuppressLint("QueryPermissionsNeeded")
    public void initializeWidgetResolveInfo() {
        mAllWidgetResolveInfo.clear();
        mAllWidgetResolveInfo.addAll(YxApplication.getContext().getPackageManager()
                .queryBroadcastReceivers(new Intent(
                      "android.intent.action.WidgetProvider"), 0));
        if (mAllWidgetResolveInfo.size() == 0) {
            mHandler.postDelayed(mReloadWidgetResolveInfoRunnable, RELOAD_DELAY);
            Log.i(TAG, "mAllWidgetResolveInfo is null, reload after 100ms");
        } else {
            mHandler.removeCallbacks(mReloadWidgetResolveInfoRunnable);
            Log.i(TAG, "initializeWidgetResolveInfo: mAllWidgetResolveInfo = "
                    + Arrays.toString(mAllWidgetResolveInfo.toArray()));
        }
    }

    public void initializeWidget() {
        mWidgetInfoList.addAll(DatabaseUtil.getInstance().queryAllWidgetInfos());
        Log.i(TAG, "WidgetInfoManager: size = " + mWidgetInfoList.size());
    }

    /**
     * Get AppWidgetProviderInfo by package name.
     * @param pkg package name
     * @return AppWidgetProviderInfo
     */
    public AppWidgetProviderInfo getAppWidgetProviderInfo(String pkg) {
        Log.i(TAG, "getAppWidgetProviderInfo: pkg = " + pkg);
        int widgetId = -1;
        AppWidgetProviderInfo appWidgetInfo;

        // 1. 根據(jù)包名獲取 ComponentName
        ComponentName component = getComponent(pkg);
        if (component == null) {
            Log.w(TAG, "getAppWidgetProviderInfo: component is null !!!");
            return null;
        }

        // 2. 根據(jù) ComponentName 獲取已保存的 WidgetId
        for (WidgetInfoEntity entity : mWidgetInfoList) {
            if (component.getPackageName().equals(entity.getPackageName())
                    && component.getClassName().equals(entity.getClassName())) {
                widgetId = entity.getWidgetId();
                break;
            }
        }

        // 3. 判斷獲取的widgetId是否有效,如果有效就使用widgetId去拿AppWidgetProviderInfo; 
        //如果無效就執(zhí)行4
        if (widgetId != -1) {
            appWidgetInfo = YxApplication.getContext()
                    .getWidgetManager().getAppWidgetInfo(widgetId);
            // 3.1 如果獲取的AppWidgetProviderInfo為null,則執(zhí)行4
            if (appWidgetInfo == null) {
                Log.w(TAG, "getAppWidgetProviderInfo: appWidgetInfo is null !!! widgetId = "
                        + widgetId);
                // 移除無效值
                removeWidgetByPkg(component.getPackageName());
                // 創(chuàng)建新的AppWidgetProviderInfo
                appWidgetInfo = createAppWidgetInfo(component);
            }
        } else {
            Log.w(TAG, "getAppWidgetProviderInfo: widgetId is -1 !!!");
            // 4. 重新創(chuàng)建widgetId -> 綁定widget -> 生成新的 AppWidgetProviderInfo
            // 移除無效值
            removeWidgetByPkg(component.getPackageName());
            // 創(chuàng)建新的 AppWidgetProviderInfo
            appWidgetInfo = createAppWidgetInfo(component);
        }
        Log.i(TAG, "getAppWidgetProviderInfo: appWidgetInfo = " + appWidgetInfo);
        return appWidgetInfo;
    }

    private AppWidgetProviderInfo createAppWidgetInfo(ComponentName component) {
        Log.i(TAG, "createAppWidgetInfo: component = " + component.toString());
        int widgetId = YxApplication.getContext().getWidgetHost().allocateAppWidgetId();
        boolean isBindAppWidgetIdIfAllowed = YxApplication.getContext()
                .getWidgetManager().bindAppWidgetIdIfAllowed(widgetId, component);
        Log.i(TAG, "createAppWidgetInfo bindAppWidgetIdIfAllowed = "
                + isBindAppWidgetIdIfAllowed);
        AppWidgetProviderInfo appWidgetInfo = YxApplication.getContext()
                .getWidgetManager().getAppWidgetInfo(widgetId);
        WidgetInfoEntity entity = new WidgetInfoEntity(widgetId, component.getPackageName(),
                component.getClassName());
        saveWidgetInfo(entity);
        return appWidgetInfo;
    }

    private ComponentName getComponent(String pkg) {
        for (ResolveInfo info : mAllWidgetResolveInfo) {
            if (info.activityInfo.packageName.equals(pkg)) {
                return new ComponentName(
                               info.activityInfo.packageName, info.activityInfo.name);
            }
        }
        Log.w(TAG, pkg + " ComponentName is null ! "
                + " mAllWidgetResolveInfo.size = " + mAllWidgetResolveInfo.size());
        return null;
    }

    /**
     * Get widget id by pkg name.
     * @param pkg package name
     * @return widgetId
     */
    public int getWidgetId(String pkg) {
        for (WidgetInfoEntity entity : mWidgetInfoList) {
            if (entity.getPackageName().equals(pkg)) {
                return entity.getWidgetId();
            }
        }
        return -1;
    }

    /**
     * saveWidgetInfo.
     *
     * @param entity WidgetInfoEntity
     */
    private void saveWidgetInfo(WidgetInfoEntity entity) {
        Log.d(TAG, "saveWidgetInfo: entity = " + entity.toString());
        // 去重,移除臟數(shù)據(jù)(入?yún)⒌膚idgetId是新生成的,可信的),保證 widgetId 的唯一性
        removeDuplicateWidget(entity.getWidgetId());

        mWidgetInfoList.add(entity);
        DatabaseUtil.getInstance().insertWidgetInfos(entity);
    }

    private void removeDuplicateWidget(int widgetId) {
        Iterator<WidgetInfoEntity> iterator = mWidgetInfoList.iterator();
        while (iterator.hasNext()) {
            WidgetInfoEntity entity = iterator.next();
            if (widgetId == entity.getWidgetId()) {
                iterator.remove();
                DatabaseUtil.getInstance().deleteWidgetInfo(entity);
            }
        }
    }

    /**
     * Remove widget by package name.
     *
     * @param pkg package name
     */
    public void removeWidgetByPkg(String pkg) {
        Iterator<WidgetInfoEntity> iterator = mWidgetInfoList.iterator();
        while (iterator.hasNext()) {
            WidgetInfoEntity entity = iterator.next();
            if (entity.getPackageName().equals(pkg)) {
                iterator.remove();
                DatabaseUtil.getInstance().deleteWidgetInfo(entity);
                YxApplication.getContext().getWidgetHost()
                        .deleteAppWidgetId(entity.getWidgetId());
                break;
            }
        }
    }
}

3.5 獲取AppWidgetHostView并顯示

我車機(jī)里有另外一個(gè)app提供了widget-provider,包名為"com.yx.mywidget",最終可以看到widget顯示在Launcher App中:

...
private void initView() {
        mWidgetFrameLayout = findViewById(R.id.widget_test_fl);
        mWidgetFrameLayout.addView(getWidgetView("com.yx.mywidget"));
    }

    /**
     * Get widget view.
     * @param pkg package name
     * @return widget view
     */
    private View getWidgetView(String pkg) {
        Log.d(TAG, "getWidgetView: pkg: " + pkg);
        AppWidgetProviderInfo appWidgetInfo = WidgetInfoManager.getInstance()
                .getAppWidgetProviderInfo(pkg);
        int widgetId = WidgetInfoManager.getInstance().getWidgetId(pkg);
        Log.i(TAG, "getWidgetView: appWidgetInfo = " + appWidgetInfo
                + " widgetId = " + widgetId);
        if (appWidgetInfo != null && widgetId != -1) {
            AppWidgetHostView hostView = YxApplication.getContext().getWidgetHost()
                    .createView(YxApplication.getContext(), widgetId, appWidgetInfo);
            // Remove HostView's default padding value
            Log.i(TAG, "getWidgetView: pkg = " + pkg + " hostView = " + hostView);
            return hostView;
        }
        return null;
    }
...

Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget

4.總結(jié)

可以看到,想要widget顯示到Launcher上其實(shí)并不復(fù)雜,主要流程就是:

  1. 定義widgetHost并startListening
  2. 獲取系統(tǒng)里所有widget-provider廣播,拿到其ComponentName
  3. 獲取AppWidgetProviderInfo,如果首次沒有widgetId就創(chuàng)建并存儲
  4. 通過widgetId和AppWidgetProviderInfo獲取AppWidgetHostView并顯示

本文是我首次進(jìn)行技術(shù)性文檔的總結(jié)并發(fā)布到網(wǎng)上,感謝你的閱讀。文章來源地址http://www.zghlxwxcb.cn/news/detail-500476.html

到了這里,關(guān)于Android車載Launcher開發(fā)(1) - 顯示W(wǎng)idget的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android 車載應(yīng)用開發(fā)之車載操作系統(tǒng)

    到 2030 年,全球電動(dòng)汽車的銷量將超過 7000 萬輛,保有量將達(dá)到 3.8 億輛,全球年度新車滲透率有望觸及 60% 。這一數(shù)據(jù)來自國際能源署(IEA)發(fā)布的《全球電動(dòng)汽車展望2023》。 市場趨勢和政策努力的雙加持下,新能源汽車來勢兇猛,燃油車保有量逐年遞減。此番景象讓死去

    2024年02月22日
    瀏覽(27)
  • Android 13.0 Launcher3 電話和短信app圖標(biāo)顯示未讀短信和未接來電的條數(shù)

    Android 13.0 Launcher3 電話和短信app圖標(biāo)顯示未讀短信和未接來電的條數(shù)

    在13.0系統(tǒng)產(chǎn)品rom定制化開發(fā)中,最近客戶有需求要求在電話app圖標(biāo)顯示未接來電的條數(shù) 在短信app圖標(biāo)上顯示未讀信息的條數(shù) 根據(jù)需求首選要在Launcher3的Launcher.java中,啟動(dòng)launcher時(shí),查詢未讀短信和未接來電 在有未接來電時(shí),更新未接來電的數(shù)量 在有未讀短信時(shí),更新未讀短

    2024年01月17日
    瀏覽(33)
  • 車載Launcher中,Service下Dialog彈框,并且覆蓋狀態(tài)欄且狀態(tài)欄不能點(diǎn)擊

    車載Launcher中,Service下Dialog彈框,并且覆蓋狀態(tài)欄且狀態(tài)欄不能點(diǎn)擊

    在Service中使用系統(tǒng)dialog彈框,但是無法覆蓋全部,底部菜單依然可以被點(diǎn)擊,在某些場景下是不符合需求的 ? 顯然是 dialog 的層級不夠高導(dǎo)致的,很多時(shí)候會直接修改層級,但是如果修改的層級涉及到系統(tǒng)權(quán)限,運(yùn)行就會直接報(bào)錯(cuò) getWindow().setType(WindowManager.LayoutParams.TYPE_SYS

    2024年02月09日
    瀏覽(15)
  • android車載開發(fā),如何模擬器上實(shí)現(xiàn)多屏

    android車載開發(fā),如何模擬器上實(shí)現(xiàn)多屏

    三個(gè)點(diǎn),Display-addSecondary display ? 方案一 通過Presentation來實(shí)現(xiàn),他是一個(gè)Dialog(context,display) 方案二 使用ActivityOptions設(shè)置activity顯示在哪個(gè)屏幕上 launchDisplayId 方案三 adb shell am start -n 包名+Activity

    2024年02月12日
    瀏覽(26)
  • Android 車載應(yīng)用開發(fā)指南(3) - SystemUI 詳解

    Android 車載應(yīng)用開發(fā)指南(3) - SystemUI 詳解

    Android 車載應(yīng)用開發(fā)指南系列文章 Android 車載應(yīng)用開發(fā)指南(1)- 車載操作系統(tǒng)全解析 Android 車載應(yīng)用開發(fā)指南(2)- 應(yīng)用開發(fā)入門 Android 車載應(yīng)用開發(fā)指南(3)- SystemUI 詳解 SystemUI 全稱 System User Interface ,直譯過來就是 系統(tǒng)級用戶交互界面 ,在 Android 系統(tǒng)中由 SystemUI 負(fù)責(zé)

    2024年02月19日
    瀏覽(50)
  • Android automotive車載開發(fā)(1)-----Automotive audio

    Android automotive車載開發(fā)(1)-----Automotive audio

    車載音頻 Android Automotive OS (AAOS) 是在核心 Android 音頻堆棧的基礎(chǔ)之上打造而成,以支持用作車輛信息娛樂系統(tǒng)的用例。AAOS 負(fù)責(zé)實(shí)現(xiàn)信息娛樂聲音(即媒體、導(dǎo)航和通訊聲音),但不直接負(fù)責(zé)具有嚴(yán)格可用性和計(jì)時(shí)要求的鈴聲和警告。雖然 AAOS 提供了信號和機(jī)制來幫助車輛管

    2023年04月08日
    瀏覽(21)
  • 車載Android應(yīng)用開發(fā)與分析 - 初試 SystemUI Plugin

    車載Android應(yīng)用開發(fā)與分析 - 初試 SystemUI Plugin

    在前面的視頻、文章中我們介紹完了整個(gè)車載Android應(yīng)用開發(fā)所需要的基礎(chǔ)知識: 【視頻文稿】車載Android應(yīng)用開發(fā)與分析 - 走進(jìn)車載操作系統(tǒng) - 掘金 【視頻文稿】車載Android應(yīng)用開發(fā)與分析 - AOSP的下載與編譯 - 掘金 【視頻文稿】車載Android應(yīng)用開發(fā)與分析 - 開發(fā)系統(tǒng)應(yīng)用 - 掘

    2024年02月02日
    瀏覽(24)
  • android多屏觸摸相關(guān)的詳解方案-安卓framework開發(fā)手機(jī)車載車機(jī)系統(tǒng)開發(fā)課程

    android多屏觸摸相關(guān)的詳解方案-安卓framework開發(fā)手機(jī)車載車機(jī)系統(tǒng)開發(fā)課程

    直播免費(fèi)視頻課程地址:https://www.bilibili.com/video/BV1hN4y1R7t2/ 在做雙屏相關(guān)需求開發(fā)過程中,經(jīng)常會有對兩個(gè)屏幕都要求可以正確觸摸的場景。但是目前我們模擬器默認(rèn)創(chuàng)建的雙屏其實(shí)是沒有辦法進(jìn)行觸摸的 靜態(tài)修改方案 使用命令查看display2即副屏的信息情況 adb shell dumpsys d

    2024年02月11日
    瀏覽(24)
  • 第二節(jié)-安卓多屏雙屏實(shí)戰(zhàn)車載車機(jī)智能駕駛艙開發(fā)/千里馬android framwork開發(fā)

    第二節(jié)-安卓多屏雙屏實(shí)戰(zhàn)車載車機(jī)智能駕駛艙開發(fā)/千里馬android framwork開發(fā)

    hi,粉絲朋友們! 上一節(jié)已經(jīng)對車載的多屏互動(dòng)進(jìn)行了相關(guān)的技術(shù)方案介紹,以及相關(guān)的核心方法 moveRootTaskToDisplay的講解和使用。 具體可以參考鏈接:https://blog.csdn.net/learnframework/article/details/130461689 本節(jié)就來進(jìn)行代碼實(shí)戰(zhàn) 要實(shí)現(xiàn)雙屏互動(dòng),主要就只需要兩個(gè)步驟: 1、手指動(dòng)

    2024年02月09日
    瀏覽(20)
  • Android 11 系統(tǒng)開發(fā)增加低電量彈窗提示 手機(jī) 平板 車載 TV 投影 通用

    SystemUIService 中啟動(dòng)PowerUI 主要修改 5、在symbols 文件中添加對應(yīng)java-symbol方便Framework代碼引用code

    2024年03月15日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包