Android13 überwacht Klickereignisse im gesamten leeren, nicht funktionsfähigen Bereich des Bildschirms, eine effektive Methode. Klicken, Berühren, globaler Klick, Klick auf den gesamten Bildschirm, Klick außerhalb der Ansicht, OutSide.

Vorher geschrieben: Ich habe diesen Artikel geschrieben, weil ich in meinem Unternehmen auf ein Bedürfnis gestoßen bin, das darin besteht, dass ich auf einen leeren, nicht funktionierenden Teil des Bildschirms klicken muss, um die Anzeige der Ansicht zu steuern, und sie regelmäßig ausblenden muss, wenn dies nicht der Fall ist anschließende Operation. Nach vielen Drehungen und Wendungen habe ich endlich eine zuverlässige Lösung gefunden, die es wert ist, aufgezeichnet zu werden.

  Zunächst müssen wir klären, was „ der leere, nicht funktionale Bereich des gesamten Bildschirms “ ist: Es handelt sich um die Stelle auf dem Bildschirm, an der nach dem Klicken keine Reaktion erfolgt.
  Machen Sie zur Erklärung zwei Bilder. Im ersten Bild des Launcher-Desktops ist der nicht funktionale Bereich der Teil, den ich rot eingekreist habe.
Fügen Sie hier eine Bildbeschreibung ein
Öffnen Sie im zweiten Bild nach Belieben eine APP, z. B. Google:
Fügen Sie hier eine Bildbeschreibung ein
  Die aktuelle Anforderung besteht darin, darauf zu klicken den gesamten Bildschirm, unabhängig davon, ob es sich um einen Launcher oder eine App handelt. In jeder Aktivitätsoberfläche muss die angegebene Ansicht das Klickereignis in einem leeren und funktionalen Bereich abhören und dann in regelmäßigen Abständen die Funktion ausführen, sich selbst anzuzeigen und auszublenden. Es ist etwas verwirrend zu lesen, aber Sie können die Anforderungen verstehen, wenn Sie es sorgfältig lesen.
  Um die Geschäftsanforderungen zu realisieren, habe ich das gesamte Netzwerk durchsucht und drei relativ ähnliche Methoden ausprobiert:

1. View überwacht OutSide-Ereignisse:

  View kann Klickereignisse außerhalb von OutSide abhören. Diese Methode kann in „durch addView hinzugefügtes schwebendes Fenster“, Dialog und Aktivität implementiert werden. Sie wird häufig verwendet, um die Funktion des Klickens außerhalb der Ansicht und des Schließens der Ansicht zu realisieren. OutSide reagiert jedoch unabhängig davon, ob Sie auf eine funktionale Stelle außerhalb der Ansicht oder auf eine leere Stelle ohne Funktionen klicken, und kann sich nicht an Geschäftsanforderungen anpassen.
Fügen Sie hier eine Bildbeschreibung ein
  Die Implementierungsmethode ist ebenfalls einfach: Nehmen Sie als Beispiel das schwebende AddView-Fenster und führen Sie Folgendes direkt aus:

第一步:LayoutParams中声明WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

p = new WindowManager.LayoutParams();
p.format = PixelFormat.RGBA_8888;
p.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
		  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
		  WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;

第二步:在View的onTouch中进行事件处理
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                Log.d("floatview_circle setOnTouchListener", String.valueOf(action));

                if (action == MotionEvent.ACTION_OUTSIDE) {
                    Message msg = MySystemUI.timeHandler.obtainMessage(TIME);
                    MySystemUI.timeHandler.sendMessageDelayed(msg, 5000);
                    Log.d("floatview_circle", "收到空白处的点击");
                    circle.setVisibility(View.VISIBLE);
                }
                return false;
            }
        });

注:上述代码不能直接拿来用,只是完整代码的一小部分,用来说明OutSide监听的实现逻辑。

2. Der Barrierefreiheitsdienst überwacht globale Gesten

  Diese Methode besteht darin, den Erkundungsmodus des Barrierefreiheitsdienstes zu verwenden, um auf eine bestimmte Rückrufmethode zu warten, aber ich habe es ohne Erfolg versucht. Durch Klicken auf eine beliebige Stelle auf dem Bildschirm wird kein Rückruf empfangen. Die Implementierungsmethode ist wie folgt:

