Android 10 将桌面设置图标长按出现的弹框中的电池去掉,去掉后如图:

这个主要是关于Android新功能Shortcuts的运用。
Launcher3的流程:
在packages\apps\Launcher3\src\com\android\launcher3\views\OptionsPopupView.java
找到布局packages\apps\Launcher3\res\layout\longpress_options_menu.xml
加载packages\apps\Launcher3\src\com\android\launcher3\shortcuts\DeepShortcutView.java、
packages\apps\Launcher3\res\layout\deep_shortcut.xml

在Launcher只是一个布局的加载,数据是在Settings.apk控制提供,

packages\apps\Settings\src\com\android\settings\shortcut\CreateShortcut.java
packages\apps\Settings\res\xml\create_shortcut.xml
packages\apps\Settings\src\com\android\settings\shortcut\CreateShortcutPreferenceController.java
在上面的三个文件是一个加载流程逻辑。
关键是这里
packages\apps\Settings\AndroidManifest.xml

<!-- Alias for launcher activity only, as this belongs to each profile. --><activity-alias android:name="Settings"android:label="@string/settings_label_launcher"android:launchMode="singleTask"android:targetActivity=".homepage.SettingsHomepageActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>**<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>**</activity-alias>

找到这个文件
packages\apps\Settings\res\xml\shortcuts.xml
更改即可

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" ><shortcutandroid:shortcutId="manifest-shortcut-wifi"android:icon="@drawable/ic_shortcut_wireless"android:shortcutShortLabel="@string/wifi_settings" ><intent android:action="android.settings.WIFI_SETTINGS" /></shortcut><shortcutandroid:shortcutId="manifest-shortcut-data-usage"android:icon="@drawable/ic_shortcut_data_usage"android:shortcutShortLabel="@string/data_usage_summary_title"><intentandroid:action="android.intent.action.MAIN"android:targetPackage="com.android.settings"android:targetClass="com.android.settings.Settings$DataUsageSummaryActivity" /></shortcut>
<!--    <shortcutandroid:shortcutId="manifest-shortcut-battery"android:icon="@drawable/ic_shortcut_battery"android:shortcutShortLabel="@string/power_usage_summary_title" ><intent android:action="android.intent.action.POWER_USAGE_SUMMARY" /></shortcut>-->
</shortcuts>

参考文章:https://my.oschina.net/shenhuniurou/blog/877148

Android7.1的Shortcuts也出来好久了,但是没有在自己的应用上尝试过,原因是之前手机一直是6.0的系统,直到最近才刷到了7.1.1,不过我发现,除了Google公司自己的app,适配了shortcuts功能的确实很少,可能是Android N的系统使用普及度太小,于是果断决定给我们公司的app加上shortcuts的功能,虽然是一些不起眼的功能,但至少要走在技术革新的最前沿吧。

shortcuts
使用及说明

关于Shortcuts的官方文档在这里App Shortcuts

Shortcuts的作用:官方的说法,让用户在你的app上快速开始一个常用或者推荐的任务。

使用条件:targetSdkVersion必须在25或者更高。

类型:静态(xml方式)和动态(代码方式)
注意事项:虽然其他app是不能在你app的shortcuts里访问元数据,但是启动器本身却可以访问这些数据。因此,我们应该在元数据中隐藏敏感的用户信息。

静态Shortcuts

之所以称为静态,是因为它是定义在资源文件中的,除非更新整个app,否则是不能更改shortcuts的东西,不过这种方式使用起来也跟简单,两步搞定。

第一步在AndroidManifest.xml中的启动activity中加上meta-data标签:其中name是固定的,resource是一个名为shortcuts.xml的资源文件。

<activityandroid:name=".ui.other.welcome.SplashActivity"android:configChanges="orientation|keyboardHidden|screenSize"android:label="@string/app_name"android:launchMode="singleTop"android:screenOrientation="portrait"android:theme="@style/AppTheme.NoActionBar"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter><meta-dataandroid:name="android.app.shortcuts"android:resource="@xml/shortcuts"/>
</activity>

第二步在res目录下新建xml资源目录,并在其下新建xml文件,命名随意。这里要吐槽下,在Android Studio里新建xm文件时,居然不能选择root-element是shortcuts的,还得自己手动去敲,气人。

