欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android Launcher3删除风格

程序员文章站 2022-04-29 10:35:02
摘要:本文主要针对客户需求将Launcher3删除风格从顶部修改为在图标旁显示。本文先从android 5.1,高通msm8916平台,对Launcher3的删除显示风格进行分析,然后在此基础上将新的删除风格添加到相应的代码中。1.Launcher3删除流程分析Launcher3原生的删除风格是长按图标,会在顶部中间出复现删除区域,通过拖动图标至删除区域实现接下来的删除操作。通过logcat分析,发现删除是调用系统的PackageInstaller.apk里的接口实现的,根据经验全局搜索Intent.A...

摘要:本文主要针对客户需求将Launcher3删除风格从顶部修改为在图标旁显示。本文先从android 5.1,高通msm8916平台,对Launcher3的删除显示风格进行分析,然后在此基础上将新的删除风格添加到相应的代码中。

1.Launcher3删除流程分析

Launcher3原生的删除风格是长按图标,会在顶部中间出复现删除区域,通过拖动图标至删除区域实现接下来的删除操作。通过logcat分析,发现删除是调用系统的PackageInstaller.apk里的接口实现的,根据经验全局搜索Intent.ACTION_DELETE在Launcher.java中找到代码位置:

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
    // returns true if the activity was started
    boolean startApplicationUninstallActivity(ComponentName componentName, int flags) {
        if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
            // System applications cannot be installed. For now, show a toast explaining that.
            // We may give them the option of disabling apps this way.
            int messageId = R.string.uninstall_system_app_text;
            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
            return false;
        } else {
            String packageName = componentName.getPackageName();
            String className = componentName.getClassName();
            Intent intent = new Intent(
                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            startActivity(intent);
            return true;
        }
    }

找到最终实现删除的代码,就可以通过在方法入口处添加log追踪trace:

android.util.Log.d(TAG, "trace: ", new Exception());
packages/apps/Launcher3/src/com/android/launcher3/DeleteDropTarget.java
    private void completeDrop(DragObject d) {
        ItemInfo item = (ItemInfo) d.dragInfo;
        boolean wasWaitingForUninstall = mWaitingForUninstall;
        mWaitingForUninstall = false;
        if (isAllAppsApplication(d.dragSource, item)) {
            // Uninstall the application if it is being dragged from AppsCustomize
            AppInfo appInfo = (AppInfo) item;
            mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags);
        } else if (isUninstallFromWorkspace(d)) {
            ShortcutInfo shortcut = (ShortcutInfo) item;
            if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
                final ComponentName componentName = shortcut.intent.getComponent();
                final DragSource dragSource = d.dragSource;
                int flags = AppInfo.initFlags(
                    ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName()));
                mWaitingForUninstall =
                    mLauncher.startApplicationUninstallActivity(componentName, flags); // 此处调用
                if (mWaitingForUninstall) {
                    final Runnable checkIfUninstallWasSuccess = new Runnable() {
                        @Override
                        public void run() {
                            mWaitingForUninstall = false;
                            String packageName = componentName.getPackageName();
                            List<ResolveInfo> activities =
                                    AllAppsList.findActivitiesForPackage(getContext(), packageName);
                            boolean uninstallSuccessful = activities.size() == 0;
                            if (dragSource instanceof Folder) {
                                ((Folder) dragSource).
                                    onUninstallActivityReturned(uninstallSuccessful);
                            } else if (dragSource instanceof Workspace) {
                                ((Workspace) dragSource).
                                    onUninstallActivityReturned(uninstallSuccessful);
                            }
                        }
                    };
                    mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
                }
            }
        } else if (isWorkspaceOrFolderApplication(d)) {
            LauncherModel.deleteItemFromDatabase(mLauncher, item);
        } else if (isWorkspaceFolder(d)) {
            // Remove the folder from the workspace and delete the contents from launcher model
            FolderInfo folderInfo = (FolderInfo) item;
            mLauncher.removeFolder(folderInfo);
            LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo);
        } else if (isWorkspaceOrFolderWidget(d)) {
            // Remove the widget from the workspace
            mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
            LauncherModel.deleteItemFromDatabase(mLauncher, item);

            final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
            final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
            if (appWidgetHost != null) {
                // Deleting an app widget ID is a void call but writes to disk before returning
                // to the caller...
                new AsyncTask<Void, Void, Void>() {
                    public Void doInBackground(Void ... args) {
                        appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
                        return null;
                    }
                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
            }
        }
        if (wasWaitingForUninstall && !mWaitingForUninstall) {
            if (d.dragSource instanceof Folder) {
                ((Folder) d.dragSource).onUninstallActivityReturned(false);
            } else if (d.dragSource instanceof Workspace) {
                ((Workspace) d.dragSource).onUninstallActivityReturned(false);
            }
        }
    }