第一步:xml过滤里面填上需要的过滤类型 flagRequestTouchExplorationMode 和android:canRequestTouchExplorationMode="true"必须成对出现才行。
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagRequestFilterKeyEvents|flagRequestTouchExplorationMode|flagRequestFingerprintGestures|flagSendMotionEvents"
    android:canRetrieveWindowContent="true"
    android:canRequestFilterKeyEvents="true"
    android:canRequestTouchExplorationMode="true"
    android:canRequestFingerprintGestures="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:description="@string/app_name">
</accessibility-service>

第二步:在无障碍服务的onGesture回调方法里面处理全局手势。
    @Override
    public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) {
        Log.d("onGesture", "监听全局手势");
        //添加自己想要的逻辑
        return super.onGesture(gestureEvent);
    }

Ich habe diese Methode ausprobiert, aber ich weiß nicht, ob es daran liegt, dass Google sie nicht mehr unterstützt oder was, aber sie ist nutzlos und ich kann keine Rückrufe erhalten, also habe ich diese Methode aufgegeben.

3. Stellen Sie die Zeigerdienstschnittstelle von WindowManagerService für APP zum Aufrufen bereit

  Ich weiß nicht, ob einer von euch Lesern jemals die Funktion „Zeigerposition“ im „Entwicklermodus“ seines Android-Telefons verwendet hat. Beim Öffnen sieht es so aus:
Fügen Sie hier eine Bildbeschreibung ein

  Jeder Punkt, an dem Sie mit dem Finger auf das Telefon klicken, wird markiert, und jeder Track, den Sie verschieben, wird ebenfalls markiert. Die von dieser Funktion aufgerufene Methode ist die Zeigerdienstmethode in WindowManagerService. Vorhandene Artikel im Internet basieren auf Zeigerdiensten. Es gibt ungefähr zwei Ideen: 1. Erstellen Sie direkt eine Instanz, um den Code im Framework aufzurufen. 2. Stellen Sie die Methode dem SDK zur Verfügung, damit die APP sie aufrufen kann, indem sie den windowManager abruft Service oder über den AIDL-Schnittstellenaufruf. Ähnliche Artikel sind wie folgt:

https://blog.csdn.net/sinat_33585352/article/details/114012871
https://blog.csdn.net/wenzhi20102321/article/details/120169226

  Die Android-Version dieser Artikel ist relativ alt und das Schreiben ist nicht detailliert genug. Bei der tatsächlichen Verwendung werden beim Kompilieren verschiedene Fehler gemeldet, sie sind nicht vollständig geschrieben und die Lesbarkeit und Benutzerfreundlichkeit sind relativ schlecht. Aber die bereitgestellte Idee ist richtig und die Methoden, die offengelegt werden müssen, sind wie folgt:
Fügen Sie hier eine Bildbeschreibung ein

