#! /usr/bin/env python
"""
@Author: xiaobaiTser
@email : 807447312@qq.com
@Time  : 2023/6/28 20:41
@File  : MonitorAndroidPackagePowser.py
"""
import os
import re
import tkinter as tk
import subprocess
import threading
import time
from tkinter import filedialog, messagebox

import matplotlib.pyplot as plt
import csv

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


class App:
    def __init__(self, master):
        CUR_PATH = os.path.dirname(os.path.abspath(__file__))
        self.master = master
        master.title("Android设备监控")
        master.geometry("700x500")
        master.iconbitmap(CUR_PATH + "\\..\\data\\favicon.ico")

        self.device_id = None
        self.power_data = []
        self.is_running = False

        # 创建开始、停止、导出按钮
        self.start_button = tk.Button(master, text="开始", command=self.start)
        self.start_button.pack(side="left", padx=10, pady=10)

        self.stop_button = tk.Button(master, text="停止", command=self.stop)
        self.stop_button.pack(side="left", padx=10, pady=10)

        self.export_button = tk.Button(master, text="导出", command=self.export)
        self.export_button.pack(side="left", padx=10, pady=10)

        # 创建tkinter标签，用于显示最新的电量数据
        self.label_power = tk.Label(master, text="设备当前电量：")
        self.label_power.pack(side="bottom", padx=2, pady=2)
        # 创建tkinter标签，用于显示最新的内存数据
        self.label_memory_rate = tk.Label(master, text="内存使用率：")
        self.label_memory_rate.pack(side="bottom", padx=2, pady=2)
        # 创建tkinter标签，用于显示最新的CPU数据
        self.label_cpu_rate = tk.Label(master, text="CPU使用率：")
        self.label_cpu_rate.pack(side="bottom", padx=2, pady=2)
        # 创建tkinter标签，用于显示最新的FPS数据
        self.label_fps_rate = tk.Label(master, text="FPS刷新率：")
        self.label_fps_rate.pack(side="bottom", padx=2, pady=2)

        # 创建matplotlib图形区域
        self.fig = plt.figure(figsize=(6, 4))
        self.ax = self.fig.add_subplot(111)
        self.ax.set_xlabel("Time(s)")
        self.ax.set_ylabel("Rate(%)")
        self.ax.set_xlim(0, 30)
        self.ax.set_ylim(0, 100)

        # 创建tkinter绘图区域
        self.canvas = FigureCanvasTkAgg(self.fig, master=master)
        self.canvas.get_tk_widget().pack(side="top", fill="both", expand=1)

    def start(self):
        self.start_time = time.time()
        if not self.is_running:
            self.is_running = True
            self.device_id = (
                subprocess.check_output(["adb", "devices"])
                .decode("utf-8")
                .split("\n")[1]
                .split("\t")[0]
            )
            self.total_memory = (
                subprocess.check_output(
                    ["adb", "shell", "cat", "/proc/meminfo", "|", "grep", '"MemTotal"']
                )
                .decode("utf-8")
                .split()[1]
            )
            self.power_data = []
            self.app_memory_data = []
            self.app_cpu_data = []
            self.app_fps_data = []

            # 在后台启动一个线程来获取数据
            self.power_thread = threading.Thread(target=self.get_data)
            self.power_thread.start()

    def stop(self):
        self.is_running = False

    def export(self):
        # 弹出文件对话框，获取用户选择的文件名和存储位置
        file_path = filedialog.asksaveasfilename(
            defaultextension=".csv", filetypes=(("CSV文件", "*.csv"),)
        )
        if file_path:
            try:
                # 合并数据
                merged_list = [
                    (a[0], a[1], b[0], b[1], c, d)
                    for a, b, c, d in zip(
                        self.power_data,
                        self.app_memory_data,
                        self.app_cpu_data,
                        self.app_fps_data,
                    )
                ]
                # 导出电量数据到CSV文件
                with open(file_path, "w", newline="") as f:
                    writer = csv.writer(f)
                    writer.writerows(
                        [
                            (
                                "运行时间（秒）",
                                "设备电量（%）",
                                "应用程序包名",
                                "内存使用率（%）",
                                "CPU使用率（%）",
                                "FPS（帧/秒）",
                            )
                        ]
                    )
                    writer.writerows(merged_list)
                messagebox.showinfo("小白提醒：", "数据已经保存完成！")
            except PermissionError:
                messagebox.showerror("小白提示：", "文件无法访问！文件是否打开？请先关闭后重新尝试！")

    def get_data(self):
        while self.is_running:
            # 电量
            power_output = (
                subprocess.check_output(
                    [
                        "adb",
                        "-s",
                        self.device_id,
                        "shell",
                        "dumpsys",
                        "battery",
                        "|",
                        "grep",
                        "level",
                    ]
                )
                .decode("utf-8")
                .strip()
            )
            power_level = int(power_output.split(":")[1])
            self.power_data.append((int(time.time() - self.start_time), power_level))

            package_name = subprocess.check_output(
                ["adb", "shell", "dumpsys", "window", "|", "grep", "mCurrentFocus"]
            ).decode("utf-8")
            if "mCurrentFocus=null" in package_name:
                status = messagebox.askyesno(
                    "小白提示：", "存在熄屏或者切换APP的行为！获取数据期间尽量不要切换或者熄屏！"
                )
                if status:
                    try:
                        subprocess.check_output(
                            [
                                "adb",
                                "shell",
                                "settings",
                                "put",
                                "system",
                                "screen_off_timeout",
                                "-1",
                            ]
                        )
                    except Exception:
                        messagebox.showerror("小白错误提示：", "设置失败！")
                else:
                    exit(0)
            else:
                package_name = package_name.split()[2].split("/")[0]
                package_pid = subprocess.check_output(
                    ["adb", "shell", "ps", "|", "grep", package_name]
                ).decode("utf-8")
                app_package_pid = ""
                for pid in package_pid.split("\r\n"):
                    if package_name in pid.split():
                        app_package_pid = pid.split()[1]
                        break

                # 内存
                result = subprocess.run(
                    ["adb", "shell", "dumpsys", "meminfo", app_package_pid],
                    capture_output=True,
                    text=True,
                )
                memory_output = result.stdout
                # 解析输出，获取当前打开的APP的内存使用情况
                lines = memory_output.split("\n")
                for line in lines:
                    if "TOTAL" in line:
                        app_memory_usage = line.split()[1]
                        self.app_memory_data.append(
                            (
                                package_name,
                                round(
                                    int(app_memory_usage)
                                    * 100
                                    / int(self.total_memory),
                                    1,
                                ),
                            )
                        )
                        break

                # CPU
                output = subprocess.check_output(
                    ["adb", "shell", "top", "-n", "1", "-d", "1", "-p", app_package_pid]
                ).decode("utf-8")
                lines = output.strip().split("\n")
                if len(lines) >= 2:
                    # 提取CPU使用率数据
                    cpu_line = lines[-1]
                    try:
                        self.app_cpu_data.append(float(cpu_line.split()[8]))
                    except Exception as e:
                        self.app_cpu_data.append(0.0)

                # 获取FPS数据
                output = subprocess.check_output(
                    ["adb", "shell", "dumpsys", "gfxinfo", package_name, "framestats"]
                ).decode("utf-8")
                lines = output.strip().split("\r\n")
                janky_frames_line = (
                    [x for x in lines if "Janky frames:" in x][0]
                    if len([x for x in lines if "Janky frames:" in x]) > 0
                    else "(0.0%)"
                )
                janky_frames = float(re.findall("\((.*?)%\)", janky_frames_line)[0])
                self.app_fps_data.append(int(60 * (1 - janky_frames / 100)))

                # 其他数据...

                self.update_data()
                time.sleep(1)

    def update_data(self):
        # 更新图表数据和电量标签
        if self.power_data:
            self.ax.clear()
            self.ax.set_xlabel("Time(s)")
            self.ax.set_ylabel("Rate(%)")
            if self.power_data[-1][0] > 30:
                self.ax.set_xlim(
                    max(0, self.power_data[-1][0] - 30), self.power_data[-1][0] + 1
                )
            else:
                self.ax.set_xlim(0, 30)
            self.ax.set_ylim(0, 100)

            if len(self.ax.lines) > 0:
                self.ax.lines.pop(0)
            if self.power_data[-1][0] > 30:
                self.ax.plot(
                    [t for t, _ in self.power_data[-30:]],
                    [p for _, p in self.power_data[-30:]],
                )
                self.ax.plot(
                    [t for t, _ in self.power_data[-30:]],
                    [p for _, p in self.app_memory_data[-30:]],
                )
                self.ax.plot(
                    [t for t, _ in self.power_data[-30:]],
                    [p for p in self.app_cpu_data[-30:]],
                )
                self.ax.plot(
                    [t for t, _ in self.power_data[-30:]],
                    [p for p in self.app_fps_data[-30:]],
                )
            else:
                self.ax.plot(
                    [t for t, _ in self.power_data], [p for _, p in self.power_data]
                )
                self.ax.plot(
                    [t for t, _ in self.power_data],
                    [p for _, p in self.app_memory_data],
                )
                self.ax.plot(
                    [t for t, _ in self.power_data], [p for p in self.app_cpu_data]
                )
                self.ax.plot(
                    [t for t, _ in self.power_data], [p for p in self.app_fps_data]
                )
            self.canvas.draw()
            # 更新电量标签
            current_power = self.power_data[-1][1]
            current_memory_rate = self.app_memory_data[-1][1]
            current_cpu_rate = self.app_cpu_data[-1]
            current_fps_rate = self.app_fps_data[-1]
            self.label_power.config(text=f"设备当前电量[蓝线]：{current_power}%")
            self.label_memory_rate.config(text=f"内存使用率[橙线]：{current_memory_rate}%")
            self.label_cpu_rate.config(text=f"CPU使用率[绿线]：{current_cpu_rate}%")
            self.label_fps_rate.config(text=f"FPS刷新率[红线]：{current_fps_rate}/s")


def power():
    root = tk.Tk()
    app = App(root)
    root.mainloop()


# if __name__ == '__main__':
#     power()