另外还要吐槽下官方文档,上面说一个app同时可以show五个shortcuts(不管是静态还是动态),但实际上最多只能显示4个,多出的不会显示出来。

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"><shortcutandroid:enabled="true"android:icon="@drawable/icon1_chosen"android:shortcutId="mall"android:shortcutDisabledMessage="@string/shortcutDisabledMessage"android:shortcutLongLabel="@string/mall"android:shortcutShortLabel="@string/mall"><intentandroid:action="android.intent.action.VIEW"android:targetClass="com.zhiwuya.ehome.app.ui.main.MainActivity"android:targetPackage="com.zhiwuya.ehome.app"/><intentandroid:action="android.intent.action.VIEW"android:targetClass="com.zhiwuya.ehome.app.ui.home.activity.MallServiceActivity"android:targetPackage="com.zhiwuya.ehome.app"/></shortcut>
</shortcuts>

shortcut标签下面的属性:

android:shortcutId必须唯一;
android:icon是该shortcut的图标;
android:enabled表示该shortcus是否可用,false表示禁用,是不会在shortcuts列表中显示出来的;
android:shortcutDisabledMessage表示当shortcut拖到手机桌面固定时,而enabled又是false禁用时点击桌面上的icon时提示给用户的文字,而且此时桌面上的icon会变灰色;
android:shortcutLongLabel是在shortcuts列表中该shortcut的名字;
android:shortcutShortLabel是拖动到桌面上显示的该shortcut的名字;
intent是点击shortcut时跳转到的app内的页面,其中action是必须的,而且是能是android.intent.action.VIEW

intent可以设置多个,相当于是按顺序将这些activity放入任务栈中,最后一个activity在栈顶,因此,点击shortcut时跳转的是最后一个intent指定的activity页面,当按返回键时,则会回退到上一个intent指定的activity。

shortcut标签也可以设置多个(最多4个),在xml中从上至下的顺序,最上面的离app在桌面的icon越近,越往下越远。

动态Shortcuts

动态即在app运行时执行发布更新和移除shortcuts等操作。

private void initShortcuts() {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {return;}ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);Intent mall = new Intent(Intent.ACTION_VIEW);mall.setClass(this, MallServiceActivity.class);mall.setPackage(BuildConfig.APPLICATION_ID);ShortcutInfo mallShortcut = new ShortcutInfo.Builder(this, "mall").setShortLabel(getString(R.string.mall)).setLongLabel(getString(R.string.mall)).setDisabledMessage(getString(R.string.shortcutDisabledMessage)).setIcon(Icon.createWithResource(this, R.drawable.icon1_chosen)).setIntent(mall).build();Intent ticket = new Intent(Intent.ACTION_VIEW);ticket.setClass(this, OrderTicketDescActivity.class);ticket.setPackage(BuildConfig.APPLICATION_ID);ShortcutInfo ticketShortcut = new ShortcutInfo.Builder(this, "tuan").setShortLabel(getString(R.string.tuan)).setLongLabel(getString(R.string.tuan)).setDisabledMessage(getString(R.string.shortcutDisabledMessage)).setIcon(Icon.createWithResource(this, R.drawable.icon3_chosen)).setIntent(ticket).build();shortcutManager.setDynamicShortcuts(Arrays.asList(mallShortcut, ticketShortcut));
}

ShortcutInfo.Builder的第二个参数即shortcutId,不能重复,否则会覆盖。setDynamicShortcuts方法是设置一组shortcuts,addDynamicShortcuts则是在原来的基础上增加一组,updateShortcuts是更新一组,removeDynamicShortcuts删除一组,removeAllDynamicShortcuts删除所有。这些方法对shortcut操作时是根据其唯一的shortcutId来的,所以这玩意千万不能重复的。

另外ShortManager还有一个方法getPinnedShortcuts,作用是获取被拖动固定到桌面上的shortcuts,这些shortcuts是不能被在代码中被修改删除的,只能通过disableShortcuts方法禁用,禁用后icon会变灰色,并且点击时提示shortcutDisabledMessage。

当某个ShortcutInfo.Builder需要设置多个Intent时,可以使用setIntents方法来代替setIntent方法。


所有应用在workspace第一页中显示

修改之后看效果,发现应用icon都是在第二页中显示,除了默认的几个icon在第一页

  packages/apps/Launcher3/src/com/android/launcher3/model/AddWorkspaceItemsTask.javaint screenCount = workspaceScreens.size();// First check the preferred screen.     // add by // int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;   int preferredScreenIndex = 0;//endif (preferredScreenIndex < screenCount) {

参考文章:https://blog.csdn.net/wxd_csdn_2016/article/details/100602869
https://blog.csdn.net/qq_24800377/article/details/78979387

最近在搞Android的Launcher开发,去掉应用列表后,每次安装应用应用图标都会放在第二页。查看源码后,发现安装应用会走LauncherModel的addAndBindAddedWorkspaceApps方法,添加绑定一个应用图标到workspace中。