2. 添加新的删除风格

可见操作是在onDrop这类操作之后判断执行的。
通过trace我们确定了添加代码的位置在workspace.java中的startDrag方法中比较合理。

packages/apps/Launcher3/src/com/android/launcher3/Workspace.java
    void startDrag(CellLayout.CellInfo cellInfo) {
        View child = cellInfo.cell;

        // Make sure the drag was started by a long press as opposed to a long click.
        if (!child.isInTouchMode()) {
            return;
        }

        mDragInfo = cellInfo;
        child.setVisibility(INVISIBLE);
        CellLayout layout = (CellLayout) child.getParent().getParent();
        layout.prepareChildForDrag(child);

        child.clearFocus();
        child.setPressed(false);

        final Canvas canvas = new Canvas();

        // The outline is used to visualize where the item will land if dropped
        mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
        beginDragShared(child, this);
        // modify @ + {
        Object dragInfo = cellInfo.cell.getTag();
        if (cellInfo.cell.getTag() instanceof ItemInfo) {
            ItemInfo item = (ItemInfo) dragInfo;
            if (item instanceof ShortcutInfo) {
                ShortcutInfo shortcutInfo = (ShortcutInfo) item;
                if (shortcutInfo.intent != null && shortcutInfo.intent.getComponent() != null) {
                    ComponentName componentName = shortcutInfo.intent.getComponent();
                    if (DeleteDropTarget.willAcceptDrop(child.getTag())) {
                        showDialog(cellInfo.cell, componentName);
                    }
                }
            }
        }
        // modify @ + }
    }

    // modify @ + {
    private PopupWindow popupWindow;
    private TextView tvDelete;
    private void showDialog(View v, final ComponentName componentName) {
        View popView = LayoutInflater.from(getContext()).inflate(R.layout.layout_long_click_dialog, null);
        tvDelete=(TextView) popView.findViewById(R.id.delete_target_tv);
        tvDelete.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (popupWindow.isShowing()) {
                    popupWindow.dismiss();
                }
                int flags = AppInfo.initFlags(
                        ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName()));
                boolean mWaitingForUninstall =
                        mLauncher.startApplicationUninstallActivity(componentName, flags);
            }
        });
        popupWindow = new PopupWindow(popView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setOutsideTouchable(true);
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        if (popupWindow.isShowing()) {
            popupWindow.dismiss();
        }
        int deleteHeight = tvDelete.getHeight() == 0 ? 280 : tvDelete.getHeight();
        int deleteWidth = tvDelete.getWidth() == 0 ? 212 : tvDelete.getWidth();
        popupWindow.showAsDropDown(v, (v.getWidth() - deleteWidth) / 2, v.getHeight() - deleteHeight);
    }
    // modify @ + }

deleteHeight和deleteWidth是在调试很多次之后确定的最佳位置。

新风格需要在长按图标时显示删除选项,同时在拖动图标时,自然就不显示了:

public void onDragOver(DragObject d) {
    // modify @ + {
    if (null != popupWindow && popupWindow.isShowing()) {
        popupWindow.dismiss();
    }
    // modify @ + }
    ...
}

layout文件:

packages/apps/Launcher3/res/layout/layout_long_click_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >

    <TextView
        android:id="@+id/delete_target_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:padding="5dp"
        android:drawableTop="@drawable/ic_launcher_trashcan_normal_holo"
        android:text="@string/delete_target_uninstall_label"
        android:textColor="@android:color/black" />

</LinearLayout>

3. 隐藏原生删除区域

另外,原生删除中有判断是否是系统apk,如果是则不弹删除区域,我们需要此判断,同时不显示原生的删除区域:

packages/apps/Launcher3/src/com/android/launcher3/DeleteDropTarget.java
    @Override
    public void onDragStart(DragSource source, Object info, int dragAction) {
        boolean isVisible = true;
        boolean useUninstallLabel = !LauncherAppState.isDisableAllApps() &&
                isAllAppsApplication(source, info);
        boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();

        // If we are dragging an application from AppsCustomize, only show the control if we can
        // delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
        // Hide the delete target if it is a widget from AppsCustomize.
        if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
            isVisible = false;
        }

        if (useUninstallLabel) {
            setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
        } else if (useDeleteLabel) {
            setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null);
        } else {
            isVisible = false;
        }
        mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();

        mActive = isVisible;
        resetHoverColor();
        ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
        if (isVisible && getText().length() > 0) {
            setText(useUninstallLabel ? R.string.delete_target_uninstall_label
                : R.string.delete_target_label);
        }
    }

通过添加log发现willAcceptDrop判断是否是系统应用,当然,我们始终将isVisible设置为false,则原生的删除区域就不会显示了。

本文地址:https://blog.csdn.net/qq_28534581/article/details/107457415

相关标签: android_launcher3