1. 程式人生 > >python3:多程序複製檔案至手機

python3:多程序複製檔案至手機

在最近的工作中又再次用到這個py指令碼,但由於機器太多,有更多的需求,故多了一些其它的需求:
1. 要求能自動檢測usb的連線情況
2. 多程序copy.
3. 在copy之前要先刪除一些檔案
4. copy完後要自動打檔案管理器進行升級,這個動作有好幾個點:
   a. 在有些操作後手機usb會自動斷開,故要求檢測usb是否斷開並自動重連後再繼續操作
   b. 判斷手動是否在亮屏狀態。
5. 只要有手機正在升級時,PC端自動響‘嘟嘟’聲. 

主要涉及的點就是以下幾點,不再單獨作說明,直接上code吧:

#!/user/bin/python
# -*- coding:utf-8 -*-

import os
from multiprocessing import Pool
from time import sleep
import winsound

# 重連動作
def reconnectAction(deviceid):
    devlist = getdevlist()
    print(f'裝置{deviceid}正在嘗試重連.')
    id = 1
    while deviceid not in devlist:
        print(f'第{id}次 ', end = ' ')
        devlist = getdevlist()
        id = id + 1
    print(f'\n裝置{deviceid}重新建立連線成功.')
    sleep(1)

# 執行普通的cmd命令
def exeCmd(cmdInfo, deviceid):
    cmd = 'adb -s ' + deviceid + ' shell ' + cmdInfo[0]
    print(f'裝置{deviceid}:exeCmd():{cmdInfo[1]}.')
    if os.system(cmd) != 0:
        return False
    else:
        return True

# 按鍵動作
def pressKeyevent(keycodeInfo, deviceid = ''):
    cmd = 'adb -s ' + deviceid + ' shell input keyevent ' + keycodeInfo[0]
    print(f'裝置{deviceid}:pressKeyevent():{keycodeInfo[1]}.')
    if os.system(cmd) != 0:
        return False
    else:
        return True
    
# 點選螢幕的動作
def clickScreen(positionInfo, deviceid = ''):
    cmd = 'adb -s ' + deviceid + ' shell input tap ' + positionInfo[0] 
    print(f'裝置{deviceid}:clickScreen():{positionInfo[1]}.')
    if os.system(cmd) != 0:
        return False
    else:
        return True
    
# 滑動螢幕的動作
def swipeScreen(positionInfo, deviceid = ''):
    cmd = 'adb -s ' + deviceid + ' shell input swipe ' + positionInfo[0]
    print(f'裝置{deviceid}:swipeScreen():{positionInfo[1]}.')
    if os.system(cmd) != 0:
        return False
    else:
        return True

# 執行app
def launchApp(appactivityInfo, deviceid = ''):
    cmd = 'adb -s ' + deviceid + ' shell am start ' + appactivityInfo[0]
    print(f'裝置{deviceid}:launchApp():{appactivityInfo[1]}.')
    if os.system(cmd) != 0:
        return False
    else:
        return True
        
# 獲取username,  如chinaren
def getusername():
    namelist = os.popen('echo %username%').readlines()
    username = namelist[0].replace("\n", "")
    # 獲取當前的username
    return username

# 獲取裝置id列表
def getdevlist():
    devlist = []
    connectfile = os.popen('adb devices')
    list = connectfile.readlines()
    # print(list)
    for i in range(len(list)):
        if list[i].find('\tdevice') != -1:
            temp = list[i].split('\t')
            devlist.append(temp[0])
    return devlist