public void addAndBindAddedWorkspaceApps(final Context context,final ArrayList<ItemInfo> workspaceApps) {

而addAndBindAddedWorkspaceApps方法又是通过findNextAvailableIconSpace方法寻找一个空位置存放应用图标。这里如果找不到,就会把页数++。

// Add this icon to the db, creating a new page if necessary.  If there
// is only the empty page then we just add items to the first page.
// Otherwise, we add them to the next pages.
int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,name, launchIntent, startSearchPageIndex, workspaceScreens);

在findNextAvailableIconSpace方法中,我发现总是从我Launcher的第二个页面开始往下寻找空位置。所以我修改了这个方法,让他从第一页开始寻找。这样就完美地解决了安装应用后,应用图标放置位置的问题。

static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,Intent launchIntent,int firstScreenIndex,ArrayList<Long> workspaceScreens) {// Lock on the app so that we don't try and get the items while apps are being addedLauncherAppState app = LauncherAppState.getInstance();LauncherModel model = app.getModel();boolean found = false;synchronized (app) {if (sWorkerThread.getThreadId() != Process.myTid()) {// Flush the LauncherModel worker thread, so that if we just did another// processInstallShortcut, we give it time for its shortcut to get added to the// database (getItemsInLocalCoordinates reads the database)model.flushWorkerThread();}final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);// Try adding to the workspace screens incrementally, starting at the default or center// screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))//修改控制安装后图标位置firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());int count = workspaceScreens.size();firstScreenIndex = firstScreenIndex >= 1 ? firstScreenIndex - 1 : firstScreenIndex;for (int screen = firstScreenIndex; screen < count && !found; screen++) {int[] tmpCoordinates = new int[2];if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,workspaceScreens.get(screen))) {// Update the Launcher dbreturn new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);}}}return null;
}

2.切换简易模式的问题。

我的程序是使用华为安全授权设置自己的程序为Launcher的

在华为手机切换系统桌面风格和切换简易模式时,都会更改系统的Launcher的,所以要在设置这些时会不显示我的程序的Launcher的,试过找广播和置顶的Activity(监听切换到这个Activity时直接执行home键代码,不让用户去设置)来解决问题。但最终还是没找到相关的广播监听,而监听置顶的Activity只能看到自己进程的Activity,无法查看到其他进程的置顶Activity,所以这两个方法都失败了。最后只能默默是查看代码了。在看到切换Launcher时都会有一句Configuration changed代码时,就想到在application中的

onConfigurationChanged()方法在切换语言时会调到这个方法就试试,居然就可以了,所以在这个方法中去重新设置自己的程序为Launcher就解决了。

3.桌面图标角标问题。

首先要在设置中打开显示圆点角标的显示
(1)跳转设置页面打开权限

if (!NotificationManagerCompat.getEnabledListenerPackages(this).contains(getPackageName())) {startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
}

(2)将角标变为特定颜色。
将mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);改为
mBadgeColor = Color.RED;即可。
如果要显示数字在网上找到一个类似的方法:https://blog.csdn.net/sex_baby/article/details/103874015

(3)设置角标后,当改变桌面图标的形状时就角标就不显示。
好像是因为NotificationListenerService的onBind有一个标志没有被更改。(因为NotificationListenerService是一直存活的Service)
参考:NotificationListenerService失效的两种情况
https://blog.csdn.net/u013836857/article/details/82732906
第二个链接主要看重置部分的代码的。
下面就整理出来的部分代码:
在LauncherAppState类的构造方法LauncherAppState(Context context)中的


