Mr.Alright---安卓N系统最近任务锁定功能实现

老规矩先看需求:在最近任务上面添加锁定功能,基于安卓7.0的源码,效果图在最后
分析移除任务的操作有哪些?
1.点击“X”按钮
2.点击“全部清除”
3.左右滑动

好啦!知道那里可以移除,在哪里修改就好了
首先你要知道最近任务在哪个包下面,不卖关子,告诉你
在frameworks\base\packages\SystemUI文件夹下,包名是com.android.systemui
上来二话不说先添加一个bool值再说,是为了多版本适配
具体可以参考 Mr.Nubility进阶记——安卓系统开发之多版本适配 这篇文章
接下来要增加锁 我们需要先找到控件所在的地方,下面提供两种方法
1.打开String.xml文件,搜索“全部清除”,然后拿到id,全局搜索看哪里用到的
2.打开AS->tools->Android->Android Device Monitor,点击界面上你要找的按钮,你想要什么自己拿就好了
通过上述两种方法可以找到布局文件recents_task_view_header.xml,在后面添加一个ImageView就好了
至于锁的图片,有美工的找美工要,没有美工的,找我要哈哈,网址如下
阿里巴巴的http://iconfont.cn/?utm_source=androiddevtools&utm_medium=website
布局画好了,开始逻辑吧,一步一步来不着急
根据布局里的ID 我们可以轻松找到TaskViewHeader.java,然后先添加imageview自身的逻辑
为了保证代码的可读性,我们尽量按照源码的方式写,只写关键代码

protected void onFinishInflate() { mFivLock = (FixedSizeImageView) findViewById(R.id.fiv_lock);}

public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
        ...//判断是否显示lock
        if (getResources().getBoolean(com.android.systemui.R.bool.enable_lock_recent_task)) {
            //设置Tag共点击时图标切换使用
            mFivLock.setTag(Prefs.getBoolean(mContext, t.title, false) ? LOCK : LOCK_OFF);
            mFivLock.setVisibility(View.VISIBLE);
            mFivLock.setOnClickListener(this);
        }
    }

public void onTaskDataLoaded() {
        mFivLock.setImageResource(Prefs.getBoolean(mContext, mTask.title, false) ? R.drawable.lock : R.drawable.lock_off);
    }

public void unbindFromTask(boolean touchExplorationEnabled) {
        mTask = null;
        mFivLock.setImageDrawable(null);
        if (touchExplorationEnabled) {
            mFivLock.setClickable(false);
        }
    }

public void onClick(View v) {
        if (v == mIconView) {
            // In accessibility, a single click on the focused app info button will show it
            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
        } else if (v == mDismissButton) {
            TaskView tv = Utilities.findParent(this, TaskView.class);
            if (!Prefs.getBoolean(mContext, mTask.title, false)) {
                    tv.dismissTask();
                    // Keep track of deletions by the dismiss button
                    MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
                            Constants.Metrics.DismissSourceHeaderButton);
                } else {
                    mAnimationHelper.startLockAnimation(tv);
                }
        ..........省略
        } else if (v == mFivLock) {
            if (v.getTag().equals(LOCK_OFF)) {
                mFivLock.setImageResource(R.drawable.lock);
                mFivLock.setTag(LOCK);
                Prefs.putBoolean(mContext, mTask.title, true);
            } else {
                mFivLock.setImageResource(R.drawable.lock_off);
                mFivLock.setTag(LOCK_OFF);
                Prefs.putBoolean(mContext, mTask.title, false);
            }
        }
    }

到此,我们已经可以点击图标切换图片了,只是锁住之后还是可以被清除而已
细心的会发现代码中下面两句代码
Prefs.putBoolean(mContext, mTask.title, false);
Prefs.getBoolean(mContext, t.title, false)
这个就是最最关键的当前Task的状态记录了,判断能不能被清除,就是通过这个判断的
这里我采用的是SharedPreferences的存储方式,其实这是当初的备选方案
开始的时候我是采用 在Task.java中设置set/get属性,然后去判断,但是后面在使用分屏功能后,
Task的状态被清除了,导致不准确,故而采用SP的存储方式
 

1.点击“X”按钮的处理
上面onClick的代码里面其实已经写了的,很简单,只需要加个判断就好了
Prefs.getBoolean(mContext, mTask.title, false)
当不可以清除的时候,toast一下就可以了,这里我是做了一个左右摇晃的动画