Hinweis: Um den neuesten Android-Quellcode zu lesen, empfehlen wir die Online-Website: http://aospxref.com/

  Ich habe versucht, ihre Methode zu verwenden, um diese beiden Methoden verfügbar zu machen, aber die Kompilierung ist fehlgeschlagen. Das Android-System von Google verfügt über eine sehr ekelhafte Funktion: Während der ursprüngliche Inhalt und die ursprünglichen Funktionen unverändert bleiben, ist es relativ einfach, Funktionen zum Android-System hinzuzufügen. Wenn Sie beispielsweise den Code eines bestimmten Frameworks direkt ändern, um die Funktion zu implementieren, wird die Kompilierung fast immer erfolgreich sein . Wenn Sie jedoch einige der nativen Einschränkungen ändern möchten, z. B. SeLinux-Regeln, im SDK enthaltene Schnittstellen, Android.bp-Kompilierung neuer Module usw., ist dies sehr schwierig. Es gibt viele Stellen, die geändert werden müssen. Viele Manchmal explodiert einem der Kopf, nachdem man es getan hat. Es ist unmöglich, es herauszufinden. Ohne die Anleitung eines Meisters, der es versteht, ist es fast eine Sackgasse. Einige Pakete unter unterschiedlichen Paketnamen können nicht einander vorgestellt werden, so wie es der Fall ist „reproduktive Isolation“. Um es ganz klar auszudrücken: Wie hat Google sie entwickelt, wenn Sie nicht ein halbes Jahr damit verbracht haben, die Android-Systemebene „effektiv zu erlernen“? Wie kann die Schnittstelle der APP der oberen Ebene zugänglich gemacht werden? Wie verbinde ich den zugrunde liegenden C++-Code? Selbststudium, das auf blinden Spekulationen basiert und 99 % der wertlosen Artikel im Internet liest, ist grundsätzlich nutzlos. Ohne systematisches und effektives Lernen gehen alle Bemühungen in die falsche Richtung und alle Bemühungen sind Unsinn. Diese Fähigkeiten machen den Unterschied zwischen erfahrenen Android-Ingenieuren und gewöhnlichen Android-APP-Ingenieuren aus. Wenn Sie diese Entwicklungsfähigkeiten erlernen möchten, werden Sie keine Informationen darüber finden. Dies sind Dinge, die von den Kernsystementwicklungsingenieuren großer Hersteller beherrscht werden. Wenn die Informationen verfügbar sind, werden sie nicht an andere weitergegeben. Dies ist der Typ, der seinen Lebensunterhalt verdient. Wie können wir das lernen? Dies beginnt bei der Aufnahmeprüfung für ein entferntes College. Wenn Sie eine berühmte Schule besuchen -> einem berühmten Unternehmen beitreten -> können Sie leicht auf diese Dinge zugreifen, mit halbem Aufwand lernen und auch Kerningenieure um Rat fragen - > neuer Kerningenieur einer großen Fabrik werden ——> Geben Sie es an den nächsten jungen Mann von einer berühmten Schule weiter. Sobald Sie die Gesellschaft verlassen, werden Sie feststellen, dass reproduktive Isolation nicht nur im Studium, sondern in allen Lebensbereichen zu finden ist. Lassen Sie uns abschweifen und zum Thema zurückkehren.
  Leider war mir auch diese Lücke versperrt, ich konnte keine verlässlichen Lernmaterialien finden und konnte es nicht richtig hinbekommen, selbst wenn ich lernen wollte. Dann seien Sie dumm und gehen Sie den einzigen Weg, der funktioniert. Werfen wir einen Blick auf den zugehörigen Inhalt des Zeigerdienstes in WindowManagerService:

    @Override
    public void registerPointerEventListener(PointerEventListener listener, int displayId) {
    
    
        synchronized (mGlobalLock) {
    
    
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
    
    
                displayContent.registerPointerEventListener(listener);
            }
        }
    }

    @Override
    public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
    
    
        synchronized (mGlobalLock) {
    
    
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
    
    
                displayContent.unregisterPointerEventListener(listener);
            }
        }
    }

    private static class MousePositionTracker implements PointerEventListener {
    
    
        private boolean mLatestEventWasMouse;
        private float mLatestMouseX;
        private float mLatestMouseY;

        /**
         * The display that the pointer (mouse cursor) is currently shown on. This is updated
         * directly by InputManagerService when the pointer display changes.
         */
        private int mPointerDisplayId = INVALID_DISPLAY;
        
        /**
         * Update the mouse cursor position as a result of a mouse movement.
         * @return true if the position was successfully updated, false otherwise.
         */
        boolean updatePosition(int displayId, float x, float y) {
    
    
            synchronized (this) {
    
    
                mLatestEventWasMouse = true;

                if (displayId != mPointerDisplayId) {
    
    
                    // The display of the position update does not match the display on which the
                    // mouse pointer is shown, so do not update the position.
                    return false;
                }
                mLatestMouseX = x;
                mLatestMouseY = y;
                return true;
            }
        }

        void setPointerDisplayId(int displayId) {
    
    
            synchronized (this) {
    
    
                mPointerDisplayId = displayId;
            }
        }

        @Override
        public void onPointerEvent(MotionEvent motionEvent) {
    
    
            if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
    
    
                updatePosition(motionEvent.getDisplayId(), motionEvent.getRawX(),
                        motionEvent.getRawY());
            } else {
    
    
                synchronized (this) {
    
    
                    mLatestEventWasMouse = false;
                }
            }
        }
    };

  Beachten Sie, dass es hier eine onPointerEvent-Methode gibt, die das gewünschte Bewegungsereignis enthält. Könnte dies das Bewegungsereignis sein, das jedes Mal hochgeladen wird, wenn ich global klicke? Ich habe zuerst ein Protokoll hinzugefügt und es ausgedruckt, und tatsächlich war es das auch! Der nächste Schritt besteht darin, zu überlegen, wie die APP jedes Mal benachrichtigt werden kann, wenn darauf geklickt wird. Darüber hinaus funktioniert diese Methode, wenn Sie auf eine beliebige Stelle auf dem Bildschirm klicken. Wie filtert man Klicks auf leere und nicht funktionale Bereiche heraus?
  Das obige Problem hat mich schon lange beschäftigt. Zufälligerweise habe ich kürzlich auch an der KeyCode-Fernbedienungs-App für den Barrierefreiheitsdienst gearbeitet. Eines Tages sah ich plötzlich diese Methode des Barrierefreiheitsdienstes: Wenn wir auf