mNotificationBadgingObserver = new SettingsObserver.Secure(mContext.getContentResolver()){@Overridepublic void onSettingChanged(boolean isNotificationBadgingEnabled) {if (isNotificationBadgingEnabled) {ensureCollectorRunning();NotificationListener.requestRebind(new ComponentName(mContext, NotificationListener.class));}}
};/*** 重置NotificationListenerService的标志*/
private void ensureCollectorRunning() {ComponentName collectorComponent = new ComponentName(mContext, NotificationListener.class);ActivityManager manager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);boolean collectorRunning = false;List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);if (runningServices != null) {for (ActivityManager.RunningServiceInfo service : runningServices) {if (service.service.equals(collectorComponent)) {if (service.pid == android.os.Process.myPid()) {collectorRunning = true;}}}if (!collectorRunning) {PackageManager pm = mContext.getPackageManager();pm.setComponentEnabledSetting(collectorComponent,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);pm.setComponentEnabledSetting(collectorComponent,PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);//这里为什么执行两次呢,只知道执行一次无法重置成功,显示角标。}}
}

https://blog.csdn.net/qq_34709057/article/details/102633159?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control


添加未读信息角标

Launcher3中添加未读信息角标

添加未读信息部分参考Android之前版本即可

首先在源码中加入对应的监听(公司使用GMS项目,因此修改的是GMS包下的overlay)

vendor/partner_gms/products/gms_overlay/frameworks/base/core/res/res/values/config.xml

-     <string name="config_defaultListenerAccessPackages" translatable="false">com.google.android.setupwizard:com.google.android.apps.restore</string>
+    <string name="config_defaultListenerAccessPackages" translatable="false">com.android.launcher3:com.google.android.setupwizard:com.google.android.apps.restore</string>

在对应的文件中注释掉Notification的拦截和低内存设备的判定

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java+                //if (mAllowedManagedServicePackages.test(listener.getPackageName())) {mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),userId, false, granted);mListeners.setPackageOrComponentEnabled(listener.flattenToString(),userId, true, granted);getContext().sendBroadcastAsUser(new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED).setPackage(listener.getPackageName()).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),UserHandle.of(userId), null);savePolicyFile();
+                //}

packages/apps/Settings/src/com/android/settings/notification/ZenAccessSettings.java

// if (!ActivityManager.isLowRamDeviceStatic()) {reloadList();getContentResolver().registerContentObserver(Secure.getUriFor(Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), false,mObserver);getContentResolver().registerContentObserver(Secure.getUriFor(Secure.ENABLED_NOTIFICATION_LISTENERS), false,mObserver);// } else {//     setEmptyText(R.string.disabled_low_ram_device);// }packages/apps/Settings/src/com/android/settings/utils/ManagedServiceSettings.java// if (!ActivityManager.isLowRamDeviceStatic()) {mServiceListing.reload();mServiceListing.setListening(true);// } else {//     setEmptyText(R.string.disabled_low_ram_device);// }

在Launcher3中修改对应文件的函数,使其满足传递对应的未读数量

packages/apps/Launcher3/src/com/android/launcher3/badge/BadgeRenderer.java

public void draw(
-            Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
+            Canvas canvas, int color, @Nullable BadgeInfo badgeInfo,
+            Rect iconBounds, float badgeScale, Point spaceForOffset) {...
+        String notificationCount = badgeInfo == null ? "0"
+                : String.valueOf(badgeInfo.getNotificationCount());
+        int numChars = notificationCount.length();canvas.save();
+
+        boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;-        mCirclePaint.setColor(Color.BLACK);
-        canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
-        mCirclePaint.setColor(color);
-        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
+        if (isText) {
+            mTextPaint.setColor(Color.RED);
+            canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset,
+                    mBitmapOffset, mTextPaint);
+            mTextPaint.setColor(Color.WHITE);
+            canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
+        } else {
+            mCirclePaint.setColor(Color.BLACK);
+            canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
+            mCirclePaint.setColor(color);
+            canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
+        }

将调用BadgeRenderer中draw方法的地方,修改:

packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java

-            mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
-                    mTempSpaceForBadgeOffset);
+            mBadgeRenderer.draw(canvas, mBadgeColor, mBadgeInfo, mTempIconBounds,
+                    mBadgeScale, mTempSpaceForBadgeOffset);packages/apps/Launcher3/src/com/android/launcher3/folder/FolderIcon.java-            mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
-                    badgeScale, mTempSpaceForBadgeOffset);
+            mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mBadgeInfo, mTempBounds,
+                   badgeScale, mTempSpaceForBadgeOffset);

注意:FolderBadgeInfo中默认返回的数量为0,需要手动修改

packages/apps/Launcher3/src/com/android/launcher3/badge/FolderBadgeInfo.java

public int getNotificationCount() {// This forces the folder badge to always show up as a dot.
-        return 0;
+        return mNumNotifications; }

来自:https://blog.csdn.net/sex_baby/article/details/103874015

参考:
https://blog.csdn.net/wxd_csdn_2016/article/details/100602869
去掉qsb

packages/apps/Launcher3/src/com/android/launcher3/Workspace.java

 注释掉下面这段代码if(!FeatureFlags.REMOVE_DRAWER){// Always add a QSB on the first screen.if (qsb == null) {// In transposed layout, we add the QSB in the Grid. As workspace does not touch the// edges, we do not need a full width QSB.qsb = LayoutInflater.from(getContext()).inflate(R.layout.search_container_workspace,firstPage, false);}CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);lp.canReorder = false;if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");}}

1.去掉搜索框和切换系统桌面风格。

首先search_container_workspace.xml就是搜索框的布局,如果只是简单的隐藏,只要将布局的高度设置为0dp就行了。

如果要将桌面图标拉到原来放搜索框的位置只是隐藏是不行的。

看代码mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb /);就是将搜索框用代码放到launcher上的,要去掉就得进入此方法中有一个布尔值**FeatureFlags.QSB_ON_FIRST_SCREEN***,这个值默认为true的,将其设置为false就可以去掉搜索框了(而且布局也不用去修改也可以去掉搜索框了)。


