The realization of Android long picture and text screenshots (support to intercept third-party apps such as Weibo, Zhihu, Toutiao, etc.)

Source code: https://github.com/zengfw/LongScreenShot

Support interception of Weibo, Zhihu, Toutiao and other third-party apps...

Take a look at the effect diagram first:
renderings

Take a look at the final long screenshot:

I'm taking a long screenshot, take a look...

I am the second long screenshot, take a look again...

Last week, the function of long screenshots suddenly popped up in my mind, thinking about how to capture the interface of third-party apps such as Weibo, Zhihu, Toutiao, etc.? Out of curiosity, I spent a week in my spare time to take a look.

Isn't it just screenshots + puzzles, what else can be difficult? this. . . It seems it is.

Question:

1. How to take a screenshot?

Before Android 5.0 API 21, if you want to take a screenshot of the system, you need root, but Android 5.0 began to open the responsive screenshot interface:

MediaProjection (added in API level 21.)
- A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities
granted depend on the type of MediaProjection.

2. How to take elegant screenshots?

The floating window is so small, do I have to slide a certain distance every time, and then click the floating window once? It is theoretically possible, but the experience is not good. It is estimated that more people tend to take screenshots by simply touching the screen, so they choose to monitor the touch screen events outside the floating window.

3. How to monitor the TouchEvent outside the floating window?

The touch screen events outside the floating window have been separated from the entire application. How to monitor it? It's really stuck here, because I really can't find how to capture this event. I feel that this problem is also the most annoying one. Later, I got some inspiration. I set a full-screen transparent background, and then set the onTouch event for this background. ,Oh! ! ! Suddenly, I thought this would work? wrong! ! In this way, the events of the entire mobile phone will be intercepted by this transparent background and cannot be transmitted to the mobile phone desktop, if the software is installed by non-developers. . , tell him to reboot. . . So I flipped through the source code of WindowManager, saw the flag parameter, read the comments of various flag parameters, and finally located the following flag parameter values.


        /** Window flag: this window won't ever get key input focus, so the
         * user can not send key or other button events to it.  Those will
         * instead go to whatever focusable window is behind it.  This flag
         * will also enable {@link #FLAG_NOT_TOUCH_MODAL} whether or not that
         * is explicitly set.
         *
         * <p>Setting this flag also implies that the window will not need to
         * interact with
         * a soft input method, so it will be Z-ordered and positioned
         * independently of any active input method (typically this means it
         * gets Z-ordered on top of the input method, so it can use the full
         * screen for its content and cover the input method if needed.  You
         * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */
        public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;

        /** Window flag: this window can never receive touch events. */
        public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;

        /** Window flag: even when this window is focusable (its
         * {@link #FLAG_NOT_FOCUSABLE} is not set), allow any pointer events
         * outside of the window to be sent to the windows behind it.  Otherwise
         * it will consume all pointer events itself, regardless of whether they
         * are inside of the window. */
        public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
        /** Window flag: if you have set {@link #FLAG_NOT_TOUCH_MODAL}, you
         * can set this flag to receive a single special MotionEvent with
         * the action
         * {@link MotionEvent#ACTION_OUTSIDE MotionEvent.ACTION_OUTSIDE} for
         * touches that occur outside of your window.  Note that you will not
         * receive the full down/move/up gesture, only the location of the
         * first down as an ACTION_OUTSIDE.
         */
        public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;

In an environment with a full-screen transparent background, I thought that I could monitor the Down, Move, and Up events of the desktop, but all the events that occurred were intercepted and died on this transparent background and could not be transmitted to the mobile phone desktop, and then I found a combination of these parameters to summarize this idea Not advisable.

Looking at the comments, you can know that setting FLAG_WATCH_OUTSIDE_TOUCH can receive a specified event MotionEvent#ACTION_OUTSIDE outside the window (ie outside the App) , but at the same time, you will not be able to receive any Down, Move, Up events. So, that's all it can do. . There are other brilliant brothers under the guidance of Kazakhstan.

4. How to control the frequency of screenshots?

On the basis of step 3, it is basically possible to make a screenshot strategy. For example, take a screenshot every time ACTION_OUTSIDE is received, or, every time ACTION_OUTSIDE is monitored, a counter is accumulated. Set the threshold to 2 like this.

5. How to make a puzzle?

This varies from person to person, but the purpose is the same. Compare the pictures taken in the above steps to compare different places, and then stitch the different places together. For the sake of operational efficiency, here I use JNI to implement the main function:

JNIEXPORT void JNICALL Java_com_zfw_screenshot_utils_SewUtils_merge(
        JNIEnv *env, jobject thiz, jobject bmp0, jobject bmp1, jobject bmp2, int h0, int h1, int h2, int samePart, int len) {

    int *pixels_0 = lockPixel(env, bmp0);
    int *pixels_1 = lockPixel(env, bmp1);
    int *pixels_2 = lockPixel(env, bmp2);
    /* -------------------- merge the difference ----------------------- */
    int index = 0;
    while(index < h0) {
        if(index < h1) {
            getRowPixels(pixels_0, index, pixels_1, index, len);
        } else {
            getRowPixels(pixels_0, index, pixels_2, index - h1 + samePart, len);
        }
        index++;
    }
    /* -------------------- merge the difference ----------------------- */
    unlockPixel(env, bmp0);
    unlockPixel(env, bmp1);
    unlockPixel(env, bmp2);
}

There is no difficulty in implementing the function. I feel that it is more about choosing a strategy for implementation, such as how to elegantly monitor the Touch event outside the floating window, how to elegantly implement a "fixed-point" screenshot strategy, and how to elegantly compare the difference between two Bitmaps place for stitching.

Source code portal: https://github.com/zengfw/LongScreenShot

If you have any good strategies and questions, please leave a message and discuss together to see if there is a more elegant implementation!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325538396&siteId=291194637