Fügen Sie hier eine Bildbeschreibung ein
  die Schaltfläche „Ansicht“ klicken, ziehen Der Fortschrittsbalken und die Switch-Aktivitätsschnittstelle. Beim Schließen und Öffnen der APP können hier verschiedene Arten von Ereignistypen empfangen werden. Ich habe damals darüber nachgedacht, ob ich WindowManagerService und die Filterung des Barrierefreiheitsdienstes kombinieren könnte, um Klickereignisse in leeren und nicht funktionsfähigen Fällen zu überwachen Bereiche?
  Nach langer Zeit des Herumwerfens und Debuggens wurde es endlich geschafft. Die Genauigkeit der Überwachung leerer, nicht funktionierender Bereiche beträgt 99 %, was drahtlos nahezu 100 % entspricht. Wenn nichts anderes passiert, liegt sie bei 100 %.

Der endgültige Plan und die wirksamen Änderungen sind unten aufgeführt:

1. XML-Konfigurationsdatei für die Filterung des Barrierefreiheitsdiensts

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagRequestFilterKeyEvents|flagRequestTouchExplorationMode|flagRequestFingerprintGestures|flagSendMotionEvents"
    android:canRetrieveWindowContent="true"
    android:canRequestFilterKeyEvents="true"
    android:canRequestTouchExplorationMode="true"
    android:canRequestFingerprintGestures="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:description="@string/app_name">
</accessibility-service>

2. WindowManagerService-Änderung

Frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

+import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityEvent;

/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    
    
    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
    private static final int TRACE_MAX_SECTION_NAME_LENGTH = 127;

    static final int LAYOUT_REPEAT_THRESHOLD = 4;

    +static AccessibilityManager accessibilityManager;
    
    . . . . . .
    . . . . . .

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, DisplayWindowSettingsProvider
            displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
    
    
        installLock(this, INDEX_WINDOW);
        mGlobalLock = atm.getGlobalLock();
        mAtmService = atm;
        mContext = context;

        +accessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
        
    . . . . . .
    . . . . . .

    private static class MousePositionTracker implements PointerEventListener {
    
    
        private boolean mLatestEventWasMouse;
        private float mLatestMouseX;
        private float mLatestMouseY;

        /**
         * The display that the pointer (mouse cursor) is currently shown on. This is updated
         * directly by InputManagerService when the pointer display changes.
         */
        private int mPointerDisplayId = INVALID_DISPLAY;

        +int action;

        +float downX ,downY;

        +float upX ,upY;

        +AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.WINDOWS_CHANGE_PIP);

        /**
         * Update the mouse cursor position as a result of a mouse movement.
         * @return true if the position was successfully updated, false otherwise.
         */
        boolean updatePosition(int displayId, float x, float y) {
    
    
            synchronized (this) {
    
    
                mLatestEventWasMouse = true;

                if (displayId != mPointerDisplayId) {
    
    
                    // The display of the position update does not match the display on which the
                    // mouse pointer is shown, so do not update the position.
                    return false;
                }
                mLatestMouseX = x;
                mLatestMouseY = y;
                return true;
            }
        }

        void setPointerDisplayId(int displayId) {
    
    
            synchronized (this) {
    
    
                mPointerDisplayId = displayId;
            }
        }

        @Override
        public void onPointerEvent(MotionEvent motionEvent) {
    
    

            +action = motionEvent.getAction();

            +if(action == MotionEvent.ACTION_DOWN){
    
    
                +downX = motionEvent.getRawX();
                +downY = motionEvent.getRawY();

                +Log.d("onPointerEvent ACTION_DOWN",String.valueOf(downX)+ " "+String.valueOf(downY));
            +}

            +if(action == MotionEvent.ACTION_UP){
    
    
                +Log.d("onPointerEvent","xu 全局触控事件");

                +upX = motionEvent.getRawX();
                +upY = motionEvent.getRawY();

                +Log.d("onPointerEvent ACTION_UP",String.valueOf(upX)+ " "+String.valueOf(upY));

                +if(downX == upX && downY == upY) {
    
    //点击事件才模拟触发无障碍服务,上传event给无障碍服务过滤,Move移动事件不处理。
                    +event.getText().add("ClickBlandUp"); // 自定义标识符
                    +accessibilityManager.sendAccessibilityEvent(event);
                +}
            +}

            if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
    
    
                updatePosition(motionEvent.getDisplayId(), motionEvent.getRawX(),
                        motionEvent.getRawY());
            } else {
    
    
                synchronized (this) {
    
    
                    mLatestEventWasMouse = false;
                }
            }
        }
    };