public void startLockAnimation(View view) {
        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe(View.TRANSLATION_X,
                    Keyframe.ofFloat(0f, 0f),
                    Keyframe.ofFloat(0.2f, -distance),
                    Keyframe.ofFloat(0.4f, distance),
                    Keyframe.ofFloat(0.6f, -distance),
                    Keyframe.ofFloat(0.8f, distance),
                    Keyframe.ofFloat(1.0f, 0f)
            );
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
        objectAnimator.setDuration(duration);
        objectAnimator.start();
    }

2.点击“全部清除”的处理

public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        ...
        LayoutInflater inflater = LayoutInflater.from(context);
        mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,this, false)
        mStackActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().send(new DismissAllTaskViewsEvent());
            }
        });
        ...
    }
找到了,在RecentsView.java中,点击的时候发了一个消息,仔细一看,我屮艸芔茻,EventBus,可以的,我喜欢
全局搜索DismissAllTaskViewsEvent,你会发现好多地方接收,最终根据逻辑确定TaskStackView.java
public final void onBusEvent(final DismissAllTaskViewsEvent event) {
        ArrayList tasks = new ArrayList<>(mStack.getStackTasks());
        for (int i = tasks.size() - 1; i >= 0; i--) {
            Task task = tasks.get(i);
            if (!Prefs.getBoolean(mContext, task.title, false)) {
            //这里必须设置动画,并且是用手动滑动删除,点击按钮删除的动画Interpolators.FAST_OUT_SLOW_IN
            //不然比如一共10个任务,将第一个,最后一个锁住,这时候点击全部清除的话,中间被删除的还会占着原来的空间
            //加上这个动画后,每次清除完成都会重新排版,两个TaskView会紧挨在一起
                mStack.removeTask(task, new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
                        Interpolators.FAST_OUT_SLOW_IN), false);
                EventBus.getDefault().send(new DeleteTaskDataEvent(task));
            }
        }

        // 为了执行清除时的抖动动画,这里创建了一个用于存储锁住的taskView
        ArrayList lockedViews = new ArrayList<>();
        //getTaskViews()这个方法获得的是你能看到的TaskView 如果任务多了的话就 没显示的不在这里面
        for (TaskView view : getTaskViews()) {
            if (!Prefs.getBoolean(mContext, view.getTask().title, false)) {
                unlockedViews.add(view);
            } else {
                lockedViews.add(view);
            }
        }

        //这个动画放在最后执行,可以保证原本看不到的TaskView,重新回到视线可以看见的时候也会抖动
        mAnimationHelper.startLockAnimation(lockedViews);
    }
删除代码 有兴趣可以自己看一下
public final void onBusEvent(DeleteTaskDataEvent event) {
    // Remove any stored data from the loader
    RecentsTaskLoader loader = Recents.getTaskLoader();
    loader.deleteTaskData(event.task, false);

    // Remove the task from activity manager
    SystemServicesProxy ssp = Recents.getSystemServices();
    ssp.removeTask(event.task.key.id);
}

3.左右滑动的处理
这个看得我比较烦躁,各种事件分发、拦截,但是也并不难处理,打日志就可以了
事件分发拦截,可以参考这篇文章 ************************
最终锁定处理逻辑在,SwipeHelper.java中

public boolean onTouchEvent(MotionEvent ev) {
           ...
            case MotionEvent.ACTION_CANCEL:
                ... {
                    if (isDismissGesture(ev)) {
                        dismissChild(mCurrView, velocity,
                                !swipedFastEnough() /* useAccelerateInterpolator */);
                    } else {
                        mCallback.onDragCancelled(mCurrView);
                        snapChild(mCurrView, 0 /* leftTarget */, velocity);
                    }
                ...
        }
        return true;
    }
判断是不是删除的手势,比如很快,很远等
protected boolean isDismissGesture(MotionEvent ev) {
    boolean falsingDetected = mCallback.isAntiFalsingNeeded();
    if (mFalsingManager.isClassiferEnabled()) {
        falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
    } else {
        falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
    }
    // add by HZH on 2018/1/18 start 
    TaskView taskView = (TaskView) mCurrView;
    boolean isLocked = Prefs.getBoolean(mContext, taskView.getTask().title, false);
    return  !isLocked
            && !falsingDetected
            && (swipedFastEnough() || swipedFarEnough())
            && ev.getActionMasked() == MotionEvent.ACTION_UP
            && mCallback.canChildBeDismissed(mCurrView);
}

到此,功能已经完成,收工!以前弄得,现在可能还有人在用7.0的系统,姑且放出来,下面是效果图

Mr.Alright---安卓N系统最近任务锁定功能实现_第1张图片

你可能感兴趣的:(系统修改,最近任务锁,launcher)