# 返回指定txt檔案的第一行內容,主要是為了獲取待Copy的檔名
def getspecifytxtfilefirstline(txtfilename):
    '''
    返回指定txt檔案的第一行內容
    txt檔要求與py檔在同一目錄
    '''
    currunningpyfilepath = os.path.split(os.path.realpath(__file__))[0]
    copyfile2phoneTXT = currunningpyfilepath + '\\' + txtfilename
    while True:
        if os.path.exists(copyfile2phoneTXT) == True:
            with open(copyfile2phoneTXT, 'r') as f:
                filename = f.readline()
                if filename == '':
                    print('txt檔為空,輸入重新寫入待copy檔名.')
                    with open(copyfile2phoneTXT, 'w') as f:
                        fileStr = input('請輸入你要copy檔案的完整檔名\n(建議直接拖入檔案至這裡,會自動獲取寫入txt檔):')
                        filenamelist = fileStr.split('\\')
                        filename = filenamelist[len(filenamelist) - 1]
                        f.write(filename)
                        print('輸入的檔名已寫入.')
                        return filename
                else:
                    return filename
        else:
            print('copyfile2phone.txt檔案不存在,將在當前目錄新建一個此txt檔案!')
            with open(copyfile2phoneTXT, 'w') as f:
                print('copyfile2phone.txt檔案新建成功!')
                fileStr = input('請輸入你要copy檔案的完整檔名\n(建議直接拖入檔案至這裡,會自動獲取寫入txt檔):')
                filenamelist = fileStr.split('\\')
                filename = filenamelist[len(filenamelist) - 1]
                f.write(filename)
                print('輸入的檔名已寫入.')
                return filename

# 測試Adb連線性
def checkAdbConnectability(flag=0):
    '''
    flag =0時,當連線正常時返回True(default)
    flag!=0時,直接打印出結果
    '''
    connectstring = '''ADB連線失敗, 請check以下項:
    1. 是否有連線上手機?請連線上手機選並重新check連線性!
    2. 是否有開啟"開發者選項\\USB除錯模式"?\n'''
    connectinfolist = getdevlist()

    if len(connectinfolist) == 0:
        return False
    if len(connectinfolist) == 1:
        if flag != 0:
            print('連線正常')
            print(f'裝置SN: {connectinfolist[0]}')
        else:
            return True
    if len(connectinfolist) >= 2:
        print('連線正常,當前有連線多臺裝置. ')
        for i in range(len(connectinfolist)):
            print(f'裝置{i + 1} SN: {connectinfolist[i]}')
        return True

def isAwaked(deviceid = ''):
    '''
    判斷的依據是'    mAwake=false\n'
    '''
    if deviceid == '':
        cmd = 'adb shell dumpsys window policy'
    else:
        cmd = 'adb -s ' + deviceid + ' shell dumpsys window policy'
    screenAwakevalue = '    mAwake=true\n' 
    allList = os.popen(cmd).readlines()
    if screenAwakevalue in allList:
        return True
    else:
        return False
        
# Copy檔案操作
def copyfile_task(deviceid, filename):
    # 刪除BxxTest資料夾和*.zip檔案
    delBxxTestcmd  = ('rm -r /sdcard/BxxTest', '刪除BxxTest資料夾')
    delXXXreportscmd = ('rm -r /sdcard/XXXreports', '刪除1report資料夾')
    delzipcmd      = ('rm -r /sdcard/*.*', '刪除根目錄下的舊版本檔案')

    print(f'裝置:{deviceid}正在執行刪除BSTTest、XXXreports資料夾動作... ...')
    exeCmd(delBxxTestcmd, deviceid)
    exeCmd(delXXXreportscmd, deviceid)
    exeCmd(delzipcmd, deviceid)
                            
    copyfilepath = 'C:\\Users\\' + getusername() + '\\Desktop\\' + filename
    print(f'裝置:{deviceid}準備執行copy操作!')
    cmd = 'adb -s ' + deviceid + ' push ' + copyfilepath + ' /sdcard/'
    if os.system(cmd) == 0:
        print(f'裝置:{deviceid} 檔案copy成功!\n')
    else:
        winsound.Beep(800, 10000)
        
    # 開啟filemanager application
    print('正在開啟filemanager應用... ...')
    isexistCmd  = ('ps | findstr filemanager', '檢視filemanager app是否已開啟')
    closeappCmd = ('am force-stop com.android.filemanager', '關閉filemanager app')
    runappCmd   = ('am start com.android.filemanager/.FileManagerListActivity', '開啟filemanager app')

    if exeCmd(isexistCmd, deviceid) == True:
        print(f'裝置{deviceid}的filemanager app之前已開啟.將關閉再開啟')
        exeCmd(closeappCmd, deviceid)
        # exeCmd(runappCmd, deviceid)
        if exeCmd(runappCmd, deviceid) == False:
            reconnectAction(deviceid)
            exeCmd(runappCmd, deviceid)
    else:
        print('filemanager app已開啟.')
        if exeCmd(runappCmd, deviceid) == False:
            reconnectAction(deviceid)
            exeCmd(runappCmd, deviceid)
        
    # 點亮手機螢幕開始升級
    lightCmd        = ('26', '點亮螢幕')
    unlockscreenCmd = ('380 1300 380 300', '螢幕解鎖')
    swipescreen     = ('380 1300 380 300', '上滑螢幕')
    clickload       = ('400 1300', '在檔案列表中點選load')
    clickupdata     = ('509 1330', '點選升級按鍵')
    if isAwaked(deviceid) == False:
        pressKeyevent(lightCmd, deviceid)
    swipeScreen(unlockscreenCmd, deviceid)
    sleep(0.5)
    # 因為在解鎖後設備會大概率的斷開一下,故如果斷線則需要等待重連
    if swipeScreen(swipescreen, deviceid) == False:
        reconnectAction(deviceid)
        swipeScreen(swipescreen, deviceid)
    swipeScreen(swipescreen, deviceid)
    sleep(0.5)
    if clickScreen(clickload, deviceid) == False:
        reconnectAction(deviceid)
        clickScreen(clickload, deviceid)
    sleep(0.5)
    if clickScreen(clickupdata, deviceid) == False:
        reconnectAction(deviceid)
        clickScreen(clickupdata, deviceid)
    # 當所有操作完成時,手機會不斷的閃屏+PC會不斷的'嘟嘟'提醒,直到裝置斷開
    print(f'\n當前裝置{deviceid} copy動作已完成... 請撥線... ')
    temp = getdevlist()
    while deviceid in temp:
        # os.popen('adb -s ' + deviceid + ' shell input keyevent 26')
        winsound.Beep(500, 500)
        temp = getdevlist()