注:前面有加号的内容为新增内容。

3. Filtern Sie im Barrierefreiheitsdienst simulierte hochgeladene Ereignisse.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MenuService extends AccessibilityService {
    
    
    
    List<Integer> clickList = new ArrayList<>();

    //线程池
    ExecutorService executor = Executors.newSingleThreadExecutor();

    int size;

    AccessibilityEvent myevent;
    
    . . . . . .
    . . . . . .

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    
    

        myevent = event;

        Log.d("onAccessibilityEvent allTypes", "检测到"+String.valueOf(event.getEventType()));
        //判断事件是否被消费
        int eventType = event.getEventType();

        if(eventType == AccessibilityEvent.WINDOWS_CHANGE_PIP && event.getText().contains("ClickBlandUp")){
    
    
            Log.d("onAccessibilityEvent WINDOWS_CHANGE_PIP", "全局点击");
            clickList.add(eventType);

            execut();
        }

        if (eventType == AccessibilityEvent.TYPE_VIEW_CLICKED) {
    
    //WINDOWS_CHANGE_PIP
            // 处理点击事件
            clickList.add(eventType);

            Log.d("onAccessibilityEvent TYPE_VIEW_CLICKED", "检测到点击功能按键");
        }

        if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
    
    
            // 处理点击事件
            clickList.add(eventType);
            Log.d("onAccessibilityEvent TYPE_WINDOW_STATE_CHANGED", "检测到窗口发生变化");

        }

        if(eventType == AccessibilityEvent.TYPE_WINDOWS_CHANGED){
    
    
            clickList.add(eventType);
        }

        if(eventType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED){
    
    
            clickList.add(eventType);
        }

    }

    public void execut() {
    
    
        executor.submit(()->{
    
    
            try {
    
    
                Thread.sleep(200);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            size = clickList.size();
            Log.d("onAccessibilityEvent execut"," "+String.valueOf(clickList.get(size-1)));

            if(clickList.get(size-1) == AccessibilityEvent.WINDOWS_CHANGE_PIP){
    
    
                //点击屏幕任意一点,最后只有空白无功能处的点击,经过过滤会走到这个判断里面,
                //在这里可以自己实现监听到空白无功能处点击之后的逻辑,加在这里。
                //Settings.System.putInt(mycontext.getContentResolver(), WINDOW_MANAGER_TO_OSD, 1);
            }
        });
    }

}

注:上述无障碍的代码,可以照搬。

  OK, hier wurde die Funktion „Überwachung von Klickereignissen auf leeren und nicht funktionierenden Teilen des gesamten Bildschirms“ implementiert. Ich werde die Implementierungslogik des oben geänderten Codes zusammenfassen, um das Verständnis der Leser zu erleichtern: In WindowManagerService, im onPointerEvent
  ( MotionEvent (motionEvent)-Methode (durch DOWN, Die absoluten Koordinaten von UP schirmen das MOVE-Ereignis ab, um sicherzustellen, dass nur Klickereignisse simuliert werden.) Wenn ein Klickereignis empfangen wird, löst die Simulation den Rückruf des Barrierefreiheitsdienstes aus. ——————> Das onAccessibilityEvent (AccessibilityEvent-Ereignis) des Barrierefreiheitsdienstes führt den Rückruf aus, verwendet eventType, um den spezifischen Typ zum Eintragen in die Liste zu erfüllen, und fügt dem Thread-Pool eine Verzögerung von 200 Millisekunden hinzu, bevor Thread.sleep beurteilt wird (200), Filtern des Klickereignisses und schließlich Nur „Der gesamte Bildschirm ist leer und hat kein Funktionsklickereignis“ kann die Anweisung if(clickList.get(size-1) == AccessibilityEvent.WINDOWS_CHANGE_PIP) eingeben, und das können wir auch Implementieren Sie unsere eigene Codelogik in dieser if-Anweisung.

Am Ende geschrieben: Ich habe immer an einen Satz geglaubt -> Technische Artikel dienen nicht dazu, den Lesern zu zeigen, wie großartig Ihre Technologie ist oder wie tief Ihr Verständnis ist, sondern um Probleme zu lösen.

Wert == Sie können es verwenden, sobald Sie es erhalten, und es ist effektiv, wenn Sie es verwenden.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43522377/article/details/134729374
conseillé
Classement