去掉hotseat
packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java

// addif (FeatureFlags.REMOVE_DRAWER) {lp.height = 0;}// endhotseat.setLayoutParams(lp);

隐藏allapps button

packages/apps/Launcher3/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
 @Overrideprotected void onFinishInflate() {super.onFinishInflate();mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);mAllAppsHandle.setVisibility(View.GONE);     //  add for hide allapp button...}

hotseat中应用的icon不能被添加到workspace

所有应用可以再第一页显示,但是发现少了几个应用的icon,开始没注意到是hotseat中的几个应用,后面抓log发现,loadworkspace时会做判断,dw_phone_hotseat.xml中标记-101,不会添加,具体代码在 LoadCursor中
注释掉dw_phone_hotseat.xml中的代码

修改workspace行列

packages/apps/Launcher3/src/com/android/launcher3/InvariantDeviceProfile.java

 // add//numRows = closestProfile.numRows;//numColumns = closestProfile.numColumns;numRows = 4;numColumns = 3;// end

onResume状态时,HotSeat和这个箭头会一起做一个向上跳动的动画

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

 if (shouldShowDiscoveryBounce()) {//注释 begin// mAllAppsController.showDiscoveryBounce();//注释 end}if (mLauncherCallbacks != null) {mLauncherCallbacks.onResume();}

应用排序

packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java
+import com.android.launcher3.allapps.AppInfoComparator;if (FeatureFlags.REMOVE_DRAWER && (mBgAllAppsList.data.size() > 0)) {
+    AppInfoComparator mAppNameComparator = new AppInfoComparator(mApp.getContext());ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>(mBgAllAppsList.data);
+    Collections.sort(appInfos, mAppNameComparator);mApp.getModel().addAndBindAddedWorkspaceItems(new LzyProvider(appInfos));}

修改workspace中item拖拽移除为取消
packages/apps/Launcher3/src/com/android/launcher3/DeleteDropTarget.java

 public void onDragStart(DropTarget.DragObject dragObject, DragOptions options){super.onDragStart(dragObject, options);
-        setTextBasedOnDragSource(dragObject.dragSource);
+       // setTextBasedOnDragSource(dragObject.dragSource);
+        setTextBasedOnDragSource(dragObject.dragInfo);}public void setTextBasedOnDragSource(ItemInfo item) {if (!TextUtils.isEmpty(mText)) {
-            mText = getResources().getString(dragSource.supportsDeleteDropTarget()
-                    ? R.string.remove_drop_target_label
-                    : android.R.string.cancel);
+           mText = getResources().getString(!isCanDrop(item)
+                    ? R.string.remove_drop_target_label
+                    : android.R.string.cancel);requestLayout();}+ private boolean isCanDrop(ItemInfo item){
+        return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+            item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+ }  

packages/apps/Launcher3/src/com/android/launcher3/dragndrop/DragController.java

+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.LauncherSettings;...
private void drop(DropTarget dropTarget, Runnable flingAnimation) {...// Drop onto the target.boolean accepted = false;if (dropTarget != null) {dropTarget.onDragExit(mDragObject);if (dropTarget.acceptDrop(mDragObject)) {if (flingAnimation != null) {flingAnimation.run();} else if (!mIsInPreDrag) {dropTarget.onDrop(mDragObject);}+            if (dropTarget instanceof DeleteDropTarget && isCanDrop(mDragObject.dragInfo)) {
+                cancelDrag();
+            }accepted = true;}}...
+ private boolean isCanDrop(ItemInfo item){
+        return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+            item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+ }
}

隐藏左边箭头和PageIndicator
packages/apps/Launcher3/res/layout/launcher.xml

     we go into AllApps --><com.android.launcher3.pageindicators.WorkspacePageIndicatorandroid:id="@+id/page_indicator"android:layout_width="match_parent"android:layout_width="0dp"android:layout_height="@dimen/vertical_drag_handle_size"android:layout_gravity="bottom|center_horizontal"android:theme="@style/HomeScreenElementTheme" /><includeandroid:id="@+id/scrim_view"layout="@layout/scrim_view" />layout="@layout/scrim_view"android:visibility="gone" /><includeandroid:id="@+id/apps_view"

