Compare commits

...

87 Commits
2.0.1 ... main

Author SHA1 Message Date
ef3409419a 更新README 2024-02-05 09:05:30 +08:00
6aed0296ee 忘记修改版本号了 2024-02-04 11:06:33 +08:00
022e2f1b53 小调 2024-02-04 11:05:09 +08:00
27cf5b077a 2.2.0.1 2024-02-04 11:02:19 +08:00
c8e0d5042e 2.2.0 2024-01-30 11:19:53 +08:00
82b352d827 新增Via安装功能 2024-01-30 10:15:14 +08:00
e94ebecfa2 修复部分系统在使用程序的一些功能时发生崩溃的问题 2024-01-30 09:57:46 +08:00
123fadd7ac 新增QQ群入口 2024-01-30 09:21:36 +08:00
3fbfb38a53 新增ARM架构非飞腾CPU识别 2024-01-29 21:27:47 +08:00
83e34a983c 调整关于窗口 2024-01-29 20:41:37 +08:00
53b16c9d35 修改关于窗口背景 2024-01-29 19:30:05 +08:00
4c940403bd 新增赞赏页面 2024-01-29 19:26:54 +08:00
f82d767a19 修复 deepin 23 安装的 APK 无法正常在启动器显示图标的问题 2024-01-29 19:17:20 +08:00
d385fdd64c 适配deepin23 2024-01-29 18:55:14 +08:00
ce15113a7a 新增运行aapt脚本 2024-01-29 18:33:14 +08:00
23179c0266 精简aapt程序包 2024-01-29 18:26:52 +08:00
b8dde0ff52 进一步补充aapt 2024-01-21 17:19:28 +08:00
3df126d700 添加aapt包 2024-01-21 13:31:46 +08:00
e045169d99 添加bwrap文件以准备支持deepin23 2024-01-21 13:28:06 +08:00
414681fc0b 优化安装量统计机制 2023-11-11 10:08:09 +08:00
691d140db5 修改README链接 2023-08-17 13:59:16 +08:00
96cf7dfe74 2.1.2 2023-08-14 17:03:00 +08:00
576470f70a 引导用户到Gitee反馈bug 2023-08-14 16:55:12 +08:00
625e456e6b 修复deepin23缺失pil的问题 2023-08-14 16:46:20 +08:00
Bail
2a17547da3
修复添加应用图标时activity名称错误的问题 2023-07-28 17:58:47 +08:00
Bail
fb46147547
修复 #I6ZRZX
根据对报错信息的分析,我认为这个问题可能是由于程序退出时由于updatekiller.py已将pid文件删除,导致要退出的程序在atexit模块注册的函数要删除记录到pid时找不到pid文件,导致报错。
但经过复现尝试,发现不是杀进程时触发的。因为杀进程时被杀的进程不会执行atexit。
由于已更新至最新版本,无法通过更新进行复现。所以具体报错原因尚未探究清楚,问题尚未完全修复。
2023-07-18 21:57:53 +08:00
d37c7514f3 修改deb打包格式 2023-07-10 09:40:45 +08:00
7fe6187f98 修复bug提交问题 2023-07-09 06:41:43 +08:00
0bbb560b48 auto building 2023-07-06 15:36:29 +08:00
d583c396b1 忘改control了 2023-07-06 11:48:59 +08:00
2096022bd4 修改README 2023-07-06 11:39:08 +08:00
e3c342c07b 修复postinst在无网状态下导致uengine-loading-ubuntu出错的问题 2023-07-06 11:34:58 +08:00
ee6c7c45d8 postrm 2023-07-06 11:28:09 +08:00
76d5d60c2b 2.1.1 2023-07-06 11:16:36 +08:00
00dd3283e9 添加更新内容 2023-07-06 11:00:31 +08:00
b2998ffe80 优化postinst 2023-07-06 10:48:21 +08:00
579663c32c 修改dtk获取的url 2023-07-06 10:45:30 +08:00
717284ad14 修复UEngine在debian12无法正常开启的问题 2023-07-06 10:44:45 +08:00
197f0a9dca 修复 https://gitee.com/gfdgd-xi/uengine-runner/issues/I6B6C8 的问题 2023-06-25 12:02:30 +08:00
b1e455ca48 治标 2023-06-25 11:57:34 +08:00
86bc122c5f 修复在DEEPIN/UOS程序菜单检查到UENGINE不存在但是无法安装的问题 2023-06-23 16:51:13 +08:00
0ca2e4f01b 修改README 2023-06-03 22:09:57 +08:00
4e14f35021 修改README 2023-06-03 20:21:41 +08:00
10d94886a5 修复缺少zentiy依赖的问题 2023-06-03 17:33:53 +08:00
e57ac28cde Merge branch 'main' of https://gitee.com/gfdgd-xi/uengine-runner 2023-05-27 22:43:51 +08:00
5853fc68c2 修改README 2023-05-27 22:43:41 +08:00
aa039277a9 Repair UEngine Installer System Check Problem 2023-05-21 18:28:23 +08:00
d759f81905 修改程序文案 2023-05-13 15:04:11 +08:00
a0b673afb0 修复README格式问题 2023-05-13 13:32:52 +08:00
b9fd45980f 修改README 2023-05-13 13:21:16 +08:00
31aeb66cd4 update 2023-05-13 13:14:30 +08:00
896214d2b7 新增更新内容 2023-05-10 22:25:34 +08:00
6df4263107 更新更新内容 2023-05-10 22:20:36 +08:00
373bc98869 修改SuperSU UEngine地址 2023-05-10 21:09:17 +08:00
2a9df20b5c 修改源 2023-05-10 21:05:05 +08:00
0e4afd90d9 修改url至jihulab 2023-05-06 21:35:01 +08:00
0dc3a079ee 新增加载ashmem模块 2023-05-03 22:37:50 +08:00
898f873cec 新增安装完成的提示对话框 2023-05-03 09:27:12 +08:00
de79106d61 优化installer 2023-05-03 09:23:04 +08:00
543571d66b 优化uengine-loading-binder 2023-05-03 09:00:06 +08:00
f2acfcc4be 修改版本号 2023-05-02 21:14:16 +08:00
5a0aa5160c 取消只能在ubuntu安装的限制 2023-05-02 19:40:05 +08:00
下雨中gfd
abf6d4903f
update new-deb-build/DEBIAN/control.
Signed-off-by: 下雨中gfd <gfdgdxi@foxmail.com>
2023-05-02 10:43:34 +00:00
rain-gfd
5bcb05142c change daemon 2023-05-02 17:30:06 +08:00
rain-gfd
b358b56974 change 2023-05-02 17:26:02 +08:00
rain-gfd
ef1b7800c6 repair error coding grammar 2023-05-02 17:21:13 +08:00
rain-gfd
0b2a8e7821 add sudo command 2023-05-02 17:13:26 +08:00
rain-gfd
9608ce6bfc url error 2023-05-02 17:03:24 +08:00
rain-gfd
0e21b7187d repair check kernel problem 2023-05-02 17:00:20 +08:00
b277bb25b4 更改安装包地址 2023-05-02 15:10:01 +08:00
35416ce12b 修改makefile 2023-05-02 14:07:11 +08:00
8ccd721c98 修改2048位置 2023-05-02 13:58:56 +08:00
0f6f24aa95 新增资源解压检查 2023-05-02 13:41:33 +08:00
6bdfd5a9ef 新增加载binder免密 2023-05-02 13:38:06 +08:00
27817c824e run_daemon加载binder功能 2023-05-02 13:22:17 +08:00
06c763d862 更新run_daemon 2023-05-02 13:12:47 +08:00
4937e60333 新增index文件 2023-05-02 13:10:48 +08:00
6775f5433d 更换shell地址 2023-05-02 13:09:24 +08:00
eeeff8429a 添加daemon 2023-05-02 13:07:24 +08:00
1d3ca4bf2d 新增加载新版内核的binder功能 2023-05-02 13:03:02 +08:00
59e2ccfd63 开始尝试安装器不强制要求安装内核 2023-05-02 12:58:06 +08:00
f716c22e76 修改接口地址 2023-04-30 14:06:41 +08:00
9c86d23d31 换源 2023-04-30 13:26:22 +08:00
4e3b49007d 更新帮助 2023-04-29 16:39:28 +08:00
0a2c8bbdab 更新md 2023-04-29 16:37:36 +08:00
0c1ab111a6 修改帮助标题 2023-04-29 16:35:24 +08:00
8092d608c3 end 2023-04-29 16:19:10 +08:00
171 changed files with 5849 additions and 273 deletions
.github/workflows
.gitignore
APK
AutoConfig.pyConfigLanguareRunner.pyDownload.py
Help
Icon
LoadingBinder
MakefileProgramFen.pyREADME.md
UI
aapt
api
bwrap
getxmlimg.pyinformation.jsonmainwindow.py
new-deb-build

26
.github/workflows/auto-building.yml vendored Normal file

@ -0,0 +1,26 @@
name: Auto Building Wine Runner
run-name: ${{ github.actor }} Auto Building Wine Runner 🚀
on:
push:
workflow_dispatch:
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- name: Building DEB
run: |
# 配置环境
sudo apt update
sudo apt install git dpkg-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qt5-qmake -y
git clone https://github.com/gfdgd-xi/uengine-runner
cd uengine-runner
make build
mv com.gitee.uengine.runner.spark.deb ~
- name: upload result
uses: actions/upload-artifact@v1
with:
name: com.gitee.uengine.runner.spark.deb
path: /home/runner/com.gitee.uengine.runner.spark.deb

1
.gitignore vendored Normal file

@ -0,0 +1 @@
__pycache__

BIN
APK/Via.apk Normal file

Binary file not shown.