def main():
    print('---------重要提示---------')
    print('請將待Copy檔案放置在桌面再進行操作\n\n')
    
    connectdevice = input('請輸入每次要同時下載的裝置數(注意:如果要改變裝置同時下載的裝置數,則需要關閉指令碼重新run):')
    number = int(connectdevice.strip())
   
    while True:
        lists = getdevlist()
        devnum = len(lists)
        id = 1
        tempdevlist = getdevlist()
        if devnum < number:
            print(f'\n裝置未全部識別,應識別{number}臺裝置!\n當前已識別{devnum}臺裝置,請連線裝置並等待識別:\n\n')
            for i in range(devnum):
                print(f'裝置{id}: {lists[i]}')
                id = id + 1
        while devnum < number:
            lists = getdevlist()
            curnum = len(lists)
            if curnum > devnum:
                for i in range(len(lists)):
                    if lists[i] not in tempdevlist:
                        print(f'裝置{id}: {lists[i]}')
                        id = id + 1
                        tempdevlist = getdevlist()
                devnum = curnum
            
        print(f'\n所有裝置已全部識別!當前有連線{len(getdevlist())}臺裝置.\n\n')
        
        while True:
            copyfilename = getspecifytxtfilefirstline('copyfile2phone.txt')
            if checkAdbConnectability() == True:
                copyfilepath = 'C:\\Users\\' + getusername() + '\\Desktop\\' + copyfilename
                if os.path.isfile(copyfilepath) == True:
                    devicelist = getdevlist()
                    print(f'要copy的檔名:{copyfilename}')
                    print('COPY檔案準備中,請稍候... ...\n')
                    p = Pool(4)
                    for i in range(len(devicelist)):
                        p.apply_async(copyfile_task, args=(devicelist[i], copyfilename))
                    p.close()
                    p.join()
                    # 等待的裝置斷開
                    devnum = len(getdevlist())
                    while devnum != 0:
                        devnum = len(getdevlist())
                    print('\n所有裝置已斷開!\n\n\n請連線其它手機進行下一輪Copy... ...')
                else:
                    print('TXT檔中指定的檔名不存在TXT檔將被刪除並新建,需要重新輸入檔名:')
                    # 刪除檔案
                    curfilepath = os.path.split(os.path.realpath(__file__))[0]
                    TXTfilepath = curfilepath + '\\' + 'copyfile2phone.txt'
                    os.remove(TXTfilepath)
                    print('TXT檔被刪除成功')
            else:
                break  

# 主程式
if __name__ == '__main__':
    main()