修改PageIndicator为小圆点

packages/apps/Launcher3/res/layout/launcher.xml
+++ b/alps/packages/apps/Launcher3/res/layout/launcher.xml<!-- Keep these behind the workspace so that they are not visible whenwe go into AllApps -->
-        <com.android.launcher3.pageindicators.WorkspacePageIndicator
+        <!-- <com.android.launcher3.pageindicators.WorkspacePageIndicator -->
+        <com.android.launcher3.pageindicators.PageIndicatorDotsandroid:id="@+id/page_indicator"android:layout_width="match_parent"
-            android:layout_height="@dimen/vertical_drag_handle_size"
+            android:layout_height="4dp"android:layout_gravity="bottom|center_horizontal"android:theme="@style/HomeScreenElementTheme" />

packages/apps/Launcher3/src/com/android/launcher3/Workspace.java

 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorDots;import com.android.launcher3.popup.PopupContainerWithArrow;import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;import com.android.launcher3.testing.TestProtocol;
@@ -110,7 +111,8 @@ import java.util.function.Predicate;* Each page contains a number of icons, folders or widgets the user can* interact with. A workspace is meant to be used with a fixed width only.*/
-public class Workspace extends PagedView<WorkspacePageIndicator>
+// public class Workspace extends PagedView<WorkspacePageIndicator>
+public class Workspace extends PagedView<PageIndicatorDots>implements DropTarget, DragSource, View.OnTouchListener,DragController.DragListener, Insettable, LauncherStateManager.StateHandler,WorkspaceLayoutManager {

packages/apps/Launcher3/src/com/android/launcher3/pageindicators/PageIndicatorDots.java

import com.android.launcher3.Utilities;import com.android.launcher3.util.Themes;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import android.widget.FrameLayout;
+import android.graphics.Rect;
+import android.view.Gravity;/*** {@link PageIndicator} which shows dots per page. The active page is shown with the current* accent color.*/
-public class PageIndicatorDots extends View implements PageIndicator {
+public class PageIndicatorDots extends View implements PageIndicator, Insettable {private static final float SHIFT_PER_ANIMATION = 0.5f;private static final float SHIFT_THRESHOLD = 0.1f;
@@ -97,6 +103,8 @@ public class PageIndicatorDots extends View implements PageIndicator {private float[] mEntryAnimationRadiusFactors;+    private final Launcher mLauncher;
+public PageIndicatorDots(Context context) {this(context, null);}
@@ -117,6 +125,41 @@ public class PageIndicatorDots extends View implements PageIndicator {mInActiveColor = Themes.getAttrColor(context, android.R.attr.colorControlHighlight);mIsRtl = Utilities.isRtl(getResources());
+
+        mLauncher = Launcher.getLauncher(context);
+    }
+
+    //add for change WorkspacePageIndicator line to dot
+    @Override
+    public void setInsets(Rect insets) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+
+        if (grid.isVerticalBarLayout()) {
+            Rect padding = grid.workspacePadding;
+            lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+            lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+            lp.bottomMargin = padding.bottom;
+        } else {
+            lp.leftMargin = lp.rightMargin = 0;
+            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+            lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+        }
+        setLayoutParams(lp);
+    }
+
+        /**
+     * Pauses all currently running animations.
+     */
+    public void pauseAnimations() {
+        stopAllAnimations();
+    }
+
+    /**
+     * Force-ends all currently running or paused animations.
+     */
+    public void skipAnimationsToEnd() {
+        stopAllAnimations();}@Override
@@ -126,6 +169,10 @@ public class PageIndicatorDots extends View implements PageIndicator {currentScroll = totalScroll - currentScroll;}int scrollPerPage = totalScroll / (mNumPages - 1);
+            //add for change WorkspacePageIndicator line to dot
+            if (scrollPerPage == 0) {
+                return;
+            }int pageToLeft = currentScroll / scrollPerPage;int pageToLeftScroll = pageToLeft * scrollPerPage;int pageToRightScroll = pageToLeftScroll + scrollPerPage;

packages/apps/Launcher3/src/com/android/launcher3/states/SpringLoadedState.java

     public void onStateEnabled(Launcher launcher) {Workspace ws = launcher.getWorkspace();ws.showPageIndicatorAtCurrentScroll();
-        ws.getPageIndicator().setShouldAutoHide(false);
+        // ws.getPageIndicator().setShouldAutoHide(false);// Prevent any Un/InstallShortcutReceivers from updating the db while we are// in spring loaded mode
@@ -96,7 +97,8 @@ public class SpringLoadedState extends LauncherState {@Overridepublic void onStateDisabled(final Launcher launcher) {
-        launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
+        // launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);// Re-enable any Un/InstallShortcutReceiver and now process any queued itemsInstallShortcutReceiver.disableAndFlushInstallQueue(

Lancher3添加默认widget
原文链接:https://blog.csdn.net/qq_30552095/article/details/84790453

话不多说,直接上代码
在res/xml/default_workspace_4x4.xml中添加

<appwidgetlauncher:container="-100"launcher:packageName="com.android.deskclock"launcher:className="com.android.alarmclock.DigitalAppWidgetProvider"launcher:screen="0"launcher:spanX="5"launcher:spanY="2"launcher:x="0"launcher:y="2"/>

其中
a. launcher:container="-100",表示添加在 desktop 中,如果是-101那就是在 HotSeat 中,但是这里我们的 widget 是要添加在 desktop,所以是-100;
b. launcher:packageName=“com.android.deskclock”,这个没啥说的,就是widget的包名,我这里添加的是数字时钟,所以这里填写的是 时钟模块 的包名;
c. launcher:className=“com.android.alarmclock.DigitalAppWidgetProvider”,这个是 widget 所在的类,这是是数字时钟,如果要添加 表盘时钟(指针时钟),就填写com.android.alarmclock.AnalogAppWidgetProvider;
d. launcher:screen=“0”,这个是添加在哪一屏;
e. launcher:spanX=“5”,这个表示 widget 在 x 方向上占位多少,我的launcher是 x 方向可以放5个APP图标,所以这里widget是占满整个 x 方向;
f. launcher:spanY=“2”,这个表示 widget 在 y 方向上站位多少,2表示占用相当于两个APP图标的高度;
g. launcher:x=“0”,这个表示 widget 的 x 方向上的位置,这里0表示从屏幕最左侧开始显示;
h. launcher:y=“2”,这个表示 widget 的 y 方向上的位置,这里3表示从上往下第3个位置开始显示(从0开始,所以2就是第3个)。
————————————————

这样就结束了。

但是有一个偶现的问题:有时widget会添加不上!
找了一下原因,发现在
src/com/android/launcher3/DefaultLayoutParser.java中:
protected class AppWidgetParser extends PendingWidgetParser {

    @Overrideprotected long verifyAndInsert(ComponentName cn, Bundle extras) {......final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);long insertedId = -1;try {int appWidgetId = mAppWidgetHost.allocateAppWidgetId();if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {Log.e(TAG, "Unable to bind app widget id " + cn);mAppWidgetHost.deleteAppWidgetId(appWidgetId);return -1;}......

每一个widget添加的时候都会存到 launcher.db 中,失败的时候 appWidgetId 的值是0,这里调用的是

framework/base/core/java/android/appwidget/AppWidgetHost.java->allocateAppWidgetId()
/*** Get a appWidgetId for a host in the calling process.** @return a appWidgetId*/public int allocateAppWidgetId() {if (sService == null) {return -1;}try {return sService.allocateAppWidgetId(mContextOpPackageName, mHostId);}catch (RemoteException e) {throw new RuntimeException("system server dead?", e);}}

最终调用的是:
framework/base/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java->allocateAppWidgetId()
@Override
public int allocateAppWidgetId(String callingPackage, int hostId) {
final int userId = UserHandle.getCallingUserId();

    if (DEBUG) {Slog.i(TAG, "allocateAppWidgetId() " + userId);}// Make sure the package runs under the caller uid.mSecurityPolicy.enforceCallFromPackage(callingPackage);synchronized (mLock) {ensureGroupStateLoadedLocked(userId);if (mNextAppWidgetIds.indexOfKey(userId) < 0) {mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1);}final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId);// NOTE: The lookup is enforcing security across users by making// sure the caller can only access hosts it owns.HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);Host host = lookupOrAddHostLocked(id);Widget widget = new Widget();widget.appWidgetId = appWidgetId;widget.host = host;host.widgets.add(widget);addWidgetLocked(widget);saveGroupStateAsync(userId);if (DEBUG) {Slog.i(TAG, "Allocated widget id " + appWidgetId+ " for host " + host.id);}return appWidgetId;}
}

找了一会还是没找到最终出现这个问题的原因,如果有同学知道原因,还请不吝指教

Android 9/10 Launcher3 适配出现的问题修改相关推荐

  1. 华为android o适配名单,华为给出首批升级名单,这8款华为手机率先适配Android Q 10系统...

    原标题:华为给出首批升级名单,这8款华为手机率先适配Android Q 10系统 昨天,在谷歌的I/O大会上,谷歌正式向消费者介绍了Android Q 10系统中的新功能,除了自家的Pixel设备率先 ...

  2. Android Q(10.0)版本新特性以及兼容性适配

    北京时间2019年3月14日Google正式对外发布Android Q Beta 1及预览版SDK,这意味着安卓开发者们又即将迎来一年一度的新版本适配工作了.Android Q 为开发者们带来了许多新 ...

  3. 小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!

    点击上方"何俊林",马上关注,每天早上8:50准时推送 真爱,请置顶或星标 转载自公众号:刘望舒,作者: 吃猫猫的鱼 地址:https://juejin.im/post/5cad5 ...

  4. Android APP全面屏适配技术要点

    全面屏的概念 为什么先要解释一下全面屏,因为这个词在现在来讲就是一个伪命题.全面屏字面意思就是手机的正面全部都是屏幕,100%的屏占比.但是现在推出所谓"全面屏"手机的厂商没有一个 ...

  5. 《转》Android 今日头条屏幕适配方案终极版正式发布!

    概述 Android系统发布十多年以来,关于Android的UI的适配一直是开发环节中最重要的问题,但是我看到还是有很多小伙伴对Android适配方案不了解. 刚好,近期准备对糗事百科Android客 ...

  6. Android 9.0 简单适配

    转载请声明,本文来自: 本文仅描述当前项目apk遇到的适配9.0问题,所以不是全面的,请自行参考: 适配一: 纯后台服务,需要在AndroidManifest.xml添加权限 <uses-per ...

  7. Android Studio 屏幕分辨率适配

    android studio进行android 应用屏幕分辨率适配使用建立不同dimens.xml方法,刚开始建立的文件夹都叫 values-1280x720 values-800x400之类的,发现 ...

  8. Android 11 变更及适配攻略

    终于开始了Android 11的适配工作.记录一下,供需要的人参考. 1. 准备工作 老规矩,首先将我们项目中的 targetSdkVersion 改为 30.或者使用兼容性调试工具,后面我会说到. ...

  9. Android高手笔记-屏幕适配 UI优化

    Android高手笔记-屏幕适配 & UI优化 屏幕与适配 由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,适配成本越来越高: 屏幕适配究其根本 ...

最新文章

  1. Chrome与chromedriver版本对应
  2. 已知bug列表——Solidity中文文档(12)
  3. ASM_PREFERRED_READ_FAILURE_GROUPS
  4. 如何在Java中转义JSON字符串-Eclipse IDE技巧
  5. django后端用websocket传输数据
  6. Pentium 4处理器架构/微架构/流水线 (8) - NetBurst执行核详解 - 指令时延与吞吐量
  7. 移动端业务数据管理平台+健康管理平台+banner管理+图标管理+订单管理+门店内容管理+用户信息管理+版本更新管理Axure通用web端高保真交互app业务数据管理平台
  8. 解决PowerPoint英语课件配音难同步的问题
  9. RAC11g使用数据泵导入导出报ORA-6512,ORA-25306,ORA-39079错
  10. UVA516 POJ1365 LA5533 ZOJ1261 Prime Land【欧拉筛法】
  11. 《老罗Android开发视频教程》
  12. 数据可视化常用LED字体
  13. 转fatfs 文件系统选择
  14. opencv学习 给视频添加文字
  15. 一个故事轻松记忆常见252个英语字根(31~80)
  16. 笔记本电脑总是锁定计算机呢,笔记本电脑键盘锁定了怎么办有什么方法解锁
  17. 基于微信小程序的大学生心理健康测试设计与实现 .docx
  18. 怎么把计算机里面的解压到桌面上,电脑桌面怎么加压文件
  19. SQL-聚合函数结果作为筛选条件时where与having用法的区别
  20. Python写文件到指定路径以及读取文件内容

热门文章

  1. 年底了,开发板开始搞促销了,推荐几款超值的ARM开发板。
  2. 基于51单片机和OLED屏幕的贪吃蛇游戏
  3. 地图导航开启蓝牙后无导航语音
  4. java集合List解析
  5. 《Total Commander:万能文件管理器》——第2.2节.快速了解一款软件的4条途径
  6. 中国政府融资平台态势分析及发展前景规划评估研究报告2022-2028年版
  7. 看看那些穷苦的人们... [图]
  8. 部署C#服务到win7/Windows server 2008 R2服务器上
  9. S32K AUTOSAR ISOLAR工程配置-ComM/EcuM
  10. 2021年全球与中国线性滑轨行业市场规模及发展前景分析