@ -35,9 +35,9 @@ print("""译文:一轮缓缓移动的秋月洒下万里金波,就像那刚
我要乘风飞上万里长空俯视祖国的大好山河还要砍去月中摇曳的桂树枝柯人们说这将使月亮洒下人间的光辉更多""")
print("================================")
urlSourcesList = [
"https://code.gitlink.org.cn/gfdgd_xi/uengine-runner-list/raw/branch/master/auto", # Gitlink
"http://config.uengine-runner.gfdgdxi.top/", # Github
"https://gitee.com/gfdgd-xi/uengine-wine-runner-auto-configuration-script/raw/master/", # Gitee 源
"https://gfdgd-xi.github.io/uengine-wine-runner-auto-configuration-script/", # Github
"https://code.gitlink.org.cn/gfdgd_xi/uengine-runner-list/raw/branch/master/auto", # Gitlink
"http://gfdgdxi.msns.cn/uengine-runner-list/auto/", # 备用源1纯 IPv6 源
"http://gfdgdxi.free.idcfengye.com/uengine-runner-auto-configuration-script/", # 备用源2
"http://gfdgdxi.free.idcfengye.com/uengine-runner-list/auto/", # 备用源 3
@ -146,7 +146,7 @@ class InformationWindow():
about = f"<h1>关于“{choose}”的介绍</h1>\n<p>暂无此程序的介绍</p>"
try:
import requests as r
fenlists = requests.get(base64.b64decode("aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy93aW5lLXJ1bm5lci1kb3dubG9hZHMtb2YtcnVubmVyL3Jhdy9icmFuY2gvbWFzdGVyL0Jhc2hBcHBGZW4v").decode("utf-8") + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8"), timeout=1000).json()
fenlists = requests.get("http://data.download.gfdgdxi.top/BashAppFen/" + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8"), timeout=1000).json()
tipsInfo = ""
except:
fenlists = [0, 0, 0, 0, 0]
@ -166,19 +166,19 @@ class InformationWindow():
if maxHead > 5:
for i in range(end):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/BadStar.svg' width=50>\n"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/BadStar.svg' width=50>\n"
else:
starHtml += f"<img src='{programPath}/Icon/BadStar.svg' width=50>\n"
else:
for i in range(maxHead):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/Star.svg' width=50>\n"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/Star.svg' width=50>\n"
else:
starHtml += f"<img src='{programPath}/Icon/Star.svg' width=50>\n"
head = maxHead
for i in range(head, end):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/UnStar.svg' width=50>"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/UnStar.svg' width=50>"
else:
starHtml += f"<img src='{programPath}/Icon/UnStar.svg' width=50>"
about += f"\n<hr/><h1>评分情况</h1>\n<p align='center'>{starHtml}</p>\n<p align='center'>{tipsInfo}</p>"
@ -260,7 +260,7 @@ class ProgramRunStatusShow():
fileName = i[1]
break
try:
fenlists = requests.get(base64.b64decode("aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy93aW5lLXJ1bm5lci1kb3dubG9hZHMtb2YtcnVubmVyL3Jhdy9icmFuY2gvbWFzdGVyL0Jhc2hBcHBGZW4v").decode("utf-8") + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8")).json()
fenlists = requests.get("http://data.download.gfdgdxi.top/BashAppFen/" + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8")).json()
tipsInfo = ""
except:
#traceback.print_exc()
@ -318,7 +318,7 @@ class ProgramRunStatusShow():
else:
# 显示最新的3条评论
try:
all = int(requests.get(f"{base64.b64decode('aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy9iYXNocGlubHVuL3Jhdy9icmFuY2gvbWFzdGVyLw==').decode('utf-8')}{urllib.request.quote(fileName)}/data.txt").text)
all = int(requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/data.txt").text)
now = all - 3
print(all)
if all < 3:
@ -329,7 +329,7 @@ class ProgramRunStatusShow():
uploadList = []
for i in range(all - 1, start - 1, -1):
print(f"{i + 1} 条评论:")
info = requests.get(f"{base64.b64decode('aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy9iYXNocGlubHVuL3Jhdy9icmFuY2gvbWFzdGVyLw==').decode('utf-8')}{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
info = requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
print(info)
uploadList.append([f"用户{i + 1}", i + 1, info])
Add(uploadList)
@ -386,7 +386,7 @@ def UpdateFen():
uploadList = []
for i in range(now + 2, now - 1, -1):
print(f"{i + 1} 条评论:")
info = requests.get(f"https://code.gitlink.org.cn/gfdgd-xi-org/bashpinlun/raw/branch/master/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
info = requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
print(info)
uploadList.append([f"用户{i + 1}", i + 1, info])
#ProgramRunStatusShow.pingLunLayout.removeItem(ProgramRunStatusShow.pingLunLayout.itemAt(2))
@ -525,7 +525,7 @@ def readtxt(path):
def ChangeSources():
global urlSources
global urlSourcesIndex
sources = [ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action]
sources = [ui.actionGithub, ui.actionGitee, ui.actionGitlink, ui.action_IPv6, ui.action_2, ui.action_3, ui.action]
for i in range(0, len(sources)):
if sources[i].isChecked():
old_urlSources = urlSources #先备份
@ -541,7 +541,7 @@ def ChangeSources():
ui.searchList.setModel(nmodel)
urlSourcesIndex = i
except:
[ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action][urlSourcesIndex].setChecked(True)
#[ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action][urlSourcesIndex].setChecked(True)
traceback.print_exc()
QtWidgets.QMessageBox.critical(window, "提示", "无法连接服务器")
urlSources = old_urlSources #如果源不可用则换回来

@ -41,8 +41,8 @@ programEnv = [
["($PROGRAMPATH)", programPath],
["($VERSION)", version],
["($THANK)", thankText],
["($MAKER)", "RacoonGX 团队By gfdgd xi"],
["($COPYRIGHT)", f"©2020~{time.strftime('%Y')} RacoonGX 团队By gfdgd xi"],
["($MAKER)", "gfdgd xi"],
["($COPYRIGHT)", f"©2020~{time.strftime('%Y')} gfdgd xi"],
["($?)", "0"],
["($PLATFORM)", platform.system()],
["($DEBUG)", "1"]

@ -1,5 +1,7 @@
#!/usr/bin/env python3
import os
import sys
import json
import base64
import requests
print("""浣溪沙
@ -8,4 +10,7 @@ print("""浣溪沙
print("")
print("听一支新曲喝一杯美酒,还是去年的天气旧日的亭台,西落的夕阳何时再回来?那花儿落去我也无可奈何,那归来的燕子似曾相识,在小园的花径上独自徘徊。")
print("================================")
print(requests.get(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3VlbmdpbmUtcnVubmVyL0luc3RhbGwucGhwP1ZlcnNpb249").decode("utf-8") + sys.argv[1]).text)
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
with open(f"{programPath}/information.json") as file:
version = json.loads(file.read())["Version"]
print(requests.get(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3VlbmdpbmUtcnVubmVyL0luc3RhbGwucGhwP1ZlcnNpb249").decode("utf-8") + version).text)

Before

Width: 32px  |  Height: 32px  |  Size: 4.2 KiB

After

Width: 32px  |  Height: 32px  |  Size: 4.2 KiB

Before

(image error) Size: 5.7 KiB

After

(image error) Size: 5.7 KiB

@ -2,7 +2,7 @@
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width initial-scale=1'>
<link rel="shortcut icon" href="../runner.svg">
<title>UEngine 程序帮助</title>
<title>UEngine 运行器帮助</title>
</head>
<h1 id="uengine-运行器帮助">UEngine 运行器帮助</h1>
<hr>
@ -12,6 +12,9 @@
</blockquote>
<hr>
<h2 id="常见问题">常见问题</h2>
<h3 id="Deepin 23Alpha2 缺失依赖怎么办?">Deepin 23Alpha2 缺失依赖怎么办?</h3>
<p><a href="https://gfdgdxi.lanzoue.com/b01qiu22f">https://gfdgdxi.lanzoue.com/b01qiu22f</a> 拉取依赖</p>
<p>密码:dihl</p>
<h3 id="出现如下图的情况如何修复?">出现如下图的情况如何修复?</h3>
<p><img src="2-1-1.png" alt=""><br>出于安全考虑,您的平板电脑已设置为禁止安装来源不明的应用。 </p>
<h4 id="修复方法">修复方法</h4>

@ -16,6 +16,10 @@
---
## 常见问题
### Deepin 23Alpha2 缺失依赖怎么办?
从 https://gfdgdxi.lanzoue.com/b01qiu22f 拉取依赖
密码:dihl
### 出现如下图的情况如何修复?
![](2-1-1.png)
出于安全考虑,您的平板电脑已设置为禁止安装来源不明的应用。

@ -1,17 +1,14 @@
<head>
<title>Wine 运行器程序公告</title>
<title>UEngine 运行器程序公告</title>
</head>
<body>
<h1>针对在 Ubuntu 安装 UEngine 后狂刷通知的解答UEngine Installer</h1>
<p>先用如下命令移除掉该组件(在终端输入),然后等待作者修复该问题:</p>
<pre><code>s
sudo rm /usr/share/applications/uengine-loading-ubuntu.desktop
sudo rm /etc/xdg/autostart/uengine-loading-ubuntu.desktop
sudo rm /usr/bin/uengine-loading-ubuntu</code></pre>
<hr/>
<h1>常见链接</h1>
<p>程序论坛:<a href="https://gfdgdxi.flarum.cloud/">https://gfdgdxi.flarum.cloud/</a></p>
<p>星火应用商店:<a href="https://spark-app.store/">https://spark-app.store/</a></p>
<p><a href='https://deepin-community-store.gitee.io/spk-resolv?spk=spk://store/tools/com.gitee.uengine.runner.spark'>点击这里从星火应用商店获取 UEngine 运行器</a></p>
<hr/>
<p align="center"><img src="https://gitee.com/gfdgd-xi/uengine-runner/widgets/widget_card.svg?colors=eae9d7,2e2f29,272822,484a45,eae9d7,747571"></p>
</body>
<hr/>
<h1>程序安装/打开量</h1>
<p align="center"><img src="http://gfdgd-xi.github.io/images/uengine-runner-open.svg"></p>
<p align="center"><img src="http://gfdgd-xi.github.io/images/uengine-runner.svg"></p>
</body>

3
Help/shell/index.html Normal file

@ -0,0 +1,3 @@
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>文件目录</title></head><body><h1>文件目录</h1><ul>
<li><a href="run_daemon.sh">run_daemon.sh</a></li>
</ul></body></html>

77
Help/shell/run_daemon.sh Normal file

@ -0,0 +1,77 @@
#/bin/bash
# 狗头
echo '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
echo '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`***,@@@@@@@@'
echo '@@@@^*****,\@@@@@@@@@@@@@@@@@@@@`*.....*\@@@@@@'
echo '@@@@*.......**\@[`***......*****.....*..*\@@@@@'
echo '@@@@^*..............................***...@@@@@'
echo '@@@@@*...............................**..*\@@@@'
echo '@@@@@^*..*............................*..*=@@@@'
echo '@@@@@^**................................**=@@@@'
echo '@@@@@/*..................................*=@@@@'
echo '@@@@@*..... ..... ............*@@@@'
echo '@@@@`*. .]]]]` ... ]/[[[O/O]`........*,@@@'
echo '@@@@*.=` =O.,OO......=` .OOOOOO^........*@@@'
echo '@@@^*.\ =OOOOO^.....=` .OOOOOO^........*=@@'
echo '@@@^*..,\].=OO/.........,[\]]O/[` ......*=@@'
echo '@@@@*...................... .*@@@'
echo '@@@@`. ......,]]...... .,@@@'
echo '@@@@@*. OOOOOOO^ .*@@@@'
echo '@@@@@\*. \OOOO` .*/@@@@'
echo '@@@@@@\*. =. / .*/@@@@@'
echo '@@@@@@@@`*. ,\OOOOOOO]]OO` .*,@@@@@@@'
echo '@@@@@@@@@@`*. . .*,@@@@@@@@@'
echo '@@@@@@@@@@@@\**.. ......... ..**/@@@@@@@@@@@'
echo '@@@@@@@@@@@@@@@@]`***......***,]@@@@@@@@@@@@@@@'
echo '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
# 使 UEngine 能在 Wayland 下运行
export XDG_SESSION_TYPE=x11
export QT_QPA_PLATFORM=xcb
unset WAYLAND_DISPLAYCOPY
# 判断是否是开机后第一次运行(无用)
#if [[ ! -f /tmp/uengine-loading-ubuntu ]]; then
# # 需等待一段时间等系统全部加载完成
# notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "UEngine 服务正在加载" -a uengine-runner
# sleep 10
# touch /tmp/uengine-loading-ubuntu
#fi
# 修复程序显示问题
XDG_CURRENT_DESKTOP="Deepin"
export LD_LIBRARY_PATH=/usr/share/uengine/lib64/
# 判断 UEngine 是否被正确安装
which uengine
if [[ $? != 0 ]]; then
notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "未安装 UEngine结束" -a uengine-runner
exit
fi
uname -r | grep 5.17.3
if [[ $? != 0 ]]; then
if [[ -f /usr/bin/uengine-loading-binder ]]; then
echo 加载 binder
pkexec /usr/bin/uengine-loading-binder
fi
fi
notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "UEngine 服务启动完成" -a uengine-runner
bad=0
# 守护进程,防止异常退出
while [[ true ]]; do
uengine session-manager -platformtheme=deepin
# 让用户可以强制结束
if [[ -f "/tmp/uengine-loading-ubuntu-end" ]]; then
rm /tmp/uengine-loading-ubuntu-end
echo UEngine 服务异常崩溃,不再重启服务
notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "UEngine 服务异常崩溃,不再重启服务" -a uengine-runner
exit
fi
if [[ $bad -gt 9 ]]; then
# 错误次数太多,结束
notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "UEngine 服务异常崩溃次数过多,不再重启服务" -a uengine-runner
echo UEngine 服务异常崩溃次数过多,不再重启服务
exit
fi
bad=$(($bad+1))
echo "UEngine 服务异常结束,重新启动($bad次"
notify-send -i /opt/apps/com.gitee.uengine.runner.spark/files/icon.png "UEngine 服务异常结束,重新启动($bad次" -a uengine-runner
done

BIN
Icon/Program/about-background.png Executable file

Binary file not shown.

After

(image error) Size: 9.9 KiB

BIN
Icon/Program/about-icon.png Executable file

Binary file not shown.

After

(image error) Size: 69 KiB

BIN
Icon/QR/Alipay.jpg Normal file

Binary file not shown.

After

(image error) Size: 270 KiB

BIN
Icon/QR/QQ.png Normal file

Binary file not shown.

After

(image error) Size: 66 KiB

BIN
Icon/QR/Wechat.png Normal file

Binary file not shown.

After

(image error) Size: 76 KiB

BIN
Icon/QR/advertisement0.jpg Normal file

Binary file not shown.

After

(image error) Size: 338 KiB

13
LoadingBinder/load.cpp Normal file

@ -0,0 +1,13 @@
#include <iostream>
using namespace std;
int main(){
// 检查是否是 root 用户
if(system("[[ `whoami` == root ]]")){
cout << "这不是 root 账户,失败!";
return 1;
}
system("modprobe binder_linux");
system("mkdir /dev/binderfs");
system("mount -t binder binder /dev/binderfs");
return 0;
}

BIN
LoadingBinder/load.out Executable file

Binary file not shown.

17
LoadingBinder/load.sh Normal file

@ -0,0 +1,17 @@
#!/bin/bash
if [[ `whoami` != root ]]; then
echo 这不是 root 用户,结束
exit 1
fi
if [ -d /dev/binderfs ]; then
echo 已启动 binderend
systemctl start uengine-container.service
systemctl start uengine-session.service
exit
fi
modprobe binder_linux
modprobe ashmem_linux
mkdir /dev/binderfs
mount -t binder binder /dev/binderfs
systemctl start uengine-container.service
systemctl start uengine-session.service

7
LoadingBinder/uengine Normal file

@ -0,0 +1,7 @@
#!/bin/bash
export XDG_SESSION_TYPE=x11
export QT_QPA_PLATFORM=xcb
unset WAYLAND_DISPLAYCOPY
XDG_CURRENT_DESKTOP="Deepin"
export LD_LIBRARY_PATH=/usr/share/uengine/lib64/
uengine-session "$@" -platformtheme=deepin

@ -0,0 +1,15 @@
#!/bin/bash
programPath=$(cd $(dirname $0); pwd)
if [[ ! -f /usr/bin/uengine ]]; then
echo 未安装uengine请先安装
exit 1
fi
if [[ -f /usr/bin/uengine-session ]] ;then
echo 已经安装补丁,无需重复安装!
exit 1
fi
sudo mv /usr/bin/uengine /usr/bin/uengine-session
sudo cp "$programPath/uengine" /usr/bin/uengine
sudo chmod +x /usr/bin/uengine
sudo systemctl restart uengine-session.service
echo 补丁安装完成!

@ -0,0 +1,15 @@
#!/bin/bash
programPath=$(cd $(dirname $0); pwd)
if [[ ! -f /usr/bin/uengine ]]; then
echo 未安装uengine请先安装
exit 1
fi
if [[ ! -f /usr/bin/uengine-session ]] ;then
echo 未安装补丁,无需卸载!
exit 1
fi
rm -fv /usb/bin/uengine
sudo mv /usr/bin/uengine-session /usr/bin/uengine
sudo chmod +x /usr/bin/uengine
sudo systemctl restart uengine-session.service
echo 补丁卸载完成!

@ -1,5 +1,6 @@
clean:
python3 RemovePycacheFile.py
rm com.gitee.uengine.runner.spark.deb -fv
build:
echo 别云间
@ -56,6 +57,10 @@ build:
cp -rv Icon new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv CompareVersion.py new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv uengine-remove.sh new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv pkexec new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv LoadingBinder new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv aapt new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
cp -rv APK new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
rm -rfv new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/Help/information
python3 UpdateTime.py
python3 RemovePycacheFile.py #new-deb-build/opt/apps/com.gitee.uengine.runner.spark/files/
@ -84,4 +89,4 @@ depend:
python3 -m pip install --upgrade keyboard --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple
run:
python3 mainwindow.py
python3 mainwindow.py

@ -23,7 +23,7 @@ class ProgramRunStatusShow():
try:
fenlists = []
for i in range(6):
fenlists.append(int(requests.get(base64.b64decode("aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy93aW5lLXJ1bm5lci1kb3dubG9hZHMtb2YtcnVubmVyL3Jhdy9icmFuY2gvbWFzdGVyL0Zlbi1VRW5naW5lL0Zlbg==").decode("utf-8") + f"{i}.txt").text))
fenlists.append(int(requests.get("http://data.download.gfdgdxi.top/Fen-UEngine/Fen" + f"{i}.txt").text))
tipsInfo = ""
except:
traceback.print_exc()

117
README.md

@ -1,22 +1,23 @@
<p width=100px align="center"><img src="runner.svg"></p>
<h1 align="center">UEngine 运行器 2.0.1</h1>
<h1 align="center">UEngine 运行器 2.2.0.1</h1>
<hr>
<a href='https://gitee.com/gfdgd-xi-org/uengine-runner/stargazers'><img src='https://gitee.com/gfdgd-xi-org/uengine-runner/badge/star.svg?theme=dark' alt='star'></img></a>
<a href='https://gitee.com/gfdgd-xi/uengine-runner/stargazers'><img src='https://gitee.com/gfdgd-xi/uengine-runner/badge/star.svg?theme=dark' alt='star'></img></a>
<a href='https://gitee.com/gfdgd-xi/uengine-runner/members'><img src='https://gitee.com/gfdgd-xi/uengine-runner/badge/fork.svg?theme=dark' alt='fork'></img></a>
## 介绍
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;新版本Deepin/UOS发布后可以在应用商店安装部分官方已适配的安卓应用对爱好者来说不能自己安装APK软件包始终差点意思本程序可以为Deepin/UOS上的UEngine安卓运行环境安装自定义APK软件包并能发送安装的APK包启动菜单到桌面或系统菜单
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;虽然通过Deepin/UOS应用商店已经能够安装部分安卓应用但对于安卓应用爱好者来说不能自由地安装任意APK软件包实在是不尽如人意。本软件可以实现在Deepin/UOS上安装任意APK软件包并能将其启动图标发送到系统桌面或启动器中方便用户快速启动它
![图片.png](https://storage.deepin.org/thread/202212181918041904_图片.png)
测试平台UOS 家庭版 21.3.1deepin 20.8,UOS 专业版 1050
适配架构amd64、arm64
支持系统Deepinamd64、UOSamd64/arm64、Debianamd64≥debian 10、Ubuntuamd64≥Ubuntu 20.04
Debian 10、Debian 11、Ubuntu 20.04 需要从 http://dtk.gfdgdxi.top/ 下载 dtk版本≥5.5)并安装才能通过运行器的安装程序正确安装 UEngine
暂不支持 Debian 12
### 已知问题
UEngine 运行器 2.0.0 的 UEngine 安装器故障(正在研究解决方法):
https://gitee.com/gfdgd-xi/uengine-runner/issues/I6NTCR
https://gitee.com/gfdgd-xi/uengine-runner/issues/I6CKHB
QQ 交流群872491938
## 在 deepin 23 上安装缺失依赖?
依赖下载地址https://gfdgdxi.lanzv.com/b01rwfgtg 密码:648f
## 程序相关网站
作者个人站https://www.gfdgdxi.top
查询 APK 运行情况https://gfdgd-xi.github.io/uengine-runner-info/
@ -25,12 +26,29 @@ https://gitee.com/gfdgd-xi/uengine-runner/issues/I6CKHB
Deepin 官网https://www.deepin.org
Deepin 论坛https://bbs.deepin.org
## 在 Deepin 23 Alpha2 安装 UEngine 运行器?
## 在 Deepin 23 Alpha2/Beta 安装 UEngine 运行器?
> 例如https://gitee.com/gfdgd-xi/uengine-runner/issues/I6MBBC
需要从以下链接补充依赖:
https://gfdgdxi.lanzoue.com/b01qiu22f
密码:dihl
或者从星火应用商店下载安装,星火应用商店的源里已经补齐 UEngine 运行器在 Deepin 23 Alpha2/Beta 缺少的依赖
然后输入:
```bash
python3 -m pip install pillow
```
## 上架到的应用商店
> 可以从下列应用商店获取 UEngine 运行器
### Deepin/UOS 应用商店
![图片.png](https://storage.deepin.org/thread/20230603201852396_图片.png)
### 星火应用商店
![图片.png](https://storage.deepin.org/thread/202306032021155134_图片.png)
## 安装前必读
@ -68,6 +86,73 @@ sudo apt upgrade
![image.png](https://storage.deepin.org/thread/202205220801513371_image.png)
### 更新内容
#### V2.2.0.12023年02月04日
**※1、解决 deepin 23 beta3 缺失依赖的问题**
#### V2.2.02023年01月30日
**※1、支持 deepin 23不需要强制依赖 aapt**
**※2、修复 deepin 23 安装的 APK 无法正常在启动器显示图标的问题**
**※3、新增 ARM 架构非飞腾 CPU 识别防止破坏鲲鹏 kbox 环境**
**※4、修复部分系统在使用程序的一些功能时发生崩溃的问题**
**※5、新增 UEngine For Wayland 补丁**
6、优化安装量统计机制
7、关于窗口新增赞助页
8、新增 QQ 交流群入口
9、内置 Via 浏览器
![截图_选择区域_20240130111917.png](https://storage.deepin.org/thread/20240130031926574_截图_选择区域_20240130111917.png)
#### V2.1.22023年08月14日
**※1、修复 https://gitee.com/gfdgd-xi/uengine-runner/issues/I6ZRZX**
**※2、修复添加应用图标时activity名称错误的问题**
**※3、修复 UEngine 运行器在 23 运行时提示缺失 PIL.Image 库的问题**
![图片.png](https://storage.deepin.org/thread/202308141701411843_图片.png)
#### V2.1.12023年07月06日
**※1、修复在 Ubuntu/Debian 安装 UEngine 时提示“该脚本不支持 deepin”的问题**
**※2、修复 UEngine 在 debian12 安装无法正常开启 UEngine 的问题;**
**※3、修复在 UEngine 程序菜单检查到 UEngine 不存在但是无法安装的问题;**
**※4、修复了 https://gitee.com/gfdgd-xi/uengine-runner/issues/I6B6C8 的问题;**
**※5、优化了 postinst 和 postrm 脚本;**
6、添加 zenity 依赖;
7、屏蔽在更新程序过程中杀死程序中输出的错误
8、修改下载 dtk 的网址。
![图片.png](https://storage.deepin.org/thread/202307061119021692_图片.png)
![图片.png](https://storage.deepin.org/thread/202307061138408182_图片.png)
#### V2.1.02023年05月13日
**※1、修复源地址迁出 Gitlink**
**※2、优化 UEngine 安装器,可以在 Debian/Ubuntu 使用Debian11/Debian10/Ubuntu20.04 需要安装 dtk≥5.5dtk 可以从 https://gitee.com/gfdgd-xi/dtk-sources-for-uos-apt/releases 获取);**
**※3、UEngine 安装器不再强制安装 5.17.3 内核以运行 UEngine 运行环境,只要当前使用内核支持 binder 模块即可继续(安装前会进行检测,如果用 2.0.0/2.0.1 安装了 5.17.3 内核也可以继续使用该内核);**
**※4、修复 uengine-loading-ubuntu 的问题(参考 #I6CKHB:Ubuntu 22.04 LTS安装运行器并按提示安装UEngine后UEngine服务持续崩溃无法使用 )以及 binder 模块调用问题。**
![图片.png](https://storage.deepin.org/thread/202305131320341501_图片.png)
![微信图片_20230502211631.png](https://storage.deepin.org/thread/202305022117208736_微信图片_20230502211631.png)
![微信图片_20230502211624.png](https://storage.deepin.org/thread/202305022117206682_微信图片_20230502211624.png)
![2023-05-03_08-46.png](https://storage.deepin.org/thread/202305031117201797_2023-05-03_08-46.png)
![2023-05-03 07-09-02 的屏幕截图.png](https://storage.deepin.org/thread/202305031117196454_2023-05-0307-09-02的屏幕截图.png)
![微信图片_20230502211613.jpg](https://storage.deepin.org/thread/202305022117207441_微信图片_20230502211613.jpg)
#### V2.0.12023年04月29日劳动节版
**※1、新增移除在 Ubuntu 上安装的 UEngine 及其附属脚本的功能**
**※2、优化 Ubuntu 安装器启用和安装逻辑,不允许在 UOS/Deepin 安装移植的 Ubuntu UEngine**
@ -486,4 +571,14 @@ make install
[![星火社区作品集/spark-webapp-runtime 运行器](https://gitee.com/spark-community-works-collections/spark-webapp-runtime-runner/widgets/widget_card.svg?colors=4183c4,ffffff,ffffff,e3e9ed,666666,9b9b9b)](https://gitee.com/spark-community-works-collections/spark-webapp-runtime-runner)
[![gfdgd xi/定时器](https://gitee.com/gfdgd-xi/timer/widgets/widget_card.svg?colors=4183c4,ffffff,ffffff,e3e9ed,666666,9b9b9b)](https://gitee.com/gfdgd-xi/timer)
### ©2021-Now gfdgd xi
### 请作者喝杯茶
如果您觉得对你有帮助,可以请作者喝杯茶
<img src="Icon/QR/Wechat.png" width="250" />
<img src="Icon/QR/Alipay.jpg" width="250" />
<img src="Icon/QR/QQ.png" width="250" >
#### 广告
支付宝官方活动,扫描获得支付红包!
<p><img src="Icon/QR/advertisement0.jpg" width="250" ></p>
## ©2021-Now gfdgd xi

@ -64,13 +64,13 @@ class Ui_MainWindow(object):
self.openFile.setObjectName("openFile")
self.actionGitlink = QtWidgets.QAction(MainWindow)
self.actionGitlink.setCheckable(True)
self.actionGitlink.setChecked(True)
self.actionGitlink.setObjectName("actionGitlink")
self.actionGitee = QtWidgets.QAction(MainWindow)
self.actionGitee.setCheckable(True)
self.actionGitee.setObjectName("actionGitee")
self.actionGithub = QtWidgets.QAction(MainWindow)
self.actionGithub.setCheckable(True)
self.actionGithub.setChecked(True)
self.actionGithub.setObjectName("actionGithub")
self.action_IPv6 = QtWidgets.QAction(MainWindow)
self.action_IPv6.setCheckable(True)
@ -88,9 +88,9 @@ class Ui_MainWindow(object):
self.menu.addAction(self.openFile)
self.menu.addSeparator()
self.menu.addAction(self.exitProgram)
self.menu_2.addAction(self.actionGitlink)
self.menu_2.addAction(self.actionGitee)
self.menu_2.addAction(self.actionGithub)
self.menu_2.addAction(self.actionGitee)
self.menu_2.addAction(self.actionGitlink)
self.menu_2.addAction(self.action_IPv6)
self.menu_2.addAction(self.action_2)
self.menu_2.addAction(self.action_3)

BIN
aapt/aapt Executable file

Binary file not shown.

BIN
aapt/aapt2 Executable file

Binary file not shown.

1
aapt/libaapt.so Symbolic link

@ -0,0 +1 @@
libaapt.so.0

BIN
aapt/libaapt.so.0 Normal file

Binary file not shown.

BIN
aapt/libandroidfw.so.0 Normal file

Binary file not shown.

19
aapt/run-aapt.sh Executable file

@ -0,0 +1,19 @@
#!/bin/bash
# 判断是不是 Deepin23
cat /etc/deepin_version | grep 23
if [[ $? != 0 ]]; then
# 如果不是
# 判断系统是否有安装 aapt
which aapt > /dev/null
if [[ $? == 0 ]]; then
# 如果有安装
aapt "$@"
exit $?
fi
fi
# 如果是
programPath=$(cd $(dirname $0); pwd)
echo $programPath
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$programPath"
$programPath/aapt "$@"
exit $?

@ -6,6 +6,12 @@ import traceback
import subprocess
from getxmlimg import getsavexml
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
# 获取用户主目录
def get_home()->"获取用户主目录":
return os.path.expanduser('~')
class ProgramInformation:
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
version = "1.6.0Alpha2"
@ -49,7 +55,7 @@ class APK:
def uninstall(self):
return os.system("uengine uninstall --pkg='{}'".format(self.packageName()))
def information(self):
return subprocess.getoutput("aapt dump badging '{}'".format(self.apkPath))
return subprocess.getoutput("'{}/aapt/run-aapt.sh' dump badging '{}'".format(programPath, self.apkPath))
def activityName(self):
info = self.information()
for line in info.split('\n'):

BIN
bwrap/bwrap_amd64 Executable file

Binary file not shown.

BIN
bwrap/bwrap_arm64 Executable file

Binary file not shown.

0
bwrap/run.sh Normal file

@ -1,13 +1,22 @@
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import os
try:
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
except:
os.system("python3 -m pip install --upgrade pillow --break-system-packages --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple")
os.system("python3 -m pip install --upgrade pillow --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple")
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import zipfile
import subprocess
import re
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
class getsavexml():
def savexml(self,apkFilePath,xmlpath,iconSavePath):
cmddumpid = "aapt dump xmltree "+ apkFilePath + " " + xmlpath
cmddumpid = f"'{programPath}/aapt/run-aapt.sh' dump xmltree "+ apkFilePath + " " + xmlpath
print(cmddumpid)
xmltree = subprocess.getoutput(cmddumpid)
xmls = xmltree.splitlines()
@ -29,7 +38,7 @@ class getsavexml():
print(foreimgid)
# 直接从apk resource文件获取前后两层图片路径及ID字符串
resource = subprocess.getoutput("aapt dump --values resources " + apkFilePath + "| grep -iE -A1 " + "\"" + backimgid + "|" + foreimgid + "\"")
resource = subprocess.getoutput(f"'{programPath}/aapt/run-aapt.sh' dump --values resources " + apkFilePath + "| grep -iE -A1 " + "\"" + backimgid + "|" + foreimgid + "\"")
resourcelines = resource.splitlines()
print(resourcelines)

@ -2,11 +2,10 @@
"Package": "com.gitee.uengine.runner.spark",
"Url": [
"https://gitee.com/gfdgd-xi/uengine-runner",
"https://github.com/gfdgd-xi/uengine-runner",
"https://gitlink.org.cn/gfdgd_xi/uengine-runner"
"https://github.com/gfdgd-xi/uengine-runner"
],
"Version": "2.0.1",
"System": "Linuxdeepin/UOS/Ubuntu22.04",
"Version": "2.2.0.1",
"System": "Linuxdeepin/UOS/Ubuntu/Debian",
"Tips": [
"更多可见https://gitee.com/gfdgd-xi/uengine-runner/wikis 或程序的更多帮助",
"安装APK点浏览按钮选中需要安装的APK然后点安装按钮",
@ -23,6 +22,41 @@
"5、如果想要使用adb连接UEngine或其他手机请使用 1.2.0 以前的版本。如需连接UEngine请安装adb补丁"
],
"Update": [
"<b>V2.2.0.1</b>",
"※1、解决 deepin 23 beta3 缺失依赖的问题",
"",
"<b>V2.2.0</b>",
"※1、支持 deepin 23不需要强制依赖 aapt",
"※2、修复 deepin 23 安装的 APK 无法正常在启动器显示图标的问题",
"※3、新增 ARM 架构非飞腾 CPU 识别防止破坏鲲鹏 kbox 环境",
"※4、修复部分系统在使用程序的一些功能时发生崩溃的问题",
"※5、新增 UEngine For Wayland 补丁",
"6、优化安装量统计机制",
"7、关于窗口新增赞助页",
"8、新增 QQ 交流群入口",
"9、内置 Via 浏览器",
"",
"<b>V2.1.2</b>",
"※1、修复 https://gitee.com/gfdgd-xi/uengine-runner/issues/I6ZRZX",
"※2、修复添加应用图标时activity名称错误的问题",
"※3、修复 UEngine 运行器在 23 运行时提示缺失 PIL.Image 库的问题",
"",
"<b>V2.1.1</b>",
"※1、修复在 Ubuntu/Debian 安装 UEngine 时提示“该脚本不支持 deepin”的问题",
"※2、修复 UEngine 在 debian12 安装无法正常开启 UEngine 的问题;",
"※3、修复在 UEngine 程序菜单检查到 UEngine 不存在但是无法安装的问题;",
"※4、修复了 https://gitee.com/gfdgd-xi/uengine-runner/issues/I6B6C8 的问题;",
"※5、优化了 postinst 脚本;",
"6、添加 zenity 依赖;",
"7、屏蔽在更新程序过程中杀死程序中输出的错误",
"8、修改下载 dtk 的网址。",
"",
"<b>V2.1.0</b>",
"※1、修复源地址迁出 Gitlink",
"※2、优化 UEngine 安装器,可以在 Debian/Ubuntu 使用Debian11/Debian10/Ubuntu20.04 需要安装 dtk≥5.5dtk 可以从 https://gitee.com/gfdgd-xi/dtk-sources-for-uos-apt/releases 获取);",
"※3、UEngine 安装器不再强制安装 5.17.3 内核以运行 UEngine 运行环境,只要当前使用内核支持 binder 模块即可继续(安装前会进行检测,如果用 2.0.0/2.0.1 安装了 5.17.3 内核也可以继续使用该内核);",
"※4、修复 uengine-loading-ubuntu 的问题(参考 #I6CKHB:Ubuntu 22.04 LTS安装运行器并按提示安装UEngine后UEngine服务持续崩溃无法使用 )以及 binder 模块调用问题。",
"",
"<b>V2.0.1</b>",
"※1、新增移除在 Ubuntu 上安装的 UEngine 及其附属脚本的功能",
"※2、优化 Ubuntu 安装器启用和安装逻辑,不允许在 UOS/Deepin 安装移植的 Ubuntu UEngine",
@ -247,6 +281,7 @@
"Contribute": [
"<b>感谢以下用户提供的问题、建议、图标、代码等,如果有遗漏,请及时与开发者联系添加,以及如果侵犯到您的合法权益,也及时与开发者联系:</p>",
"<hr>",
"感谢 @鹤舞白沙 优化了程序文案",
"感谢 @Bail 修复了修复应用商店换源的问题,实现了微型应用商店更新关闭进程的功能",
"感谢 RacoonGX 团队的付出和贡献",
"感谢 cleverwwh 反馈的 UEngine 打包器在部分系统无法打开的问题",

@ -89,8 +89,8 @@ class UninstallProgram(QtCore.QThread):
self.error.emit("疑似卸载失败,请检查 UEngine 是否正常安装、运行以及 APK 文件或包名是否正确、完整")
DisabledAndEnbled(False)
return
if os.path.exists("{}/.local/share/applications/uengine/{}.desktop".format(get_home(), package)):
os.remove("{}/.local/share/applications/uengine/{}.desktop".format(get_home(), package))
if os.path.exists("{}/{}.desktop".format(desktopFilePath, package)):
os.remove("{}/{}.desktop".format(desktopFilePath, package))
if os.path.exists("{}/{}.desktop".format(get_desktop_path(), package)):
os.remove("{}/{}.desktop".format(get_desktop_path(), package))
findApkHistory.append(ComboInstallPath.currentText())
@ -174,9 +174,9 @@ class InstallApk(QtCore.QThread):
try:
if not os.path.exists("/tmp/uengine-runner"):
os.makedirs("/tmp/uengine-runner")
if not os.path.exists("{}/.local/share/applications/uengine/".format(get_home())):
if not os.path.exists(desktopFilePath):
print("Mkdir")
os.makedirs("{}/.local/share/applications/uengine/".format(get_home()))
os.makedirs(desktopFilePath)
# 读取设置
setting = json.loads(readtxt(get_home() + "/.config/uengine-runner/setting.json"))
# 安装应用
@ -246,7 +246,7 @@ logicalHeight {verticalHeighe}
"{}/{}.desktop".format(get_desktop_path(), GetApkPackageName(path)))
print("start install apk3")
BuildUengineDesktop(GetApkPackageName(path), GetApkActivityName(path), GetApkChineseLabel(path), iconSavePath,
"{}/.local/share/applications/uengine/{}.desktop".format(get_home(), GetApkPackageName(path)))
"{}/{}.desktop".format(desktopFilePath, GetApkPackageName(path)))
print("\nprint install complete")
if quit:
return
@ -266,7 +266,7 @@ def InstallBuildDesktop(iconSavePath):
"{}/{}.desktop".format(get_desktop_path(), GetApkPackageName(path)), choose)
print("start install apk3")
BuildUengineDesktop(GetApkPackageName(path), GetApkActivityName(path), GetApkChineseLabel(path), iconSavePath,
"{}/.local/share/applications/uengine/{}.desktop".format(get_home(), GetApkPackageName(path)), choose)
"{}/{}.desktop".format(desktopFilePath, GetApkPackageName(path)), choose)
print("\nprint install complete")
def UpdateCombobox(tmp):
@ -418,7 +418,7 @@ def write_txt(path: "路径", things: "内容")->"写入文本文档":
# 获取 aapt 的所有信息
def GetApkInformation(apkFilePath: "apk 所在路径")->"获取 aapt 的所有信息":
return GetCommandReturn("aapt dump badging '{}'".format(apkFilePath))
return GetCommandReturn("'{}/aapt/run-aapt.sh' dump badging '{}'".format(programPath, apkFilePath))
# 获取 apk Activity
def GetApkActivityName(apkFilePath: "apk 所在路径")->"获取 apk Activity":
@ -587,8 +587,8 @@ def BackUengineClean()->"清空 uengine 数据":
if QtWidgets.QMessageBox.warning(widget, "警告", "清空后数据将会完全丢失,确定要继续吗?", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) == QtWidgets.QMessageBox.Ok:
DisabledAndEnbled(True)
try:
if os.path.exists("{}/.local/share/applications/uengine/".format(get_home())):
shutil.rmtree("{}/.local/share/applications/uengine/".format(get_home()))
if os.path.exists(desktopFilePath):
shutil.rmtree(desktopFilePath)
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", traceback.format_exc())
@ -1004,7 +1004,7 @@ class UengineWindowSizeSetting:
i.setChecked(True)
UengineWindowSizeSetting.setting.setWindowTitle(f"设置 Android 应用的窗口大小缩放设置")
UengineWindowSizeSetting.setting.show()
UengineWindowSizeSetting.setting.resize(UengineWindowSizeSetting.setting.frameSize().width() * 1.3, UengineWindowSizeSetting.setting.frameSize().height())
UengineWindowSizeSetting.setting.resize(int(UengineWindowSizeSetting.setting.frameSize().width() * 1.3), int(UengineWindowSizeSetting.setting.frameSize().height()))
def ReadSetting():
file = open(f"/usr/share/uengine/appetc/{UengineWindowSizeSetting.package}.txt")
@ -1153,7 +1153,7 @@ class UpdateWindow():
cancel.clicked.connect(UpdateWindow.update.close)
ok.setDisabled(True)
try:
UpdateWindow.data = json.loads(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-update-information/raw/branch/master/uengine-runner/update.json").text)
UpdateWindow.data = json.loads(requests.get("http://update.gfdgdxi.top/uengine-runner/update.json").text)
versionLabel = QtWidgets.QLabel(f"当前版本:{version}\n最新版本:{UpdateWindow.data['Version']}\n更新内容:")
if UpdateWindow.data["Version"] == version:
updateText.setText("此为最新版本,无需更新")
@ -1191,7 +1191,7 @@ class UpdateWindow():
UpdateWindow.update.setCentralWidget(updateWidget)
UpdateWindow.update.setWindowTitle("检查 UEngine 运行器更新")
UpdateWindow.update.setWindowIcon(QtGui.QIcon(iconPath))
UpdateWindow.update.resize(updateWidget.frameGeometry().width(), int(updateWidget.frameGeometry().height() * 1.5))
UpdateWindow.update.resize(int(updateWidget.frameGeometry().width()), int(updateWidget.frameGeometry().height() * 1.5))
UpdateWindow.update.show()
def Update():
@ -1324,7 +1324,7 @@ class ApkInformation():
QtWidgets.QMessageBox.critical(widget, "错误", langFile[lang]["Main"]["MainWindow"]["Error"]["ApkFileError"])
return
try:
data = json.loads(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-downloads-of-runner/raw/branch/master/uengineapp/" + package +"/data.json").text)
data = json.loads(requests.get("http://data.download.gfdgdxi.top/uengineapp/" + package +"/data.json").text)
print(data)
except:
QtWidgets.QMessageBox.information(widget, "提示", "此程序暂时没有评分,欢迎您贡献第一个评分!")
@ -1410,7 +1410,7 @@ class ShowTextTipsWindow():
ShowTextTipsWindow.messageWindow.setWindowTitle("提示")
ShowTextTipsWindow.messageWindow.setWindowIcon(QtGui.QIcon(iconPath))
ShowTextTipsWindow.messageWindow.show()
ShowTextTipsWindow.messageWindow.resize(ShowTextTipsWindow.messageWindow.frameSize().width() * 2, ShowTextTipsWindow.messageWindow.frameSize().height() * 1.5)
ShowTextTipsWindow.messageWindow.resize(int(ShowTextTipsWindow.messageWindow.frameSize().width() * 2), int(ShowTextTipsWindow.messageWindow.frameSize().height() * 1.5))
return
# 添加/删除 uengine 应用快捷方式
@ -1462,7 +1462,7 @@ class AddNewUengineDesktopLink():
# 添加快捷方式
def SaveDesktopLink():
try:
if os.path.exists("{}/.local/share/applications/uengine/{}.desktop".format(get_home(), packageName.text())):
if os.path.exists("{}/{}.desktop".format(desktopFilePath, packageName.text())):
if QtWidgets.QMessageBox.question(widget, "提示", "文件已存在,是否要覆盖?") == QtWidgets.QMessageBox.No:
return
if not os.path.exists("{}/.local/share/icons/hicolor/256x256/apps/".format(get_home())):
@ -1470,9 +1470,9 @@ class AddNewUengineDesktopLink():
global activityName
iconSavePath = "{}/.local/share/icons/hicolor/256x256/apps/{}.png".format(get_home(), packageName.text())
shutil.copy(programPath + "/defult.png", iconSavePath)
BuildUengineDesktop(packageName.text(), activityName, packageName.text(), iconSavePath,
"{}/.local/share/applications/uengine/{}.desktop".format(get_home(), packageName.text()))
BuildUengineDesktop(packageName.text(), activityName, packageName.text(), iconSavePath,
BuildUengineDesktop(packageName.text(), activityName.text(), packageName.text(), iconSavePath,
"{}/{}.desktop".format(desktopFilePath, packageName.text()))
BuildUengineDesktop(packageName.text(), activityName.text(), packageName.text(), iconSavePath,
"{}/{}.desktop".format(get_desktop_path(), packageName.text()))
AddNewUengineDesktopLink.SaveHistory()
QtWidgets.QMessageBox.information(widget, "提示", "创建完毕!")
@ -1485,13 +1485,13 @@ class AddNewUengineDesktopLink():
def DelDesktopLink():
try:
global packageName
if not os.path.exists("{}/.local/share/applications/uengine/{}.desktop".format(get_home(), packageName.text())):
QtWidgets.QMessageBox.critical(widget, "错误", "此包名对应的 UEngine 桌面快捷方式不存在!")
if not os.path.exists("{}/{}.desktop".format(desktopFilePath, packageName.text())):
QtWidgets.QMessageBox.critical(widget, "错误", "此包名对应的 UEngine 快捷方式不存在!")
return
if QtWidgets.QMessageBox.warning(widget, "警告", "你确定要删除吗?删除后将无法恢复!", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) == QtWidgets.QMessageBox.Cancel:
return
try:
os.remove("{}/.local/share/applications/uengine/{}.desktop".format(get_home(), packageName.text()))
os.remove("{}/{}.desktop".format(desktopFilePath, packageName.text()))
AddNewUengineDesktopLink.SaveHistory()
QtWidgets.QMessageBox.information(widget, "提示", "已删除")
except:
@ -1524,7 +1524,8 @@ class AddNewUengineDesktopLink():
def GetNewInformation():
try:
text = requests.get("https://code.gitlink.org.cn/gfdgd_xi/uengine-runner-list/raw/branch/master/information/index.html").text
text = requests.get("http://www.gfdgdxi.top/uengine-runner/Help/information/index.html").text
print(text)
except:
traceback.print_exc()
text = """<p>无法连接到服务器</p>
@ -1558,7 +1559,7 @@ def UseProgram():
<p>10deepin 终端{}</p>'''.format(subprocess.getoutput("uengine version"),
subprocess.getoutput("python3 --version"),
QtCore.qVersion,
subprocess.getoutput("aapt version"),
subprocess.getoutput(f"'{programPath}/aapt/run-aapt.sh' version"),
subprocess.getoutput("dpkg --version"),
subprocess.getoutput("mkdir --version"),
subprocess.getoutput("chmod --version"),
@ -1569,6 +1570,26 @@ def BackAPK(choice):
global choose
choose = choice
def InstallUEnginePatchForWayland():
if os.system("which uengine"):
QtWidgets.QMessageBox.critical(window, "错误", "未安装UEngine请先安装UEngine")
return
if os.path.exists("/usr/bin/uengine-session"):
QtWidgets.QMessageBox.critical(window, "提示", "已安装该补丁,请勿重复安装")
return
os.system(f"pkexec bash '{programPath}/LoadingBinder/uengine-wayland-install.sh'")
QtWidgets.QMessageBox.information(window, "提示", "安装成功!重启电脑后生效")
def RemoveUEnginePatchForWayland():
if os.system("which uengine"):
QtWidgets.QMessageBox.critical(window, "错误", "未安装UEngine请先安装UEngine")
return
if not os.path.exists("/usr/bin/uengine-session"):
QtWidgets.QMessageBox.critical(window, "提示", "已卸载该补丁,无需卸载")
return
os.system(f"pkexec bash '{programPath}/LoadingBinder/uengine-wayland-uninstall.sh'")
QtWidgets.QMessageBox.information(window, "提示", "卸载成功!重启电脑后生效")
###########################
# 程序信息
###########################
@ -1581,11 +1602,10 @@ if not lang in langFile.keys():
programUrl = information["Url"][0]
version = information["Version"]
goodRunSystem = information["System"]
aaptVersion = GetCommandReturn("aapt version")
aaptVersion = GetCommandReturn(f"'{programPath}/aapt/run-aapt.sh' version")
SystemVersion = GetSystemVersion()
iconPath = "{}/runner.svg".format(os.path.split(os.path.realpath(__file__))[0])
about = f'''<p align="center"><img width=256 src="{iconPath}"/></p>
<p>介绍一个基于 Python3 PyQt5 制作的 UEngine 运行器在新版本Deepin/UOS发布后可以在应用商店安装部分官方已适配的安卓应用对爱好者来说不能自己安装APK软件包始终差点意思本程序可以为Deepin/UOS上的UEngine安卓运行环境安装自定义APK软件包并能发送安装的APK包启动菜单到桌面或系统菜单</p>
about = f'''<p>介绍虽然通过Deepin/UOS应用商店已经能够安装部分安卓应用但对于安卓应用爱好者来说不能自由地安装任意APK软件包实在是不尽如人意。本软件可以实现在Deepin/UOS上安装任意APK软件包并能将其启动图标发送到系统桌面或启动器中方便用户快速启动它。 </p>
<p>程序开源许可证GPLV3</p>
<p>版本{version}</p>
<p>适用平台{goodRunSystem}</p>
@ -1593,10 +1613,22 @@ about = f'''<p align="center"><img width=256 src="{iconPath}"/></p>
<p>程序官网{programUrl}</p>
<p>系统版本{SystemVersion}</p>
<p>安装包构建时间{information['Time']}</p>
<p>QQ 交流群872491938</p>
<h1>©2021-{time.strftime("%Y")} gfdgd xi</h1>'''
updateThingsString = ""
tips = ""
contribute = ""
appreciate = f"""<h3>请作者喝杯茶</h3>
<p>如果您觉得 UEngine 运行器对你有帮助可以请作者喝杯茶 </p>
<p>
<img src="{programPath}/Icon/QR/Wechat.png" width="250" />
<img src="{programPath}/Icon/QR/Alipay.jpg" width="250" />
<img src="{programPath}/Icon/QR/QQ.png" width="250" >
</p>
<hr/>
<h3>广告</h3>
<p>支付宝官方活动扫描获得支付红包</p>
<p><img src="{programPath}/Icon/QR/advertisement0.jpg" width="250" ></p>"""
for i in information["Tips"]:
tips += f"<p>{i}</p>"
for i in information["Update"]:
@ -1610,13 +1642,25 @@ desktop = programPath + "/UengineAndroidProgramList.desktop"
desktopName = "UengineAndroidProgramList.desktop"
useProgram = ""
threading.Thread(target=UseProgram).start()
isDeepin23=False
# 判断是不是 Deepin23
if os.path.exists("/etc/deepin_version"):
try:
with open(f"/etc/deepin_version") as file:
isDeepin23 = "23" in file.read()
except:
traceback.print_exc()
desktopFilePath = f"{get_home()}/.local/share/applications/uengine/"
if isDeepin23:
desktopFilePath = f"{get_home()}/.local/share/applications/"
###########################
# 加载配置
###########################
app = QtWidgets.QApplication(sys.argv)
if not os.path.exists("{}/.local/share/applications/uengine/".format(get_home())):
os.makedirs("{}/.local/share/applications/uengine/".format(get_home()))
if not os.path.exists(desktopFilePath):
os.makedirs(desktopFilePath)
if not os.path.exists(get_home() + "/.config/uengine-runner"): # 如果没有配置文件夹
os.makedirs(get_home() + "/.config/uengine-runner") # 创建配置文件夹
if not os.path.exists(get_home() + "/.config/uengine-runner/FindApkHistory.json"): # 如果没有配置文件
@ -1696,10 +1740,10 @@ except:
windowflag = "close"
def Open():
try:
lists = json.loads(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-downloads-of-runner/raw/branch/master/Open-UEngine/lists.json").text)
lists = json.loads(requests.get("https://data.download.gfdgdxi.top/Open-UEngine/lists.json").text)
data = []
for i in lists:
data.append(int(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-downloads-of-runner/raw/branch/master/Open-UEngine/{}.txt".format(i)).text))
data.append(int(requests.get("https://data.download.gfdgdxi.top/Open-UEngine/{}.txt".format(i)).text))
except:
QtWidgets.QMessageBox.critical(widget, "错误", "服务器出错!数据获取失败!")
return
@ -1716,11 +1760,12 @@ def Open():
def Download():
try:
lists = json.loads(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-downloads-of-runner/raw/branch/master/Install-UEngine/lists.json").text)
lists = json.loads(requests.get("https://data.download.gfdgdxi.top/Install-UEngine/lists.json").text)
data = []
for i in lists:
data.append(int(requests.get("https://code.gitlink.org.cn/gfdgd_xi/wine-runner-downloads-of-runner/raw/branch/master/Install-UEngine/{}.txt".format(i)).text))
data.append(int(requests.get("https://data.download.gfdgdxi.top/Install-UEngine/{}.txt".format(i)).text))
except:
traceback.print_exc()
QtWidgets.QMessageBox.critical(widget, "错误", "服务器出错!数据获取失败!")
return
fig = matplotlib.pylab.figure()
@ -1743,7 +1788,16 @@ def showhelp():
def ChgLog():
HelpStr.setHtml(updateThingsString)
def ChgAbout(event):
HelpStr.setHtml(about)
HelpStr.setHtml(f"<p align='center'><a href='https://www.gfdgdxi.top/ChangeIcon'><img width=256 src='{iconPath}'/></a></p>\n" + about)
def OpenUrl(url):
print(url.url())
if url.url() == "https://www.gfdgdxi.top/ChangeIcon":
ChgAboutChangeIcon()
return
webbrowser.open_new_tab(url.url())
def ChgAboutChangeIcon():
HelpStr.setHtml(f"<p align='center'><img width=256 src='{programPath}/Icon/Program/about-icon.png'/></p>\n" + about)
def ChgDep():
if useProgram == "":
BtnZujian.setDisabled(True)
@ -1753,7 +1807,8 @@ def showhelp():
HelpStr.setHtml(contribute)
def ChgTips():
HelpStr.setHtml(tips)
def ChgAppreciate():
HelpStr.setHtml(appreciate)
def ChgGPLV3():
try:
with open(f"{programPath}/LICENSE", "r") as file:
@ -1776,7 +1831,12 @@ def showhelp():
BtnDownN = QtWidgets.QPushButton("程序下载量")
BtnOpenN = QtWidgets.QPushButton("程序打开量")
BtnGPLV3 = QtWidgets.QPushButton("程序开源许可证")
appreciateButton = QtWidgets.QPushButton("赞赏作者")
HelpStr = QtWidgets.QTextBrowser()
HelpStr.setOpenLinks(False)
HelpStr.setHtml(about)
HelpStr.setOpenExternalLinks(False)
HelpStr.anchorClicked.connect(OpenUrl)
# 此功能从 2.0.0 后不再隐藏
#BtnDownN.setEnabled("--彩蛋" in sys.argv)
BtnReadme.clicked.connect(ChgTips)
@ -1787,6 +1847,7 @@ def showhelp():
BtnDownN.clicked.connect(Download)
BtnGPLV3.clicked.connect(ChgGPLV3)
BtnOpenN.clicked.connect(Open)
appreciateButton.clicked.connect(ChgAppreciate)
ChgTips()
@ -1797,14 +1858,18 @@ def showhelp():
helpLayout.addWidget(BtnDownN, 4, 0, 1, 1)
helpLayout.addWidget(BtnOpenN, 5, 0, 1, 1)
helpLayout.addWidget(BtnGPLV3, 6, 0, 1, 1)
helpLayout.addWidget(BtnAbout, 7, 0, 1, 1)
helpLayout.addWidget(HelpStr, 0, 1, 10, 1)
helpLayout.addWidget(appreciateButton, 7, 0, 1, 1)
helpLayout.addWidget(BtnAbout, 8, 0, 1, 1)
helpLayout.addWidget(HelpStr, 0, 1, 11, 1)
helpWidget.setLayout(helpLayout)
helpWindow.setCentralWidget(helpWidget)
helpWindow.setFixedSize(int(helpWindow.frameSize().width() * 0.9), int(helpWindow.frameSize().height() * 1.5))
helpWindow.setWindowTitle("帮助")
helpWindow.setWindowIcon(QtGui.QIcon(iconPath))
# 设置背景
helpWindow.setObjectName("helpWindow")
helpWindow.setStyleSheet(f"QWidget#helpWindow {{background: url({programPath}/Icon/Program/about-background.png) no-repeat;background-position: left bottom;}}")
helpWindow.show()
return
@ -1814,6 +1879,9 @@ def showhelp():
if not os.path.exists("/usr/bin/uengine"):
# Deepin/UOS 用户
if "deepin" in SystemVersion.lower() or "uos" in SystemVersion.lower() or subprocess.getoutput("arch").replace("\n", "").replace(" ", "") != "x86_64":
if not "ft-" in GetCommandReturn("lscpu").lower() and GetCommandReturn("lscpu").replace(" ", "").replace("\n", "") == "aarch64":
QtWidgets.QMessageBox.critical(None, "错误", "UEngine 运行器不支持非飞腾 CPU")
sys.exit(1)
if QtWidgets.QMessageBox.question(None, "提示", "您的电脑没有安装 UEngine是否安装 UEngine 以便更好的使用\n安装完后重新启动该程序即可") == QtWidgets.QMessageBox.Yes:
OpenTerminal(f"pkexec apt install uengine -y")
sys.exit(0)
@ -1823,15 +1891,24 @@ if not os.path.exists("/usr/bin/uengine"):
#QtWidgets.QMessageBox.critical(None, "错误", "请安装 UEngine 后继续")
#sys.exit(0)
#if QtWidgets.QMessageBox.question(None, "提示", "您的电脑没有安装 UEngine是否安装 UEngine 以便更好的使用\n这里将会使用 shenmo 提供的脚本进行安装\n安装完后重新启动该程序即可\n提示无法保证此安装脚本安装的 UEngine 可以使用") == QtWidgets.QMessageBox.Yes:
if QtWidgets.QMessageBox.question(None, "提示", "您的电脑没有安装 UEngine是否安装 UEngine 以便更好的使用\n这里将会安装移植版本(之前由 Shenmo 编写)\n安装完后重新启动该程序即可\n提示:需要在 Ubuntu 22.04 及以上版本才能正常安装") == QtWidgets.QMessageBox.Yes:
if QtWidgets.QMessageBox.question(None, "提示", "您的电脑没有安装 UEngine是否安装 UEngine 以便更好的使用\n这里将会安装移植版本(之前由 Shenmo 编写)\n安装完后重新启动该程序即可\n提示:需要在 Ubuntu 20.04 或 debian10 及以上版本才能正常安装\nUbuntu 20.04 和 debian10 需要手动安装 dtk≥5.5),可以从 http://dtk.gfdgdxi.top 获取") == QtWidgets.QMessageBox.Yes:
#os.system(f"'{programPath}/launch.sh' deepin-terminal -C \"bash '{programPath}/uengine-installer'\"")
OpenTerminal(f"bash '{programPath}/uengine-installer'")
sys.exit(0)
###########################
# 窗口创建
###########################
window = QtWidgets.QMainWindow()
# 判断系统是不是 Deepin 23、有没有安装 Wayland 补丁、是不是 Wayland 环境
if not os.path.exists("/usr/bin/uengine-session") and isDeepin23 and os.getenv("XDG_SESSION_TYPE") == "wayland":
# 如果是
if QtWidgets.QMessageBox.question(None, "提示", "检测到您使用的是 Deepin 23 + Wayland 环境,建议安装 UEngine For Wayland 补丁以便能正常使用 UEngine是否安装") == QtWidgets.QMessageBox.Yes:
InstallUEnginePatchForWayland()
widget = QtWidgets.QWidget()
widgetLayout = QtWidgets.QGridLayout()
# 权重
@ -1973,9 +2050,12 @@ uengineCheckCpu = QtWidgets.QAction(QtGui.QIcon.fromTheme("cpu"), langFile[lang]
uengineDeleteUengineCheck = QtWidgets.QAction(QtWidgets.QApplication.style().standardIcon(40), langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][9])
uengineReinstall = QtWidgets.QAction(langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][10])
uengineUbuntuInstall = QtWidgets.QAction(QtGui.QIcon.fromTheme("ubuntu-logo-icon"), langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][14])
uengineUbuntuRemove = QtWidgets.QAction(QtGui.QIcon.fromTheme("ubuntu-logo-icon"), "移除在 Ubuntu 上安装的 UEngine 及其附属脚本")
uengineUbuntuInstallRoot = QtWidgets.QAction(QtGui.QIcon.fromTheme("ubuntu-logo-icon"), "在 Ubuntu 上安装 UEngineSuperSU 镜像)")
uengineUbuntuRemove = QtWidgets.QAction(QtGui.QIcon.fromTheme("ubuntu-logo-icon"), "移除在 Ubuntu/Debian 上安装的 UEngine 及其附属脚本")
uengineUbuntuInstallRoot = QtWidgets.QAction(QtGui.QIcon.fromTheme("ubuntu-logo-icon"), "在 Ubuntu/Debian 上安装 UEngineSuperSU 镜像)")
uengineWindowSizeSetting = QtWidgets.QAction(langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][16])
uengineInstallVia = QtWidgets.QAction("安装 Via")
installUEnginePatchForWayland = QtWidgets.QAction("安装 UEngine For Wayland 补丁")
uninstallUEnginePatchForWayland = QtWidgets.QAction("卸载 UEngine For Wayland 补丁")
uengine.addAction(uengineOpenDebBuilder)
uengine.addAction(uengineOpenDebBuilderMore)
uengine.addAction(uengineKeyboardToMouse)
@ -2001,6 +2081,12 @@ uengine.addSeparator()
uengine.addAction(uengineDeleteUengineCheck)
uengine.addAction(uengineReinstall)
uengineRoot = uengine.addMenu(langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][11]["Name"])
uengine.addSeparator()
uengine.addAction(uengineInstallVia)
uengine.addSeparator()
uengine.addAction(installUEnginePatchForWayland)
uengine.addAction(uninstallUEnginePatchForWayland)
#uengineUbuntuInstall.setDisabled(True)
# 绑定信号
uengineAllowOrDisallowUpdateAndroidApp.triggered.connect(AllowOrDisallowUpdateAndroidApp)
@ -2015,6 +2101,14 @@ uengineUbuntuInstallRoot.triggered.connect(UengineUbuntuInstallRoot)
uengineDeleteUengineCheck.triggered.connect(DelUengineCheck)
uengineReinstall.triggered.connect(ReinstallUengine)
uengineWindowSizeSetting.triggered.connect(UengineWindowSizeSetting.ShowWindow)
installUEnginePatchForWayland.triggered.connect(InstallUEnginePatchForWayland)
uninstallUEnginePatchForWayland.triggered.connect(RemoveUEnginePatchForWayland)
def InstallVia():
ComboInstallPath.setCurrentText(f"{programPath}/APK/Via.apk")
Button3Install()
uengineInstallVia.triggered.connect(InstallVia)
uengineStart = QtWidgets.QAction(QtGui.QIcon.fromTheme("services"), langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][2]["Menu"][0])
uengineStop = QtWidgets.QAction(QtGui.QIcon.fromTheme("services"), langFile[lang]["Main"]["MainWindow"]["Menu"][2]["Menu"][2]["Menu"][1])

@ -1,25 +1,14 @@
Package: com.gitee.uengine.runner.spark
Source: com.gitee.uengine.runner.spark
Replaces: spark-uengine-runner, com.gitee.uengine.runner.spark.ubuntu
Version: 2.0.1
Version: 2.2.0.1
Architecture: all
Section: utils
Installed-Size: 2920
Installed-Size: 9384
Maintainer: gfdgd xi <3025613752@qq.com>
Depends: python3, python3-tk, python3-pip, aapt, python3-setuptools, deepin-terminal | mate-terminal | gnome-terminal | xfce4-terminal, curl, python3-pil, python3-requests, adb, fonts-noto-cjk, python3-numpy, wget, inotify-tools, aria2, python3-pyqt5, python3-matplotlib, python3-urllib3
Recommends: uengine, deepin-elf-verify, python3-pyqt5.qtwebengine
Depends: python3, python3-tk, python3-pip, python3-setuptools, deepin-terminal | mate-terminal | gnome-terminal | xfce4-terminal, curl, python3-pil, python3-requests, adb, fonts-noto-cjk, python3-numpy, wget, aria2, python3-pyqt5, python3-matplotlib, python3-urllib3, zenity, python3-pil.imagetk
Recommends: uengine, deepin-elf-verify, python3-pyqt5.qtwebengine, aapt
Priority: optional
Conflicts: spark-uengine-apk-builder, com.gitee.uengine.runner.spark.ubuntu
Conflicts: spark-uengine-apk-builder, com.gitee.uengine.runner.spark.ubuntu, kbox
Homepage: https://gitee.com/gfdgd-xi/uengine-runner
Description: UEngine 运行器2.0.1 更新内容:
※1、新增移除在 Ubuntu 上安装的 UEngine 及其附属脚本的功能
※2、优化 Ubuntu 安装器启用和安装逻辑,不允许在 UOS/Deepin 安装移植的 Ubuntu UEngine
※3、Ubuntu 安装器不允许在非 X86 架构上使用
※4、在升级/安装运行器的 deb 时会自动移除安装错误的 UEngine 生成的脚本
※5、Ubuntu 安装器进行环境监测
※6、修复应用商店换源问题由@Bail修复
6、修改作者信息
7、更新使用帮助
8、关于窗口修复系统失败错误的问题并新增安装包构建时间
9、移除赞赏功能
10、微型应用商店适配了更新关闭进程由@Bail实现
Description: 新版本Deepin/UOS发布后可以在应用商店安装部分官方已适配的安卓应用对爱好者来说不能自己安装APK软件包始终差点意思本程序可以为Deepin/UOS上的Uengine安卓运行环境安装/卸载/打包自定义APK软件包并能发送安装的APK包启动菜单到桌面或系统菜单。

@ -1,10 +1,10 @@
#!/bin/bash
# 使用 pip 安装所需库
echo 安装组件
python3 -m pip install --upgrade pip --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple
python3 -m pip install --upgrade ttkthemes --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple
python3 -m pip install --upgrade pyautogui --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple
python3 -m pip install --upgrade keyboard --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple
python3 -m pip install --upgrade ttkthemes --break-system-packages --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple > /dev/null 2>&1 | true
python3 -m pip install --upgrade pyautogui --break-system-packages --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple > /dev/null 2>&1 | true
python3 -m pip install --upgrade keyboard --break-system-packages --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple > /dev/null 2>&1 | true
python3 -m pip install --upgrade pillow --break-system-packages --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple > /dev/null 2>&1 | true
echo 执行完成
# 建立软链接
ln -s /opt/apps/com.gitee.uengine.runner.spark/files/uengine-runner /usr/bin/uengine-runner
@ -35,28 +35,39 @@ if [[ $? != 0 ]] && [[ -f /usr/bin/uengine-loading-ubuntu ]]; then
done
fi
# 更新 uengine-loading-ubuntu如果存在
if [[ -f /usr/bin/uengine-loading-ubuntu ]]; then
if [[ -f /usr/bin/uengine-loading-ubuntu ]] || [[ -f /usr/bin/uengine-loading-binder ]]; then
echo 更新 uengine-loading-ubuntu
cp /opt/apps/com.gitee.uengine.runner.spark/files/LoadingBinder/load.sh /usr/bin/uengine-loading-binder
if [ -f /tmp/upgrade-uengine-loading-ubuntu ]; then
rm /tmp/upgrade-uengine-loading-ubuntu
fi
# 下载新版
aria2c -x 16 -s 16 -d /tmp -o upgrade-uengine-loading-ubuntu https://code.gitlink.org.cn/gfdgd_xi/uengine-installer-bak/raw/branch/master/run_daemon.sh
aria2c -x 16 -s 16 -d /tmp -o upgrade-uengine-loading-ubuntu http://uengine-runner.gfdgdxi.top/shell/run_daemon.sh
if [ $? == 0 ]; then
# 替换
cp /tmp/upgrade-uengine-loading-ubuntu /usr/bin/uengine-loading-ubuntu
chmod 755 /usr/bin/uengine-loading-ubuntu
for username in $(ls /home)
do
echo /home/$username
cp /usr/bin/uengine-loading-ubuntu /home/$username/uengine-launch/run_daemon.sh
chmod 755 /home/$username/uengine-launch/run_daemon.sh
done
else
cp /opt/apps/com.gitee.uengine.runner.spark/files/Help/shell/run_daemon.sh /usr/bin/uengine-loading-ubuntu
fi
rm /tmp/upgrade-uengine-loading-ubuntu
chmod 755 /usr/bin/uengine-loading-ubuntu
for username in $(ls /home)
do
echo /home/$username
cp /usr/bin/uengine-loading-ubuntu /home/$username/uengine-launch/run_daemon.sh
chmod 755 /home/$username/uengine-launch/run_daemon.sh
done
rm -f /tmp/upgrade-uengine-loading-ubuntu
fi
# 自动往 UEngine 安装 Via如果 UEngine 已存在)
if [[ -f /usr/bin/uengine-session-launch-helper ]]; then
# 忽略错误进行安装
cp /opt/apps/com.gitee.uengine.runner.spark/files/APK/Via.apk /tmp/Via.apk | true
/usr/bin/uengine-session-launch-helper -- uengine install --apk=/tmp/Via.apk | true
rm -f /tmp/Via.apk | true
fi
# 刷新图标缓存
# 因为 Ubuntu 的问题,省略
gtk-update-icon-cache /usr/share/icons/bloom > /dev/null | true
# 向服务器返回安装数加1不显示内容且忽略错误
python3 /opt/apps/com.gitee.uengine.runner.spark/files/Download.py 2.0.1 > /dev/null | true
python3 /opt/apps/com.gitee.uengine.runner.spark/files/Download.py 2.1.2 > /dev/null | true

@ -22,11 +22,16 @@ rm -fr /opt/apps/com.gitee.uengine.runner.spark/files/api/__pycache__
rm -fr /opt/apps/com.gitee.uengine.runner.spark/files/__pycache__
rm -f /usr/bin/uengine-runner-applist-launch.sh
rm -f /usr/bin/uengine-runner-auto-install-bash
rm -f /usr/bin/uengine-loading-ubuntu
#rm -f /usr/bin/uengine-loading-ubuntu
# 删除软件残留,简单粗暴一点直接全部删掉,防止出现警告
# 加判断是为了怕 reinstall 后程序就再也打不开了(除非卸载后重新安装)
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
rm -rf /opt/apps/com.gitee.uengine.runner.spark
# 移除 UEngine For 23 Wayland 补丁
if [[ -f /usr/bin/uengine-session ]]; then
rm -fv /usb/bin/uengine
mv -v /usr/bin/uengine-session /usr/bin/uengine
fi
fi
# 删除软件配置文件只限“purge”
# 配置目录:~/.config/uengine-runner
@ -61,7 +66,11 @@ if [ "$1" = "purge" ]; then
else
echo "非 purge跳过清理"
fi
# 自动卸载 UEngine 的 Via如果 UEngine 已存在)
if [[ -f /usr/bin/uengine-session-launch-helper ]]; then
# 忽略错误进行安装
/usr/bin/uengine-session-launch-helper -- uengine uninstall --pkg=mark.via | true
fi
# 刷新图标缓存
# 因为 Ubuntu 的问题,省略
gtk-update-icon-cache /usr/share/icons/bloom > /dev/null | true

@ -35,9 +35,9 @@ print("""译文:一轮缓缓移动的秋月洒下万里金波,就像那刚
我要乘风飞上万里长空俯视祖国的大好山河还要砍去月中摇曳的桂树枝柯人们说这将使月亮洒下人间的光辉更多""")
print("================================")
urlSourcesList = [
"https://code.gitlink.org.cn/gfdgd_xi/uengine-runner-list/raw/branch/master/auto", # Gitlink
"http://config.uengine-runner.gfdgdxi.top/", # Github
"https://gitee.com/gfdgd-xi/uengine-wine-runner-auto-configuration-script/raw/master/", # Gitee 源
"https://gfdgd-xi.github.io/uengine-wine-runner-auto-configuration-script/", # Github
"https://code.gitlink.org.cn/gfdgd_xi/uengine-runner-list/raw/branch/master/auto", # Gitlink
"http://gfdgdxi.msns.cn/uengine-runner-list/auto/", # 备用源1纯 IPv6 源
"http://gfdgdxi.free.idcfengye.com/uengine-runner-auto-configuration-script/", # 备用源2
"http://gfdgdxi.free.idcfengye.com/uengine-runner-list/auto/", # 备用源 3
@ -146,7 +146,7 @@ class InformationWindow():
about = f"<h1>关于“{choose}”的介绍</h1>\n<p>暂无此程序的介绍</p>"
try:
import requests as r
fenlists = requests.get(base64.b64decode("aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy93aW5lLXJ1bm5lci1kb3dubG9hZHMtb2YtcnVubmVyL3Jhdy9icmFuY2gvbWFzdGVyL0Jhc2hBcHBGZW4v").decode("utf-8") + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8"), timeout=1000).json()
fenlists = requests.get("http://data.download.gfdgdxi.top/BashAppFen/" + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8"), timeout=1000).json()
tipsInfo = ""
except:
fenlists = [0, 0, 0, 0, 0]
@ -166,19 +166,19 @@ class InformationWindow():
if maxHead > 5:
for i in range(end):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/BadStar.svg' width=50>\n"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/BadStar.svg' width=50>\n"
else:
starHtml += f"<img src='{programPath}/Icon/BadStar.svg' width=50>\n"
else:
for i in range(maxHead):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/Star.svg' width=50>\n"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/Star.svg' width=50>\n"
else:
starHtml += f"<img src='{programPath}/Icon/Star.svg' width=50>\n"
head = maxHead
for i in range(head, end):
if webeng:
starHtml += f"<img src='https://code.gitlink.org.cn/gfdgd_xi/deep-wine-runner/raw/branch/main/Icon/UnStar.svg' width=50>"
starHtml += f"<img src='http://wine-runner.gfdgdxi.top/Icon/UnStar.svg' width=50>"
else:
starHtml += f"<img src='{programPath}/Icon/UnStar.svg' width=50>"
about += f"\n<hr/><h1>评分情况</h1>\n<p align='center'>{starHtml}</p>\n<p align='center'>{tipsInfo}</p>"
@ -260,7 +260,7 @@ class ProgramRunStatusShow():
fileName = i[1]
break
try:
fenlists = requests.get(base64.b64decode("aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy93aW5lLXJ1bm5lci1kb3dubG9hZHMtb2YtcnVubmVyL3Jhdy9icmFuY2gvbWFzdGVyL0Jhc2hBcHBGZW4v").decode("utf-8") + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8")).json()
fenlists = requests.get("http://data.download.gfdgdxi.top/BashAppFen/" + urllib.request.quote(fileName) + base64.b64decode("L2FsbC5qc29u").decode("utf-8")).json()
tipsInfo = ""
except:
#traceback.print_exc()
@ -318,7 +318,7 @@ class ProgramRunStatusShow():
else:
# 显示最新的3条评论
try:
all = int(requests.get(f"{base64.b64decode('aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy9iYXNocGlubHVuL3Jhdy9icmFuY2gvbWFzdGVyLw==').decode('utf-8')}{urllib.request.quote(fileName)}/data.txt").text)
all = int(requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/data.txt").text)
now = all - 3
print(all)
if all < 3:
@ -329,7 +329,7 @@ class ProgramRunStatusShow():
uploadList = []
for i in range(all - 1, start - 1, -1):
print(f"{i + 1} 条评论:")
info = requests.get(f"{base64.b64decode('aHR0cHM6Ly9jb2RlLmdpdGxpbmsub3JnLmNuL2dmZGdkLXhpLW9yZy9iYXNocGlubHVuL3Jhdy9icmFuY2gvbWFzdGVyLw==').decode('utf-8')}{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
info = requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
print(info)
uploadList.append([f"用户{i + 1}", i + 1, info])
Add(uploadList)
@ -386,7 +386,7 @@ def UpdateFen():
uploadList = []
for i in range(now + 2, now - 1, -1):
print(f"{i + 1} 条评论:")
info = requests.get(f"https://code.gitlink.org.cn/gfdgd-xi-org/bashpinlun/raw/branch/master/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
info = requests.get(f"http://bashpinlun.gfdgdxi.top/{urllib.request.quote(fileName)}/pf-{i}.txt").text.strip()
print(info)
uploadList.append([f"用户{i + 1}", i + 1, info])
#ProgramRunStatusShow.pingLunLayout.removeItem(ProgramRunStatusShow.pingLunLayout.itemAt(2))
@ -525,7 +525,7 @@ def readtxt(path):
def ChangeSources():
global urlSources
global urlSourcesIndex
sources = [ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action]
sources = [ui.actionGithub, ui.actionGitee, ui.actionGitlink, ui.action_IPv6, ui.action_2, ui.action_3, ui.action]
for i in range(0, len(sources)):
if sources[i].isChecked():
old_urlSources = urlSources #先备份
@ -541,7 +541,7 @@ def ChangeSources():
ui.searchList.setModel(nmodel)
urlSourcesIndex = i
except:
[ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action][urlSourcesIndex].setChecked(True)
#[ui.actionGitlink, ui.actionGitee, ui.actionGithub, ui.action_IPv6, ui.action_2, ui.action_3, ui.action][urlSourcesIndex].setChecked(True)
traceback.print_exc()
QtWidgets.QMessageBox.critical(window, "提示", "无法连接服务器")
urlSources = old_urlSources #如果源不可用则换回来

@ -41,8 +41,8 @@ programEnv = [
["($PROGRAMPATH)", programPath],
["($VERSION)", version],
["($THANK)", thankText],
["($MAKER)", "RacoonGX 团队By gfdgd xi"],
["($COPYRIGHT)", f"©2020~{time.strftime('%Y')} RacoonGX 团队By gfdgd xi"],
["($MAKER)", "gfdgd xi"],
["($COPYRIGHT)", f"©2020~{time.strftime('%Y')} gfdgd xi"],
["($?)", "0"],
["($PLATFORM)", platform.system()],
["($DEBUG)", "1"]

@ -1,5 +1,7 @@
#!/usr/bin/env python3
import os
import sys
import json
import base64
import requests
print("""浣溪沙
@ -8,4 +10,7 @@ print("""浣溪沙
print("")
print("听一支新曲喝一杯美酒,还是去年的天气旧日的亭台,西落的夕阳何时再回来?那花儿落去我也无可奈何,那归来的燕子似曾相识,在小园的花径上独自徘徊。")
print("================================")
print(requests.get(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3VlbmdpbmUtcnVubmVyL0luc3RhbGwucGhwP1ZlcnNpb249").decode("utf-8") + sys.argv[1]).text)
programPath = os.path.split(os.path.realpath(__file__))[0] # 返回 string
with open(f"{programPath}/information.json") as file:
version = json.loads(file.read())["Version"]
print(requests.get(base64.b64decode("aHR0cDovLzEyMC4yNS4xNTMuMTQ0L3VlbmdpbmUtcnVubmVyL0luc3RhbGwucGhwP1ZlcnNpb249").decode("utf-8") + version).text)

@ -0,0 +1,19 @@
{
"esnext": true,
"indent": 2,
"maxlen": 80,
"freeze": true,
"camelcase": true,
"unused": true,
"eqnull": true,
"proto": true,
"supernew": true,
"noyield": true,
"evil": true,
"node": true,
"boss": true,
"expr": true,
"loopfunc": true,
"white": true,
"maxdepth": 4
}

@ -0,0 +1,33 @@
# Contributing
Changes and improvements are more than welcome! Feel free to fork and open a pull request.
Please follow the house rules to have a bigger chance of your contribution being merged.
## House rules
### How to make changes
- To make changes, create a new branch based on `master` (do not create one from `gh-pages` unless strictly necessary) and make them there, then create a Pull Request to master.
`gh-pages` is different from master in that it contains sharing features, analytics and other things that have no direct bearing with the game. `master` is the "pure" version of the game.
- If you want to modify the CSS, please edit the SCSS files present in `style/`: `main.scss` and others. Don't edit the `main.css`, because it's supposed to be generated.
In order to compile your SCSS modifications, you need to use the `sass` gem (install it by running `gem install sass` once Ruby is installed).
To run SASS, simply use the following command:
`sass --unix-newlines --watch style/main.scss`
SASS will automatically recompile your css when changed.
- `Rakefile` contains some tasks that help during development. Feel free to add useful tasks if needed.
- Please use 2-space indentation when editing the JavaScript. A `.jshintrc` file is present, which will help your code to follow the guidelines if you install and run `jshint`.
- Please test your modification thoroughly before submitting your Pull Request.
### Changes that might not be accepted
We have to be conservative with the core game. This means that some modifications won't be merged, or will have to be evaluated carefully before being merged:
- Undo/redo features
- Save/reload features
- Changes to how the tiles look or their contents
- Changes to the layout
- Changes to the grid size
### Changes that are welcome
- Bug fixes
- Compatibility improvements
- "Under the hood" enhancements
- Small changes that don't have an impact on the core gameplay

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Gabriele Cirulli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,38 @@
# 2048
A small clone of [1024](https://play.google.com/store/apps/details?id=com.veewo.a1024), based on [Saming's 2048](http://saming.fr/p/2048/) (also a clone).
Made just for fun. [Play it here!](http://gabrielecirulli.github.io/2048/)
The official app can also be found on the [Play Store](https://play.google.com/store/apps/details?id=com.gabrielecirulli.app2048) and [App Store!](https://itunes.apple.com/us/app/2048-by-gabriele-cirulli/id868076805)
### Contributions
[Anna Harren](https://github.com/iirelu/) and [sigod](https://github.com/sigod) are maintainers for this repository.
Other notable contributors:
- [TimPetricola](https://github.com/TimPetricola) added best score storage
- [chrisprice](https://github.com/chrisprice) added custom code for swipe handling on mobile
- [marcingajda](https://github.com/marcingajda) made swipes work on Windows Phone
- [mgarciaisaia](https://github.com/mgarciaisaia) added support for Android 2.3
Many thanks to [rayhaanj](https://github.com/rayhaanj), [Mechazawa](https://github.com/Mechazawa), [grant](https://github.com/grant), [remram44](https://github.com/remram44) and [ghoullier](https://github.com/ghoullier) for the many other good contributions.
### Screenshot
<p align="center">
<img src="https://cloud.githubusercontent.com/assets/1175750/8614312/280e5dc2-26f1-11e5-9f1f-5891c3ca8b26.png" alt="Screenshot"/>
</p>
That screenshot is fake, by the way. I never reached 2048 :smile:
## Contributing
Changes and improvements are more than welcome! Feel free to fork and open a pull request. Please make your changes in a specific branch and request to pull into `master`! If you can, please make sure the game fully works before sending the PR, as that will help speed up the process.
You can find the same information in the [contributing guide.](https://github.com/gabrielecirulli/2048/blob/master/CONTRIBUTING.md)
## License
2048 is licensed under the [MIT license.](https://github.com/gabrielecirulli/2048/blob/master/LICENSE.txt)
## Donations
I made this in my spare time, and it's hosted on GitHub (which means I don't have any hosting costs), but if you enjoyed the game and feel like buying me coffee, you can donate at my BTC address: `1Ec6onfsQmoP9kkL3zkpB6c5sA4PVcXU2i`. Thank you very much!

@ -0,0 +1,11 @@
require "date"
namespace :appcache do
desc "update the date in the appcache file (in the gh-pages branch)"
task :update do
appcache = File.read("cache.appcache")
updated = "# Updated: #{DateTime.now}"
File.write("cache.appcache", appcache.sub(/^# Updated:.*$/, updated))
end
end

Binary file not shown.

After

Width: 32px  |  Height: 32px  |  Size: 4.2 KiB

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>2048</title>
<link href="style/main.css" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="favicon.ico">
<link rel="apple-touch-icon" href="meta/apple-touch-icon.png">
<link rel="apple-touch-startup-image" href="meta/apple-touch-startup-image-640x1096.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)"> <!-- iPhone 5+ -->
<link rel="apple-touch-startup-image" href="meta/apple-touch-startup-image-640x920.png" media="(device-width: 320px) and (device-height: 480px) and (-webkit-device-pixel-ratio: 2)"> <!-- iPhone, retina -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div class="container">
<div class="heading">
<h1 class="title">2048</h1>
<div class="scores-container">
<div class="score-container">0</div>
<div class="best-container">0</div>
</div>
</div>
<div class="above-game">
<p class="game-intro">Join the numbers and get to the <strong>2048 tile!</strong></p>
<a class="restart-button">New Game</a>
</div>
<div class="game-container">
<div class="game-message">
<p></p>
<div class="lower">
<a class="keep-playing-button">Keep going</a>
<a class="retry-button">Try again</a>
</div>
</div>
<div class="grid-container">
<div class="grid-row">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
<div class="grid-row">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
<div class="grid-row">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
<div class="grid-row">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
</div>
<div class="tile-container">
</div>
</div>
<p class="game-explanation">
<strong class="important">How to play:</strong> Use your <strong>arrow keys</strong> to move the tiles. When two tiles with the same number touch, they <strong>merge into one!</strong>
</p>
<hr>
<p>
<strong class="important">Note:</strong> This site is the official version of 2048. You can play it on your phone via <a href="http://git.io/2048">http://git.io/2048.</a> All other apps or sites are derivatives or fakes, and should be used with caution.
</p>
<hr>
<p>
Created by <a href="http://gabrielecirulli.com" target="_blank">Gabriele Cirulli.</a> Based on <a href="https://itunes.apple.com/us/app/1024!/id823499224" target="_blank">1024 by Veewo Studio</a> and conceptually similar to <a href="http://asherv.com/threes/" target="_blank">Threes by Asher Vollmer.</a>
</p>
</div>
<script src="js/bind_polyfill.js"></script>
<script src="js/classlist_polyfill.js"></script>
<script src="js/animframe_polyfill.js"></script>
<script src="js/keyboard_input_manager.js"></script>
<script src="js/html_actuator.js"></script>
<script src="js/grid.js"></script>
<script src="js/tile.js"></script>
<script src="js/local_storage_manager.js"></script>
<script src="js/game_manager.js"></script>
<script src="js/application.js"></script>
</body>
</html>

@ -0,0 +1,28 @@
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
},
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
}());

@ -0,0 +1,4 @@
// Wait till the browser is ready to render the game (avoids glitches)
window.requestAnimationFrame(function () {
new GameManager(4, KeyboardInputManager, HTMLActuator, LocalStorageManager);
});

@ -0,0 +1,9 @@
Function.prototype.bind = Function.prototype.bind || function (target) {
var self = this;
return function (args) {
if (!(args instanceof Array)) {
args = [args];
}
self.apply(target, args);
};
};

@ -0,0 +1,71 @@
(function () {
if (typeof window.Element === "undefined" ||
"classList" in document.documentElement) {
return;
}
var prototype = Array.prototype,
push = prototype.push,
splice = prototype.splice,
join = prototype.join;
function DOMTokenList(el) {
this.el = el;
// The className needs to be trimmed and split on whitespace
// to retrieve a list of classes.
var classes = el.className.replace(/^\s+|\s+$/g, '').split(/\s+/);
for (var i = 0; i < classes.length; i++) {
push.call(this, classes[i]);
}
}
DOMTokenList.prototype = {
add: function (token) {
if (this.contains(token)) return;
push.call(this, token);
this.el.className = this.toString();
},
contains: function (token) {
return this.el.className.indexOf(token) != -1;
},
item: function (index) {
return this[index] || null;
},
remove: function (token) {
if (!this.contains(token)) return;
for (var i = 0; i < this.length; i++) {
if (this[i] == token) break;
}
splice.call(this, i, 1);
this.el.className = this.toString();
},
toString: function () {
return join.call(this, ' ');
},
toggle: function (token) {
if (!this.contains(token)) {
this.add(token);
} else {
this.remove(token);
}
return this.contains(token);
}
};
window.DOMTokenList = DOMTokenList;
function defineElementGetter(obj, prop, getter) {
if (Object.defineProperty) {
Object.defineProperty(obj, prop, {
get: getter
});
} else {
obj.__defineGetter__(prop, getter);
}
}
defineElementGetter(HTMLElement.prototype, 'classList', function () {
return new DOMTokenList(this);
});
})();

@ -0,0 +1,272 @@
function GameManager(size, InputManager, Actuator, StorageManager) {
this.size = size; // Size of the grid
this.inputManager = new InputManager;
this.storageManager = new StorageManager;
this.actuator = new Actuator;
this.startTiles = 2;
this.inputManager.on("move", this.move.bind(this));
this.inputManager.on("restart", this.restart.bind(this));
this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
this.setup();
}
// Restart the game
GameManager.prototype.restart = function () {
this.storageManager.clearGameState();
this.actuator.continueGame(); // Clear the game won/lost message
this.setup();
};
// Keep playing after winning (allows going over 2048)
GameManager.prototype.keepPlaying = function () {
this.keepPlaying = true;
this.actuator.continueGame(); // Clear the game won/lost message
};
// Return true if the game is lost, or has won and the user hasn't kept playing
GameManager.prototype.isGameTerminated = function () {
return this.over || (this.won && !this.keepPlaying);
};
// Set up the game
GameManager.prototype.setup = function () {
var previousState = this.storageManager.getGameState();
// Reload the game from a previous game if present
if (previousState) {
this.grid = new Grid(previousState.grid.size,
previousState.grid.cells); // Reload grid
this.score = previousState.score;
this.over = previousState.over;
this.won = previousState.won;
this.keepPlaying = previousState.keepPlaying;
} else {
this.grid = new Grid(this.size);
this.score = 0;
this.over = false;
this.won = false;
this.keepPlaying = false;
// Add the initial tiles
this.addStartTiles();
}
// Update the actuator
this.actuate();
};
// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
for (var i = 0; i < this.startTiles; i++) {
this.addRandomTile();
}
};
// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
if (this.grid.cellsAvailable()) {
var value = Math.random() < 0.9 ? 2 : 4;
var tile = new Tile(this.grid.randomAvailableCell(), value);
this.grid.insertTile(tile);
}
};
// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
if (this.storageManager.getBestScore() < this.score) {
this.storageManager.setBestScore(this.score);
}
// Clear the state when the game is over (game over only, not win)
if (this.over) {
this.storageManager.clearGameState();
} else {
this.storageManager.setGameState(this.serialize());
}
this.actuator.actuate(this.grid, {
score: this.score,
over: this.over,
won: this.won,
bestScore: this.storageManager.getBestScore(),
terminated: this.isGameTerminated()
});
};
// Represent the current game as an object
GameManager.prototype.serialize = function () {
return {
grid: this.grid.serialize(),
score: this.score,
over: this.over,
won: this.won,
keepPlaying: this.keepPlaying
};
};
// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
this.grid.eachCell(function (x, y, tile) {
if (tile) {
tile.mergedFrom = null;
tile.savePosition();
}
});
};
// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
this.grid.cells[tile.x][tile.y] = null;
this.grid.cells[cell.x][cell.y] = tile;
tile.updatePosition(cell);
};
// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
// 0: up, 1: right, 2: down, 3: left
var self = this;
if (this.isGameTerminated()) return; // Don't do anything if the game's over
var cell, tile;
var vector = this.getVector(direction);
var traversals = this.buildTraversals(vector);
var moved = false;
// Save the current tile positions and remove merger information
this.prepareTiles();
// Traverse the grid in the right direction and move tiles
traversals.x.forEach(function (x) {
traversals.y.forEach(function (y) {
cell = { x: x, y: y };
tile = self.grid.cellContent(cell);
if (tile) {
var positions = self.findFarthestPosition(cell, vector);
var next = self.grid.cellContent(positions.next);
// Only one merger per row traversal?
if (next && next.value === tile.value && !next.mergedFrom) {
var merged = new Tile(positions.next, tile.value * 2);
merged.mergedFrom = [tile, next];
self.grid.insertTile(merged);
self.grid.removeTile(tile);
// Converge the two tiles' positions
tile.updatePosition(positions.next);
// Update the score
self.score += merged.value;
// The mighty 2048 tile
if (merged.value === 2048) self.won = true;
} else {
self.moveTile(tile, positions.farthest);
}
if (!self.positionsEqual(cell, tile)) {
moved = true; // The tile moved from its original cell!
}
}
});
});
if (moved) {
this.addRandomTile();
if (!this.movesAvailable()) {
this.over = true; // Game over!
}
this.actuate();
}
};
// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
// Vectors representing tile movement
var map = {
0: { x: 0, y: -1 }, // Up
1: { x: 1, y: 0 }, // Right
2: { x: 0, y: 1 }, // Down
3: { x: -1, y: 0 } // Left
};
return map[direction];
};
// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
var traversals = { x: [], y: [] };
for (var pos = 0; pos < this.size; pos++) {
traversals.x.push(pos);
traversals.y.push(pos);
}
// Always traverse from the farthest cell in the chosen direction
if (vector.x === 1) traversals.x = traversals.x.reverse();
if (vector.y === 1) traversals.y = traversals.y.reverse();
return traversals;
};
GameManager.prototype.findFarthestPosition = function (cell, vector) {
var previous;
// Progress towards the vector direction until an obstacle is found
do {
previous = cell;
cell = { x: previous.x + vector.x, y: previous.y + vector.y };
} while (this.grid.withinBounds(cell) &&
this.grid.cellAvailable(cell));
return {
farthest: previous,
next: cell // Used to check if a merge is required
};
};
GameManager.prototype.movesAvailable = function () {
return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};
// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
var self = this;
var tile;
for (var x = 0; x < this.size; x++) {
for (var y = 0; y < this.size; y++) {
tile = this.grid.cellContent({ x: x, y: y });
if (tile) {
for (var direction = 0; direction < 4; direction++) {
var vector = self.getVector(direction);
var cell = { x: x + vector.x, y: y + vector.y };
var other = self.grid.cellContent(cell);
if (other && other.value === tile.value) {
return true; // These two tiles can be merged
}
}
}
}
}
return false;
};
GameManager.prototype.positionsEqual = function (first, second) {
return first.x === second.x && first.y === second.y;
};

@ -0,0 +1,117 @@
function Grid(size, previousState) {
this.size = size;
this.cells = previousState ? this.fromState(previousState) : this.empty();
}
// Build a grid of the specified size
Grid.prototype.empty = function () {
var cells = [];
for (var x = 0; x < this.size; x++) {
var row = cells[x] = [];
for (var y = 0; y < this.size; y++) {
row.push(null);
}
}
return cells;
};
Grid.prototype.fromState = function (state) {
var cells = [];
for (var x = 0; x < this.size; x++) {
var row = cells[x] = [];
for (var y = 0; y < this.size; y++) {
var tile = state[x][y];
row.push(tile ? new Tile(tile.position, tile.value) : null);
}
}
return cells;
};
// Find the first available random position
Grid.prototype.randomAvailableCell = function () {
var cells = this.availableCells();
if (cells.length) {
return cells[Math.floor(Math.random() * cells.length)];
}
};
Grid.prototype.availableCells = function () {
var cells = [];
this.eachCell(function (x, y, tile) {
if (!tile) {
cells.push({ x: x, y: y });
}
});
return cells;
};
// Call callback for every cell
Grid.prototype.eachCell = function (callback) {
for (var x = 0; x < this.size; x++) {
for (var y = 0; y < this.size; y++) {
callback(x, y, this.cells[x][y]);
}
}
};
// Check if there are any cells available
Grid.prototype.cellsAvailable = function () {
return !!this.availableCells().length;
};
// Check if the specified cell is taken
Grid.prototype.cellAvailable = function (cell) {
return !this.cellOccupied(cell);
};
Grid.prototype.cellOccupied = function (cell) {
return !!this.cellContent(cell);
};
Grid.prototype.cellContent = function (cell) {
if (this.withinBounds(cell)) {
return this.cells[cell.x][cell.y];
} else {
return null;
}
};
// Inserts a tile at its position
Grid.prototype.insertTile = function (tile) {
this.cells[tile.x][tile.y] = tile;
};
Grid.prototype.removeTile = function (tile) {
this.cells[tile.x][tile.y] = null;
};
Grid.prototype.withinBounds = function (position) {
return position.x >= 0 && position.x < this.size &&
position.y >= 0 && position.y < this.size;
};
Grid.prototype.serialize = function () {
var cellState = [];
for (var x = 0; x < this.size; x++) {
var row = cellState[x] = [];
for (var y = 0; y < this.size; y++) {
row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null);
}
}
return {
size: this.size,
cells: cellState
};
};

@ -0,0 +1,139 @@
function HTMLActuator() {
this.tileContainer = document.querySelector(".tile-container");
this.scoreContainer = document.querySelector(".score-container");
this.bestContainer = document.querySelector(".best-container");
this.messageContainer = document.querySelector(".game-message");
this.score = 0;
}
HTMLActuator.prototype.actuate = function (grid, metadata) {
var self = this;
window.requestAnimationFrame(function () {
self.clearContainer(self.tileContainer);
grid.cells.forEach(function (column) {
column.forEach(function (cell) {
if (cell) {
self.addTile(cell);
}
});
});
self.updateScore(metadata.score);
self.updateBestScore(metadata.bestScore);
if (metadata.terminated) {
if (metadata.over) {
self.message(false); // You lose
} else if (metadata.won) {
self.message(true); // You win!
}
}
});
};
// Continues the game (both restart and keep playing)
HTMLActuator.prototype.continueGame = function () {
this.clearMessage();
};
HTMLActuator.prototype.clearContainer = function (container) {
while (container.firstChild) {
container.removeChild(container.firstChild);
}
};
HTMLActuator.prototype.addTile = function (tile) {
var self = this;
var wrapper = document.createElement("div");
var inner = document.createElement("div");
var position = tile.previousPosition || { x: tile.x, y: tile.y };
var positionClass = this.positionClass(position);
// We can't use classlist because it somehow glitches when replacing classes
var classes = ["tile", "tile-" + tile.value, positionClass];
if (tile.value > 2048) classes.push("tile-super");
this.applyClasses(wrapper, classes);
inner.classList.add("tile-inner");
inner.textContent = tile.value;
if (tile.previousPosition) {
// Make sure that the tile gets rendered in the previous position first
window.requestAnimationFrame(function () {
classes[2] = self.positionClass({ x: tile.x, y: tile.y });
self.applyClasses(wrapper, classes); // Update the position
});
} else if (tile.mergedFrom) {
classes.push("tile-merged");
this.applyClasses(wrapper, classes);
// Render the tiles that merged
tile.mergedFrom.forEach(function (merged) {
self.addTile(merged);
});
} else {
classes.push("tile-new");
this.applyClasses(wrapper, classes);
}
// Add the inner part of the tile to the wrapper
wrapper.appendChild(inner);
// Put the tile on the board
this.tileContainer.appendChild(wrapper);
};
HTMLActuator.prototype.applyClasses = function (element, classes) {
element.setAttribute("class", classes.join(" "));
};
HTMLActuator.prototype.normalizePosition = function (position) {
return { x: position.x + 1, y: position.y + 1 };
};
HTMLActuator.prototype.positionClass = function (position) {
position = this.normalizePosition(position);
return "tile-position-" + position.x + "-" + position.y;
};
HTMLActuator.prototype.updateScore = function (score) {
this.clearContainer(this.scoreContainer);
var difference = score - this.score;
this.score = score;
this.scoreContainer.textContent = this.score;
if (difference > 0) {
var addition = document.createElement("div");
addition.classList.add("score-addition");
addition.textContent = "+" + difference;
this.scoreContainer.appendChild(addition);
}
};
HTMLActuator.prototype.updateBestScore = function (bestScore) {
this.bestContainer.textContent = bestScore;
};
HTMLActuator.prototype.message = function (won) {
var type = won ? "game-won" : "game-over";
var message = won ? "You win!" : "Game over!";
this.messageContainer.classList.add(type);
this.messageContainer.getElementsByTagName("p")[0].textContent = message;
};
HTMLActuator.prototype.clearMessage = function () {
// IE only takes one value to remove at a time.
this.messageContainer.classList.remove("game-won");
this.messageContainer.classList.remove("game-over");
};

@ -0,0 +1,144 @@
function KeyboardInputManager() {
this.events = {};
if (window.navigator.msPointerEnabled) {
//Internet Explorer 10 style
this.eventTouchstart = "MSPointerDown";
this.eventTouchmove = "MSPointerMove";
this.eventTouchend = "MSPointerUp";
} else {
this.eventTouchstart = "touchstart";
this.eventTouchmove = "touchmove";
this.eventTouchend = "touchend";
}
this.listen();
}
KeyboardInputManager.prototype.on = function (event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
};
KeyboardInputManager.prototype.emit = function (event, data) {
var callbacks = this.events[event];
if (callbacks) {
callbacks.forEach(function (callback) {
callback(data);
});
}
};
KeyboardInputManager.prototype.listen = function () {
var self = this;
var map = {
38: 0, // Up
39: 1, // Right
40: 2, // Down
37: 3, // Left
75: 0, // Vim up
76: 1, // Vim right
74: 2, // Vim down
72: 3, // Vim left
87: 0, // W
68: 1, // D
83: 2, // S
65: 3 // A
};
// Respond to direction keys
document.addEventListener("keydown", function (event) {
var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
event.shiftKey;
var mapped = map[event.which];
if (!modifiers) {
if (mapped !== undefined) {
event.preventDefault();
self.emit("move", mapped);
}
}
// R key restarts the game
if (!modifiers && event.which === 82) {
self.restart.call(self, event);
}
});
// Respond to button presses
this.bindButtonPress(".retry-button", this.restart);
this.bindButtonPress(".restart-button", this.restart);
this.bindButtonPress(".keep-playing-button", this.keepPlaying);
// Respond to swipe events
var touchStartClientX, touchStartClientY;
var gameContainer = document.getElementsByClassName("game-container")[0];
gameContainer.addEventListener(this.eventTouchstart, function (event) {
if ((!window.navigator.msPointerEnabled && event.touches.length > 1) ||
event.targetTouches.length > 1) {
return; // Ignore if touching with more than 1 finger
}
if (window.navigator.msPointerEnabled) {
touchStartClientX = event.pageX;
touchStartClientY = event.pageY;
} else {
touchStartClientX = event.touches[0].clientX;
touchStartClientY = event.touches[0].clientY;
}
event.preventDefault();
});
gameContainer.addEventListener(this.eventTouchmove, function (event) {
event.preventDefault();
});
gameContainer.addEventListener(this.eventTouchend, function (event) {
if ((!window.navigator.msPointerEnabled && event.touches.length > 0) ||
event.targetTouches.length > 0) {
return; // Ignore if still touching with one or more fingers
}
var touchEndClientX, touchEndClientY;
if (window.navigator.msPointerEnabled) {
touchEndClientX = event.pageX;
touchEndClientY = event.pageY;
} else {
touchEndClientX = event.changedTouches[0].clientX;
touchEndClientY = event.changedTouches[0].clientY;
}
var dx = touchEndClientX - touchStartClientX;
var absDx = Math.abs(dx);
var dy = touchEndClientY - touchStartClientY;
var absDy = Math.abs(dy);
if (Math.max(absDx, absDy) > 10) {
// (right : left) : (down : up)
self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0));
}
});
};
KeyboardInputManager.prototype.restart = function (event) {
event.preventDefault();
this.emit("restart");
};
KeyboardInputManager.prototype.keepPlaying = function (event) {
event.preventDefault();
this.emit("keepPlaying");
};
KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) {
var button = document.querySelector(selector);
button.addEventListener("click", fn.bind(this));
button.addEventListener(this.eventTouchend, fn.bind(this));
};

@ -0,0 +1,63 @@
window.fakeStorage = {
_data: {},
setItem: function (id, val) {
return this._data[id] = String(val);
},
getItem: function (id) {
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
},
removeItem: function (id) {
return delete this._data[id];
},
clear: function () {
return this._data = {};
}
};
function LocalStorageManager() {
this.bestScoreKey = "bestScore";
this.gameStateKey = "gameState";
var supported = this.localStorageSupported();
this.storage = supported ? window.localStorage : window.fakeStorage;
}
LocalStorageManager.prototype.localStorageSupported = function () {
var testKey = "test";
try {
var storage = window.localStorage;
storage.setItem(testKey, "1");
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
// Best score getters/setters
LocalStorageManager.prototype.getBestScore = function () {
return this.storage.getItem(this.bestScoreKey) || 0;
};
LocalStorageManager.prototype.setBestScore = function (score) {
this.storage.setItem(this.bestScoreKey, score);
};
// Game state getters/setters and clearing
LocalStorageManager.prototype.getGameState = function () {
var stateJSON = this.storage.getItem(this.gameStateKey);
return stateJSON ? JSON.parse(stateJSON) : null;
};
LocalStorageManager.prototype.setGameState = function (gameState) {
this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));
};
LocalStorageManager.prototype.clearGameState = function () {
this.storage.removeItem(this.gameStateKey);
};

@ -0,0 +1,27 @@
function Tile(position, value) {
this.x = position.x;
this.y = position.y;
this.value = value || 2;
this.previousPosition = null;
this.mergedFrom = null; // Tracks tiles that merged together
}
Tile.prototype.savePosition = function () {
this.previousPosition = { x: this.x, y: this.y };
};
Tile.prototype.updatePosition = function (position) {
this.x = position.x;
this.y = position.y;
};
Tile.prototype.serialize = function () {
return {
position: {
x: this.x,
y: this.y
},
value: this.value
};
};

Binary file not shown.

After

(image error) Size: 5.7 KiB

Some files were not shown because too many files have changed in this diff Show More