mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-07-06 04:52:09 -07:00
Compare commits
198 commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1fd418685 | ||
|
bb5a79f234 | ||
|
4eb90a7a65 | ||
|
94fcdc3c61 | ||
|
8911a44766 | ||
|
89368f63c9 | ||
|
d4a0603bf2 | ||
|
666238ba9f | ||
|
bcdd0dd7fd | ||
|
6096de8c13 | ||
|
582ec7aa54 | ||
|
e14083c87c | ||
|
c2f0932670 | ||
|
7a490b5aae | ||
|
4c8209762c | ||
|
76690a319c | ||
|
36816db7ac | ||
|
801f902bea | ||
|
74d39e6314 | ||
|
40a9dee4ed | ||
|
6bfbcfc456 | ||
|
e00f5fae9d | ||
|
4164255ef9 | ||
|
b790fb3fb5 | ||
|
b4f051395f | ||
|
420809b24e | ||
|
7d820bd4ab | ||
|
aa3a3b8bbc | ||
|
ccecc67d74 | ||
|
dcd2e6af4a | ||
|
7a01ac0d41 | ||
|
796a128456 | ||
|
695da18473 | ||
|
7870a28cdd | ||
|
a44000ea2b | ||
|
2b4a7426ff | ||
|
f50b060795 | ||
|
a049ed43ec | ||
|
ae3ca1ac7a | ||
|
2257030ade | ||
|
6df937a57c | ||
|
078e83ec70 | ||
|
ac8a3a3a3c | ||
|
fcdc414692 | ||
|
505910bc5f | ||
|
f8b34febbe | ||
|
35130b08b7 | ||
|
5699c06dd8 | ||
|
cb5870f30d | ||
|
28f017d640 | ||
|
bae081d5b3 | ||
|
baa034fa3f | ||
|
fa44a3269c | ||
|
5914ceec77 | ||
|
402782b4af | ||
|
e6af02a35c | ||
|
367489e230 | ||
|
9a0ef224a0 | ||
|
510064c899 | ||
|
3a90ef0a65 | ||
|
b69bf280f8 | ||
|
c1a4504470 | ||
|
7c00b13818 | ||
|
3b85e147b6 | ||
|
2906cfdd69 | ||
|
2a4b67b58e | ||
|
bb4e5398b2 | ||
|
0b6e479e0b | ||
|
fc58e83e9c | ||
|
1b8fa556f8 | ||
|
12054a91c9 | ||
|
acf15480f2 | ||
|
9ab84e6054 | ||
|
a0c3768edb | ||
|
7785c310a9 | ||
|
96cbd5d9b8 | ||
|
42a3b864d0 | ||
|
24d73064db | ||
|
cb827a525a | ||
|
170dcd4d65 | ||
|
32e1539c22 | ||
|
88c03aa744 | ||
|
698bce8022 | ||
|
20c54b05ad | ||
|
abc9a27208 | ||
|
0e324a41a0 | ||
|
3272f47dc6 | ||
|
c5246541a9 | ||
|
0af3ae27d4 | ||
|
1ee23e0fa2 | ||
|
b9a9ea497c | ||
|
e531a90d26 | ||
|
7cf370c70d | ||
|
d4dc0ffee1 | ||
|
a2955d21fc | ||
|
da051803f5 | ||
|
49d65ad14a | ||
|
042e02eb3e | ||
|
3c2e9a0418 | ||
|
6c46920adb | ||
|
81984b7d48 | ||
|
fbb19f94ea | ||
|
85d9594ebc | ||
|
89c3175d71 | ||
|
9e698dd7c4 | ||
|
1ce2dbd5d2 | ||
|
854c600954 | ||
|
3a7ac73c9a | ||
|
5e3a19fccc | ||
|
526286c5d7 | ||
|
88230d54d7 | ||
|
308d6043f4 | ||
|
d10d2ac562 | ||
|
f4d255eb5b | ||
|
12c14fed05 | ||
|
264eb92792 | ||
|
5ef9983f95 | ||
|
bcda423db7 | ||
|
e6d18155af | ||
|
c19c7869d5 | ||
|
3417202049 | ||
|
c622f418e4 | ||
|
d31fb46ae8 | ||
|
a1b081bfce | ||
|
6fec401252 | ||
|
ad9b0f3d19 | ||
|
05b22e15f1 | ||
|
943e661af4 | ||
|
7378b31bc1 | ||
|
d5945f1420 | ||
|
61823f7372 | ||
|
1dfe88d74e | ||
|
7dd26f974f | ||
|
673a2de9c1 | ||
|
aba17d48a3 | ||
|
767e545f38 | ||
|
8904c86a6d | ||
|
65add80ec6 | ||
|
e2d1c11064 | ||
|
d4e1aa3b60 | ||
|
a8d2c6c29f | ||
|
92827c7911 | ||
|
aa9939e93c | ||
|
05812b7b7a | ||
|
20a7e9d123 | ||
|
21ef8fa018 | ||
|
c804bf66c7 | ||
|
b7c7d87db6 | ||
|
dfc0f32ac2 | ||
|
db5b3a1499 | ||
|
bf929cacd0 | ||
|
4d3f2aadbf | ||
|
4da09f75f3 | ||
|
402e7f01ee | ||
|
dff9844132 | ||
|
fc5306cfdd | ||
|
a12f130dc6 | ||
|
ea4ff606f0 | ||
|
c8b017c7d5 | ||
|
70725f7418 | ||
|
777c3a649a | ||
|
1193c23433 | ||
|
cfb2a64611 | ||
|
01b0f30c65 | ||
|
a11341f448 | ||
|
ffb8851835 | ||
|
ef7a97f6ae | ||
|
ea8ebc812e | ||
|
ea79836f0f | ||
|
8b6f9a5fc1 | ||
|
a2ebf7e408 | ||
|
aea23effe9 | ||
|
4ed2e4d6a9 | ||
|
8fbd1b9427 | ||
|
59e6603256 | ||
|
163fa81c03 | ||
|
ce0d5762b0 | ||
|
f633e49cb0 | ||
|
0d653def85 | ||
|
6e055d3071 | ||
|
b70455d19b | ||
|
cbc17691c8 | ||
|
cbfa49551d | ||
|
6e063109af | ||
|
8dcaad3978 | ||
|
f7c83e8416 | ||
|
e83cb04049 | ||
|
03c82ea515 | ||
|
9200c0b893 | ||
|
8ddbad6f61 | ||
|
6052d9d7d7 | ||
|
a6e24ce325 | ||
|
d5b220d7c3 | ||
|
732f312b17 | ||
|
93cce70da5 | ||
|
e5c0c805e2 | ||
|
1b05f07132 | ||
|
e42110ce56 |
346 changed files with 14306 additions and 6058 deletions
|
@ -1,34 +1,51 @@
|
||||||
image: 'Visual Studio 2017'
|
image:
|
||||||
|
- macOS
|
||||||
|
- 'Visual Studio 2019'
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- develop
|
||||||
- /^v\d.*$/
|
- /^v\d.*$/
|
||||||
- /^deploy-test(-.*)?$/
|
- /^deploy-test(-.*)?$/
|
||||||
|
|
||||||
configuration:
|
configuration:
|
||||||
- Release
|
- Release
|
||||||
|
|
||||||
install:
|
for:
|
||||||
- git submodule update --init --recursive
|
- matrix:
|
||||||
|
only:
|
||||||
|
- image: 'Visual Studio 2019'
|
||||||
|
|
||||||
build_script:
|
install:
|
||||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
- git submodule update --init --recursive
|
||||||
- C:\msys64\usr\bin\bash -lc "cd \"%APPVEYOR_BUILD_FOLDER%\" && scripts/appveyor.sh"
|
|
||||||
|
|
||||||
artifacts:
|
build_script:
|
||||||
- path: Chiaki
|
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||||
name: Chiaki
|
- C:\msys64\usr\bin\bash -lc "cd \"%APPVEYOR_BUILD_FOLDER%\" && scripts/appveyor-win.sh"
|
||||||
- path: Chiaki-PDB
|
|
||||||
name: Chiaki-PDB
|
|
||||||
|
|
||||||
deploy:
|
artifacts:
|
||||||
description: 'Chiaki Binaries'
|
- path: Chiaki
|
||||||
provider: GitHub
|
name: Chiaki
|
||||||
draft: true
|
- path: Chiaki-PDB
|
||||||
auth_token:
|
name: Chiaki-PDB
|
||||||
secure: Amvzm3PMM5nv+iFsqaU7TZ9fgyYt/YIrOLV0QMiCyOoUlLRIaiYxWiJ7maTpxhZ9
|
|
||||||
artifact: "Chiaki"
|
|
||||||
on:
|
|
||||||
appveyor_repo_tag: true
|
|
||||||
|
|
||||||
|
- matrix:
|
||||||
|
only:
|
||||||
|
- image: macOS
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
- sudo pip3 install protobuf
|
||||||
|
- HOMEBREW_NO_AUTO_UPDATE=1 brew install qt@5 opus openssl@1.1 nasm sdl2 protobuf
|
||||||
|
- scripts/build-ffmpeg.sh
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- export CMAKE_PREFIX_PATH="`pwd`/ffmpeg-prefix;/usr/local/opt/openssl@1.1;/usr/local/opt/qt@5"
|
||||||
|
- scripts/build-common.sh
|
||||||
|
- cp -a build/gui/chiaki.app Chiaki.app
|
||||||
|
- /usr/local/opt/qt@5/bin/macdeployqt Chiaki.app -dmg
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: Chiaki.dmg
|
||||||
|
name: Chiaki
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
image: alpine/edge
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- https://github.com/thestr4ng3r/chiaki.git
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- cmake
|
|
||||||
- protoc
|
|
||||||
- py3-protobuf
|
|
||||||
- opus-dev
|
|
||||||
- qt5-qtbase-dev
|
|
||||||
- qt5-qtsvg-dev
|
|
||||||
- qt5-qtmultimedia-dev
|
|
||||||
- ffmpeg-dev
|
|
||||||
- sdl2-dev
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- build: |
|
|
||||||
cd chiaki
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake ..
|
|
||||||
make -j4
|
|
||||||
test/chiaki-unit
|
|
||||||
|
|
28
.builds/android.yml
Normal file
28
.builds/android.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
image: alpine/latest
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- Chiaki.apk
|
||||||
|
- Chiaki.aab
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
- 163950ff-2ac4-423d-a280-d2d9edbef000
|
||||||
|
- f4bce41f-f96b-4633-80d8-0ff5dd74dc2a
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- build: |
|
||||||
|
cp -v ~/chiaki-local.properties chiaki/android/local.properties || echo "Secrets not available"
|
||||||
|
sudo service docker start
|
||||||
|
timeout 15 sh -c "until sudo docker info; do sleep 0.5; done"
|
||||||
|
sudo docker run \
|
||||||
|
-v /home/build:/home/build \
|
||||||
|
-u $(id -u):$(id -g) \
|
||||||
|
thestr4ng3r/android:90d826e \
|
||||||
|
/bin/bash -c "cd /home/build/chiaki/android && ./gradlew assembleRelease bundleRelease"
|
||||||
|
cp chiaki/android/app/build/outputs/apk/release/app-release*.apk Chiaki.apk
|
||||||
|
cp chiaki/android/app/build/outputs/bundle/release/app-release*.aab Chiaki.aab
|
55
.builds/common.yml
Normal file
55
.builds/common.yml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
image: alpine/latest
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- cmake
|
||||||
|
- ninja
|
||||||
|
- protoc
|
||||||
|
- py3-protobuf
|
||||||
|
- py3-setuptools
|
||||||
|
- opus-dev
|
||||||
|
- qt5-qtbase-dev
|
||||||
|
- qt5-qtsvg-dev
|
||||||
|
- qt5-qtmultimedia-dev
|
||||||
|
- ffmpeg-dev
|
||||||
|
- sdl2-dev
|
||||||
|
- podman
|
||||||
|
- fuse
|
||||||
|
- udev
|
||||||
|
- argp-standalone
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- chiaki.nro
|
||||||
|
- Chiaki.AppImage
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- setup_podman: |
|
||||||
|
sudo rc-service udev start
|
||||||
|
sudo rc-service cgroups start
|
||||||
|
sudo rc-service fuse start # Fuse for AppImages
|
||||||
|
echo build:100000:65536 | sudo tee /etc/subuid
|
||||||
|
echo build:100000:65536 | sudo tee /etc/subgid
|
||||||
|
# https://www.kernel.org/doc/Documentation/networking/tuntap.txt
|
||||||
|
# for slirp4netns
|
||||||
|
sudo mkdir -p /dev/net
|
||||||
|
sudo mknod /dev/net/tun c 10 200
|
||||||
|
sudo chmod 0666 /dev/net/tun
|
||||||
|
- local_build_and_test: |
|
||||||
|
cd chiaki
|
||||||
|
cmake -Bbuild -GNinja -DCHIAKI_ENABLE_CLI=ON -DCHIAKI_ENABLE_GUI=ON -DCHIAKI_CLI_ARGP_STANDALONE=ON
|
||||||
|
ninja -C build
|
||||||
|
build/test/chiaki-unit
|
||||||
|
- appimage: |
|
||||||
|
cd chiaki
|
||||||
|
scripts/run-podman-build-appimage.sh
|
||||||
|
cp appimage/Chiaki.AppImage ../Chiaki.AppImage
|
||||||
|
- switch: |
|
||||||
|
cd chiaki
|
||||||
|
scripts/switch/run-podman-build-chiaki.sh
|
||||||
|
cp build_switch/switch/chiaki.nro ../chiaki.nro
|
||||||
|
- bullseye: |
|
||||||
|
cd chiaki
|
||||||
|
scripts/run-podman-build-bullseye.sh
|
|
@ -1,19 +1,21 @@
|
||||||
|
|
||||||
image: freebsd/latest
|
image: freebsd/14.x
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/thestr4ng3r/chiaki
|
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
- cmake
|
- cmake
|
||||||
- protobuf
|
- protobuf
|
||||||
- py37-protobuf
|
- py311-setuptools # should not be needed with nanopb >= 0.4.9
|
||||||
|
- py311-protobuf
|
||||||
- opus
|
- opus
|
||||||
- qt5-core
|
- qt5-core
|
||||||
- qt5-qmake
|
- qt5-qmake
|
||||||
- qt5-buildtools
|
- qt5-buildtools
|
||||||
- qt5-gui
|
- qt5-gui
|
||||||
- qt5-widgets
|
- qt5-widgets
|
||||||
|
- qt5-concurrent
|
||||||
- qt5-svg
|
- qt5-svg
|
||||||
- qt5-opengl
|
- qt5-opengl
|
||||||
- qt5-multimedia
|
- qt5-multimedia
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
|
||||||
image: openbsd/6.7
|
image: openbsd/latest
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/thestr4ng3r/chiaki
|
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
- cmake
|
- cmake
|
||||||
- protobuf
|
- protobuf
|
||||||
|
- py3-setuptools # should not be needed with nanopb >= 0.4.9
|
||||||
- py3-protobuf
|
- py3-protobuf
|
||||||
- opus
|
- opus
|
||||||
- qtbase
|
- qtbase
|
||||||
|
|
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,44 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- IF YOU DO NOT USE THE TEMPLATE BELOW, YOUR ISSUE WILL BE CLOSED! THIS APPLIES FOR BUG REPORTS AND FEATURE REQUESTS, NO EXCEPTIONS!
|
|
||||||
|
|
||||||
The template below shows what you need to include in a good bug report, and you must use it.
|
|
||||||
I'm happy to help, but you have to ensure I fully understand what you want and have the information I need. -->
|
|
||||||
|
|
||||||
**Environment**
|
|
||||||
- OS/Distribution: <!-- with version if applicable, e.g. Android 10, Arch Linux, FreeBSD -->
|
|
||||||
- Desktop Environment: <!-- if applicable, also whether you use X or Wayland, e.g. Gnome on Wayland, Xmonad on X -->
|
|
||||||
- Hardware: <!-- for desktop: your GPU and which driver you use, for Android: your device, e.g. NVIDIA GeForce GTX 1080 with nouveau, Pixel 3 -->
|
|
||||||
- Exact Version or Commit Hash and Installation Method: <!-- e.g. v1.2.0 AppImage, 4e301b9 compiled from source -->
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Log Files**
|
|
||||||
If your issue is about Registration or Streaming, you MUST attach a log file of a session that showed your issue here.
|
|
||||||
Do not enable Verbose Logging unless explicitly told to.
|
|
||||||
On desktop, you can see the directory that log files are written to in the Settings under "Log Directory".
|
|
||||||
On Android, go into Settings -> Session Logs and export one from there.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- IF YOU DO NOT USE THE TEMPLATE BELOW, YOUR ISSUE WILL BE CLOSED! THIS APPLIES FOR BUG REPORTS AND FEATURE REQUESTS, NO EXCEPTIONS!
|
|
||||||
|
|
||||||
The template below shows what you need to include in a good feature request, and you must use it.
|
|
||||||
I'm happy to help, but you have to ensure I fully understand what you want and have the information I need. -->
|
|
||||||
|
|
||||||
**What platform does your feature request apply to?**
|
|
||||||
Choose one or more: Desktop/Android/Both/...
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
|
|
16
.github/workflows/switch.yml
vendored
16
.github/workflows/switch.yml
vendored
|
@ -1,16 +0,0 @@
|
||||||
name: Switch
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Switch
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Checkout submodules
|
|
||||||
run: |
|
|
||||||
git submodule init
|
|
||||||
git submodule update
|
|
||||||
- name: Build Chiaki
|
|
||||||
run: scripts/switch/run-docker-build-chiaki.sh
|
|
||||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -24,4 +24,10 @@ secret.tar
|
||||||
keystore-env.sh
|
keystore-env.sh
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
|
.cache/
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
chiaki.conf
|
||||||
|
/appimage
|
||||||
|
.cache/
|
||||||
|
/*.app
|
||||||
|
/*.dmg
|
||||||
|
|
7
.gitmodules
vendored
7
.gitmodules
vendored
|
@ -6,10 +6,13 @@
|
||||||
url = https://github.com/nanopb/nanopb.git
|
url = https://github.com/nanopb/nanopb.git
|
||||||
[submodule "third-party/jerasure"]
|
[submodule "third-party/jerasure"]
|
||||||
path = third-party/jerasure
|
path = third-party/jerasure
|
||||||
url = https://github.com/thestr4ng3r/jerasure.git
|
url = https://git.sr.ht/~thestr4ng3r/jerasure
|
||||||
[submodule "third-party/gf-complete"]
|
[submodule "third-party/gf-complete"]
|
||||||
path = third-party/gf-complete
|
path = third-party/gf-complete
|
||||||
url = https://github.com/thestr4ng3r/gf-complete.git
|
url = https://git.sr.ht/~thestr4ng3r/gf-complete
|
||||||
[submodule "android/app/src/main/cpp/oboe"]
|
[submodule "android/app/src/main/cpp/oboe"]
|
||||||
path = android/app/src/main/cpp/oboe
|
path = android/app/src/main/cpp/oboe
|
||||||
url = https://github.com/google/oboe
|
url = https://github.com/google/oboe
|
||||||
|
[submodule "switch/borealis"]
|
||||||
|
path = switch/borealis
|
||||||
|
url = https://git.sr.ht/~thestr4ng3r/borealis
|
||||||
|
|
165
.travis.yml
165
.travis.yml
|
@ -1,165 +0,0 @@
|
||||||
|
|
||||||
language: cpp
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- /^v\d.*$/
|
|
||||||
- /^deploy-test(-.*)?$/
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- export CHIAKI_VERSION="$TRAVIS_TAG"
|
|
||||||
- if [ -z "$CHIAKI_VERSION" ]; then export CHIAKI_VERSION="$TRAVIS_COMMIT"; fi
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- name: Linux (Bionic)
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:beineri/opt-qt-5.12.0-bionic"
|
|
||||||
packages:
|
|
||||||
- protobuf-compiler
|
|
||||||
- python3-protobuf
|
|
||||||
- libopus-dev
|
|
||||||
- qt512base
|
|
||||||
- qt512multimedia
|
|
||||||
- qt512gamepad
|
|
||||||
- qt512svg
|
|
||||||
- libgl1-mesa-dev
|
|
||||||
- nasm
|
|
||||||
- libsdl2-dev
|
|
||||||
- libva-dev
|
|
||||||
env:
|
|
||||||
- CMAKE_PREFIX_PATH="$TRAVIS_BUILD_DIR/ffmpeg-prefix;/opt/qt512"
|
|
||||||
- CMAKE_EXTRA_ARGS="-DCMAKE_INSTALL_PREFIX=/usr"
|
|
||||||
- DEPLOY=0
|
|
||||||
install:
|
|
||||||
- scripts/build-ffmpeg.sh
|
|
||||||
script:
|
|
||||||
- scripts/travis-build.sh || exit 1
|
|
||||||
- source scripts/travis-appimage.sh
|
|
||||||
|
|
||||||
- name: Linux (Xenial, Deploy)
|
|
||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- sourceline: "ppa:beineri/opt-qt-5.12.3-xenial"
|
|
||||||
packages:
|
|
||||||
- python3-pip
|
|
||||||
- libopus-dev
|
|
||||||
- qt512base
|
|
||||||
- qt512multimedia
|
|
||||||
- qt512svg
|
|
||||||
- libgl1-mesa-dev
|
|
||||||
- nasm
|
|
||||||
- libudev-dev
|
|
||||||
- libva-dev
|
|
||||||
env:
|
|
||||||
- CMAKE_PREFIX_PATH="$TRAVIS_BUILD_DIR/ffmpeg-prefix;$TRAVIS_BUILD_DIR/sdl2-prefix;/opt/qt512"
|
|
||||||
- CMAKE_EXTRA_ARGS="-DCMAKE_INSTALL_PREFIX=/usr"
|
|
||||||
- SDL2_FROM_SRC=1
|
|
||||||
- DEPLOY=1
|
|
||||||
install:
|
|
||||||
- sudo pip3 install protobuf
|
|
||||||
- scripts/fetch-protoc.sh
|
|
||||||
- export PATH="$TRAVIS_BUILD_DIR/protoc/bin:$PATH"
|
|
||||||
- scripts/build-ffmpeg.sh
|
|
||||||
- scripts/build-sdl2.sh
|
|
||||||
script:
|
|
||||||
- scripts/travis-build.sh || exit 1
|
|
||||||
- source scripts/travis-appimage.sh
|
|
||||||
|
|
||||||
- name: macOS
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode11
|
|
||||||
addons:
|
|
||||||
homebrew:
|
|
||||||
packages:
|
|
||||||
- qt
|
|
||||||
- opus
|
|
||||||
- openssl@1.1
|
|
||||||
- nasm
|
|
||||||
- sdl2
|
|
||||||
env:
|
|
||||||
- CMAKE_PREFIX_PATH="$TRAVIS_BUILD_DIR/ffmpeg-prefix;/usr/local/opt/openssl@1.1;/usr/local/opt/qt"
|
|
||||||
- CMAKE_EXTRA_ARGS=""
|
|
||||||
- DEPLOY=1
|
|
||||||
install:
|
|
||||||
- pip3 install protobuf
|
|
||||||
- scripts/build-ffmpeg.sh
|
|
||||||
script:
|
|
||||||
- scripts/travis-build.sh
|
|
||||||
- cp -a build/gui/chiaki.app Chiaki.app
|
|
||||||
- /usr/local/opt/qt/bin/macdeployqt Chiaki.app -dmg
|
|
||||||
- export DEPLOY_FILE="Chiaki-${CHIAKI_VERSION}-macOS-x86_64.dmg"
|
|
||||||
- mv Chiaki.dmg "$DEPLOY_FILE"
|
|
||||||
- cmake -DCHIAKI_VERSION="${CHIAKI_VERSION}" -DCHIAKI_DMG="${DEPLOY_FILE}" -DCHIAKI_CASK_OUT=chiaki.rb -P scripts/configure-cask.cmake
|
|
||||||
- echo "------------------- chiaki.rb cask -------------------"
|
|
||||||
- cat chiaki.rb
|
|
||||||
- echo "------------------------------------------------------"
|
|
||||||
|
|
||||||
- name: Android
|
|
||||||
language: android
|
|
||||||
os: linux
|
|
||||||
dist: trusty
|
|
||||||
env:
|
|
||||||
- DEPLOY=1
|
|
||||||
android:
|
|
||||||
components:
|
|
||||||
- build-tools-29.0.2
|
|
||||||
- android-29
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- python3-pip
|
|
||||||
install:
|
|
||||||
- echo y | sdkmanager "ndk-bundle"
|
|
||||||
- echo y | sdkmanager "cmake;3.10.2.4988404"
|
|
||||||
- sudo pip3 install protobuf
|
|
||||||
- scripts/fetch-protoc.sh
|
|
||||||
- export PATH="$TRAVIS_BUILD_DIR/protoc/bin:$PATH"
|
|
||||||
script:
|
|
||||||
- cd android
|
|
||||||
- ./gradlew assembleRelease bundleRelease
|
|
||||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then export DEPLOY_FILE_BASE="Chiaki-$CHIAKI_VERSION-Android"; fi
|
|
||||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then export DEPLOY_FILE="$DEPLOY_FILE_BASE.a[pa][kb]"; fi
|
|
||||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then cp app/build/outputs/apk/release/app-release.apk "../$DEPLOY_FILE_BASE.apk"; fi
|
|
||||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then cp app/build/outputs/bundle/release/app-release.aab "../$DEPLOY_FILE_BASE.aab"; fi
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
- name: "Source Package"
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
env:
|
|
||||||
- DEPLOY=1
|
|
||||||
install: ~
|
|
||||||
script:
|
|
||||||
- find . -name ".git*" | xargs rm -rfv
|
|
||||||
- mkdir chiaki && shopt -s extglob && mv !(chiaki) chiaki
|
|
||||||
- export DEPLOY_FILE="chiaki-$CHIAKI_VERSION-src.tar.gz"
|
|
||||||
- tar -czvf "$DEPLOY_FILE" chiaki
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- if [ ! -z "$encrypted_31d5e6477a29_iv" ]; then openssl aes-256-cbc -K $encrypted_31d5e6477a29_key -iv $encrypted_31d5e6477a29_iv -in secret.tar.enc -out secret.tar -d && tar -xf secret.tar; fi
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- if [ ! -z "$DEPLOY_FILE" ]; then echo "--- SHA256 for $DEPLOY_FILE"; openssl dgst -sha256 "$DEPLOY_FILE"; echo; fi
|
|
||||||
- if [ ! -z "$DEPLOY_FILE" ]; then echo "--- transfer.sh"; curl -m 30 --upload-file "$DEPLOY_FILE" "https://transfer.sh/$DEPLOY_FILE"; echo; fi
|
|
||||||
- if [ ! -z "$DEPLOY_FILE" ]; then echo "--- oshi.at"; curl -m 30 --upload-file "$DEPLOY_FILE" "https://oshi.at/$DEPLOY_FILE/129600"; echo; fi
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
skip_cleanup: true
|
|
||||||
provider: releases
|
|
||||||
draft: true
|
|
||||||
api_key:
|
|
||||||
secure: R7RjLOuGFda05EJeNX2lNG135xKU2w9IQn7p1H1P2zw4zlQMgSBpNRaW8hE408x5KJUjptJTF6QaYYmPWbHlf9VEPFVIcVzSp8YSd2Cdr+GKhmFgWF+fJPBj5y9NNqohwxvK3Nrugh0v6yVQiEYEGF7WArU6dvymSNNTw/EqXtfrOvwUgSf1bDAzQAsXn3E6Ptzf9DrQU8+mOgMSqT/3Wy5207KLmWTtwBWDgkskKwS9OEXk3tDd6U4uT7NFHHmcw+ZjQXRD+yHSHUWYs1oKR4IfgPFxQfEK0Txhkxdf3yj1aNweuk7GGC3cfRaarUfRQpoYqYYCxhTfGZ2b4rVgX3XpssMY7ZmSZHRi/SX08ETXF/c7PZGzr0RPFXZLgAGjgN6O2Dbb9agc3tOUGDUuqKEWX9sALm82WS0FRAFrFLENgMFsj5hu+DKIIkAU2yEsadYKjjhC+q+mTAEkxKKknvM50Xpx3tE1TlP/31Z53v4/NydHIHXPJ72V3mnuoTacwhG2SkGtjMbLCnEZDCtu9C4556oa7Z29cqafv90ZD7lTQMV+ijKvjxgOC9u1GeemmZLofRGDFyYSqKxOpYxxxXGOhs+7FMAdKP00h++MTLwRwIebKQs0fW0XiNKmwushWOUU8sXI1jxTbwe9dPQsspxHRv/mVo6l2vUcBjC19K0=
|
|
||||||
file_glob: true
|
|
||||||
file: $DEPLOY_FILE
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
condition: $DEPLOY = 1
|
|
147
CMakeLists.txt
147
CMakeLists.txt
|
@ -13,16 +13,26 @@ option(CHIAKI_ENABLE_TESTS "Enable tests for Chiaki" ON)
|
||||||
option(CHIAKI_ENABLE_CLI "Enable CLI for Chiaki" OFF)
|
option(CHIAKI_ENABLE_CLI "Enable CLI for Chiaki" OFF)
|
||||||
option(CHIAKI_ENABLE_GUI "Enable Qt GUI" ON)
|
option(CHIAKI_ENABLE_GUI "Enable Qt GUI" ON)
|
||||||
option(CHIAKI_ENABLE_ANDROID "Enable Android (Use only as part of the Gradle Project)" OFF)
|
option(CHIAKI_ENABLE_ANDROID "Enable Android (Use only as part of the Gradle Project)" OFF)
|
||||||
option(CHIAKI_ENABLE_SWITCH "Enable Nintendo Switch (Requires devKitPro libnx)" OFF)
|
option(CHIAKI_ENABLE_BOREALIS "Enable Borealis GUI (For Nintendo Switch or PC)" OFF)
|
||||||
tri_option(CHIAKI_ENABLE_SETSU "Enable libsetsu for touchpad input from controller" AUTO)
|
tri_option(CHIAKI_ENABLE_SETSU "Enable libsetsu for touchpad input from controller" AUTO)
|
||||||
option(CHIAKI_LIB_ENABLE_OPUS "Use Opus as part of Chiaki Lib" ON)
|
option(CHIAKI_LIB_ENABLE_OPUS "Use Opus as part of Chiaki Lib" ON)
|
||||||
|
if(CHIAKI_ENABLE_GUI OR CHIAKI_ENABLE_BOREALIS)
|
||||||
|
set(CHIAKI_FFMPEG_DEFAULT ON)
|
||||||
|
else()
|
||||||
|
set(CHIAKI_FFMPEG_DEFAULT AUTO)
|
||||||
|
endif()
|
||||||
|
tri_option(CHIAKI_ENABLE_FFMPEG_DECODER "Enable FFMPEG video decoder" ${CHIAKI_FFMPEG_DEFAULT})
|
||||||
|
tri_option(CHIAKI_ENABLE_PI_DECODER "Enable Raspberry Pi-specific video decoder (requires libraspberrypi0 and libraspberrypi-doc)" AUTO)
|
||||||
option(CHIAKI_LIB_ENABLE_MBEDTLS "Use mbedtls instead of OpenSSL as part of Chiaki Lib" OFF)
|
option(CHIAKI_LIB_ENABLE_MBEDTLS "Use mbedtls instead of OpenSSL as part of Chiaki Lib" OFF)
|
||||||
|
option(CHIAKI_LIB_MBEDTLS_EXTERNAL_PROJECT "Fetch Mbed TLS instead of using system-provided libs" OFF)
|
||||||
option(CHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT "Use OpenSSL as CMake external project" OFF)
|
option(CHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT "Use OpenSSL as CMake external project" OFF)
|
||||||
option(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON)
|
option(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON)
|
||||||
option(CHIAKI_CLI_ARGP_STANDALONE "Search for standalone argp lib for CLI" OFF)
|
option(CHIAKI_CLI_ARGP_STANDALONE "Search for standalone argp lib for CLI" OFF)
|
||||||
|
tri_option(CHIAKI_USE_SYSTEM_JERASURE "Use system-provided jerasure instead of submodule" AUTO)
|
||||||
|
tri_option(CHIAKI_USE_SYSTEM_NANOPB "Use system-provided nanopb instead of submodule" AUTO)
|
||||||
|
|
||||||
set(CHIAKI_VERSION_MAJOR 1)
|
set(CHIAKI_VERSION_MAJOR 2)
|
||||||
set(CHIAKI_VERSION_MINOR 3)
|
set(CHIAKI_VERSION_MINOR 2)
|
||||||
set(CHIAKI_VERSION_PATCH 0)
|
set(CHIAKI_VERSION_PATCH 0)
|
||||||
set(CHIAKI_VERSION ${CHIAKI_VERSION_MAJOR}.${CHIAKI_VERSION_MINOR}.${CHIAKI_VERSION_PATCH})
|
set(CHIAKI_VERSION ${CHIAKI_VERSION_MAJOR}.${CHIAKI_VERSION_MINOR}.${CHIAKI_VERSION_PATCH})
|
||||||
|
|
||||||
|
@ -36,20 +46,38 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION})
|
||||||
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
|
set(CPACK_DEBIAN_PACKAGE_SECTION "games")
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
|
set(CHIAKI_IS_SWITCH ${NSWITCH})
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/setsu/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/setsu/cmake")
|
||||||
|
|
||||||
# configure nintendo switch toolchain
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
if(CHIAKI_ENABLE_SWITCH AND CHIAKI_ENABLE_SWITCH_LINUX)
|
|
||||||
# CHIAKI_ENABLE_SWITCH_LINUX is a special testing version
|
if(CHIAKI_IS_SWITCH)
|
||||||
# the aim is to troubleshoot nitendo switch chiaki verison
|
|
||||||
# from a x86 linux os
|
|
||||||
add_definitions(-DCHIAKI_ENABLE_SWITCH_LINUX)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
|
||||||
elseif(CHIAKI_ENABLE_SWITCH)
|
|
||||||
add_definitions(-D__SWITCH__)
|
|
||||||
# TODO check if android ... or other versions are enabled
|
|
||||||
# force mbedtls as crypto lib
|
# force mbedtls as crypto lib
|
||||||
set(CHIAKI_LIB_ENABLE_MBEDTLS ON)
|
set(CHIAKI_LIB_ENABLE_MBEDTLS ON)
|
||||||
|
add_definitions(-D__SWITCH__)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_USE_SYSTEM_JERASURE)
|
||||||
|
if(CHIAKI_USE_SYSTEM_JERASURE STREQUAL AUTO)
|
||||||
|
find_package(Jerasure QUIET)
|
||||||
|
set(CHIAKI_USE_SYSTEM_JERASURE ${Jerasure_FOUND})
|
||||||
|
else()
|
||||||
|
find_package(Jerasure REQUIRED)
|
||||||
|
set(CHIAKI_USE_SYSTEM_JERASURE ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(PythonInterp 3 REQUIRED) # Make sure nanopb doesn't find Python 2.7 because Python 2 should just die.
|
||||||
|
|
||||||
|
if(CHIAKI_USE_SYSTEM_NANOPB)
|
||||||
|
if(CHIAKI_USE_SYSTEM_NANOPB STREQUAL AUTO)
|
||||||
|
find_package(Nanopb QUIET)
|
||||||
|
set(CHIAKI_USE_SYSTEM_NANOPB ${Nanopb_FOUND})
|
||||||
|
else()
|
||||||
|
find_package(Nanopb REQUIRED)
|
||||||
|
set(CHIAKI_USE_SYSTEM_NANOPB ON)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(third-party)
|
add_subdirectory(third-party)
|
||||||
|
@ -62,6 +90,58 @@ endif()
|
||||||
|
|
||||||
if(CHIAKI_LIB_ENABLE_MBEDTLS)
|
if(CHIAKI_LIB_ENABLE_MBEDTLS)
|
||||||
add_definitions(-DCHIAKI_LIB_ENABLE_MBEDTLS)
|
add_definitions(-DCHIAKI_LIB_ENABLE_MBEDTLS)
|
||||||
|
if(CHIAKI_LIB_MBEDTLS_EXTERNAL_PROJECT)
|
||||||
|
set(FETCHCONTENT_QUIET CACHE BOOL FALSE)
|
||||||
|
include(FetchContent)
|
||||||
|
set(ENABLE_TESTING CACHE INTERNAL OFF)
|
||||||
|
set(ENABLE_PROGRAMS CACHE INTERNAL OFF)
|
||||||
|
set(USE_SHARED_MBEDTLS_LIBRARY CACHE INTERNAL OFF)
|
||||||
|
FetchContent_Declare(
|
||||||
|
mbedtls
|
||||||
|
GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls.git
|
||||||
|
GIT_TAG 8b3f26a5ac38d4fdccbc5c5366229f3e01dafcc0 # v2.28.0
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(mbedtls)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_ENABLE_FFMPEG_DECODER)
|
||||||
|
find_package(FFMPEG COMPONENTS avcodec avutil)
|
||||||
|
if(FFMPEG_FOUND)
|
||||||
|
set(CHIAKI_ENABLE_FFMPEG_DECODER ON)
|
||||||
|
else()
|
||||||
|
if(NOT CHIAKI_ENABLE_FFMPEG_DECODER STREQUAL AUTO)
|
||||||
|
message(FATAL_ERROR "CHIAKI_ENABLE_FFMPEG_DECODER is set to ON, but ffmpeg could not be found.")
|
||||||
|
endif()
|
||||||
|
set(CHIAKI_ENABLE_FFMPEG_DECODER OFF)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_ENABLE_FFMPEG_DECODER)
|
||||||
|
message(STATUS "FFMPEG Decoder enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "FFMPEG Decoder disabled")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_ENABLE_PI_DECODER)
|
||||||
|
find_package(ILClient)
|
||||||
|
if(ILClient_FOUND)
|
||||||
|
set(CHIAKI_ENABLE_PI_DECODER ON)
|
||||||
|
else()
|
||||||
|
if(NOT CHIAKI_ENABLE_PI_DECODER STREQUAL AUTO)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
CHIAKI_ENABLE_PI_DECODER is set to ON, but its dependencies (ilclient source and libs) could not be resolved.
|
||||||
|
The Raspberry Pi Decoder is only supported on Raspberry Pi OS and requires libraspberrypi0 and libraspberrypi-doc.")
|
||||||
|
endif()
|
||||||
|
set(CHIAKI_ENABLE_PI_DECODER OFF)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_ENABLE_PI_DECODER)
|
||||||
|
message(STATUS "Pi Decoder enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "Pi Decoder disabled")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(lib)
|
add_subdirectory(lib)
|
||||||
|
@ -70,21 +150,30 @@ if(CHIAKI_ENABLE_CLI)
|
||||||
add_subdirectory(cli)
|
add_subdirectory(cli)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CHIAKI_ENABLE_GUI AND CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
|
||||||
|
find_package(SDL2 MODULE REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CHIAKI_ENABLE_SETSU)
|
if(CHIAKI_ENABLE_SETSU)
|
||||||
find_package(Udev QUIET)
|
if(CHIAKI_ENABLE_SETSU STREQUAL AUTO AND SDL2_FOUND AND (SDL2_VERSION_MINOR GREATER 0 OR SDL2_VERSION_PATCH GREATER_EQUAL 14))
|
||||||
find_package(Evdev QUIET)
|
message(STATUS "SDL version ${SDL2_VERSION} is >= 2.0.14, disabling Setsu")
|
||||||
if(Udev_FOUND AND Evdev_FOUND)
|
|
||||||
set(CHIAKI_ENABLE_SETSU ON)
|
|
||||||
else()
|
|
||||||
if(NOT CHIAKI_ENABLE_SETSU STREQUAL AUTO)
|
|
||||||
message(FATAL_ERROR "
|
|
||||||
CHIAKI_ENABLE_SETSU is set to ON, but its dependencies (udev and evdev) could not be resolved.
|
|
||||||
Keep in mind that setsu is only supported on Linux!")
|
|
||||||
endif()
|
|
||||||
set(CHIAKI_ENABLE_SETSU OFF)
|
set(CHIAKI_ENABLE_SETSU OFF)
|
||||||
endif()
|
else()
|
||||||
if(CHIAKI_ENABLE_SETSU)
|
find_package(Udev QUIET)
|
||||||
add_subdirectory(setsu)
|
find_package(Evdev QUIET)
|
||||||
|
if(Udev_FOUND AND Evdev_FOUND)
|
||||||
|
set(CHIAKI_ENABLE_SETSU ON)
|
||||||
|
else()
|
||||||
|
if(NOT CHIAKI_ENABLE_SETSU STREQUAL AUTO)
|
||||||
|
message(FATAL_ERROR "
|
||||||
|
CHIAKI_ENABLE_SETSU is set to ON, but its dependencies (udev and evdev) could not be resolved.
|
||||||
|
Keep in mind that setsu is only supported on Linux!")
|
||||||
|
endif()
|
||||||
|
set(CHIAKI_ENABLE_SETSU OFF)
|
||||||
|
endif()
|
||||||
|
if(CHIAKI_ENABLE_SETSU)
|
||||||
|
add_subdirectory(setsu)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -95,7 +184,6 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CHIAKI_ENABLE_GUI)
|
if(CHIAKI_ENABLE_GUI)
|
||||||
#add_subdirectory(setsu)
|
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -108,7 +196,6 @@ if(CHIAKI_ENABLE_ANDROID)
|
||||||
add_subdirectory(android/app)
|
add_subdirectory(android/app)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CHIAKI_ENABLE_SWITCH)
|
if(CHIAKI_ENABLE_BOREALIS)
|
||||||
#TODO
|
add_subdirectory(switch)
|
||||||
#add_subdirectory(switch)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
150
COPYING
150
COPYING
|
@ -1,5 +1,5 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
@ -7,17 +7,15 @@
|
||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
The GNU General Public License is a free, copyleft license for
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
software and other kinds of works.
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
The licenses for most software and other practical works are designed
|
||||||
to take away your freedom to share and change the works. By contrast,
|
to take away your freedom to share and change the works. By contrast,
|
||||||
the GNU General Public License is intended to guarantee your freedom to
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
share and change all versions of a program--to make sure it remains free
|
share and change all versions of a program--to make sure it remains free
|
||||||
software for all its users. We, the Free Software Foundation, use the
|
software for all its users.
|
||||||
GNU General Public License for most of our software; it applies also to
|
|
||||||
any other work released this way by its authors. You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
When we speak of free software, we are referring to freedom, not
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||||
want it, that you can change the software or use pieces of it in new
|
want it, that you can change the software or use pieces of it in new
|
||||||
free programs, and that you know you can do these things.
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
To protect your rights, we need to prevent others from denying you
|
Developers that use our General Public Licenses protect your rights
|
||||||
these rights or asking you to surrender the rights. Therefore, you have
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
certain responsibilities if you distribute copies of the software, or if
|
you this License which gives you legal permission to copy, distribute
|
||||||
you modify it: responsibilities to respect the freedom of others.
|
and/or modify the software.
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
A secondary benefit of defending all users' freedom is that
|
||||||
gratis or for a fee, you must pass on to the recipients the same
|
improvements made in alternate versions of the program, if they
|
||||||
freedoms that you received. You must make sure that they, too, receive
|
receive widespread use, become available for other developers to
|
||||||
or can get the source code. And you must show them these terms so they
|
incorporate. Many developers of free software are heartened and
|
||||||
know their rights.
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
Developers that use the GNU GPL protect your rights with two steps:
|
The GNU Affero General Public License is designed specifically to
|
||||||
(1) assert copyright on the software, and (2) offer you this License
|
ensure that, in such cases, the modified source code becomes available
|
||||||
giving you legal permission to copy, distribute and/or modify it.
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
For the developers' and authors' protection, the GPL clearly explains
|
An older license, called the Affero General Public License and
|
||||||
that there is no warranty for this free software. For both users' and
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
authors' sake, the GPL requires that modified versions be marked as
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
changed, so that their problems will not be attributed erroneously to
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
authors of previous versions.
|
this license.
|
||||||
|
|
||||||
Some devices are designed to deny users access to install or run
|
|
||||||
modified versions of the software inside them, although the manufacturer
|
|
||||||
can do so. This is fundamentally incompatible with the aim of
|
|
||||||
protecting users' freedom to change the software. The systematic
|
|
||||||
pattern of such abuse occurs in the area of products for individuals to
|
|
||||||
use, which is precisely where it is most unacceptable. Therefore, we
|
|
||||||
have designed this version of the GPL to prohibit the practice for those
|
|
||||||
products. If such problems arise substantially in other domains, we
|
|
||||||
stand ready to extend this provision to those domains in future versions
|
|
||||||
of the GPL, as needed to protect the freedom of users.
|
|
||||||
|
|
||||||
Finally, every program is threatened constantly by software patents.
|
|
||||||
States should not allow patents to restrict development and use of
|
|
||||||
software on general-purpose computers, but in those that do, we wish to
|
|
||||||
avoid the special danger that patents applied to a free program could
|
|
||||||
make it effectively proprietary. To prevent this, the GPL assures that
|
|
||||||
patents cannot be used to render the program non-free.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
|
@ -72,7 +60,7 @@ modification follow.
|
||||||
|
|
||||||
0. Definitions.
|
0. Definitions.
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU General Public License.
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
works, such as semiconductor masks.
|
works, such as semiconductor masks.
|
||||||
|
@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||||
the Program, the only way you could satisfy both those terms and this
|
the Program, the only way you could satisfy both those terms and this
|
||||||
License would be to refrain entirely from conveying the Program.
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
13. Use with the GNU Affero General Public License.
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
Notwithstanding any other provision of this License, you have
|
||||||
permission to link or combine any covered work with a work licensed
|
permission to link or combine any covered work with a work licensed
|
||||||
under version 3 of the GNU Affero General Public License into a single
|
under version 3 of the GNU General Public License into a single
|
||||||
combined work, and to convey the resulting work. The terms of this
|
combined work, and to convey the resulting work. The terms of this
|
||||||
License will continue to apply to the part which is the covered work,
|
License will continue to apply to the part which is the covered work,
|
||||||
but the special requirements of the GNU Affero General Public License,
|
but the work with which it is combined will remain governed by version
|
||||||
section 13, concerning interaction through a network will apply to the
|
3 of the GNU General Public License.
|
||||||
combination as such.
|
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
the GNU General Public License from time to time. Such new versions will
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
address new problems or concerns.
|
address new problems or concerns.
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
Each version is given a distinguishing version number. If the
|
||||||
Program specifies that a certain numbered version of the GNU General
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
Public License "or any later version" applies to it, you have the
|
Public License "or any later version" applies to it, you have the
|
||||||
option of following the terms and conditions either of that numbered
|
option of following the terms and conditions either of that numbered
|
||||||
version or of any later version published by the Free Software
|
version or of any later version published by the Free Software
|
||||||
Foundation. If the Program does not specify a version number of the
|
Foundation. If the Program does not specify a version number of the
|
||||||
GNU General Public License, you may choose any version ever published
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
by the Free Software Foundation.
|
by the Free Software Foundation.
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
If the Program specifies that a proxy can decide which future
|
||||||
versions of the GNU General Public License can be used, that proxy's
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
public statement of acceptance of a version permanently authorizes you
|
public statement of acceptance of a version permanently authorizes you
|
||||||
to choose that version for the Program.
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
@ -635,40 +633,40 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||||
Copyright (C) <year> <name of author>
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
If the program does terminal interaction, make it output a short
|
If your software can interact with users remotely through a computer
|
||||||
notice like this when it starts in an interactive mode:
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
<program> Copyright (C) <year> <name of author>
|
interface could display a "Source" link that leads users to an archive
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
of the code. There are many ways you could offer source, and different
|
||||||
This is free software, and you are welcome to redistribute it
|
solutions will be better for different programs; see section 13 for the
|
||||||
under certain conditions; type `show c' for details.
|
specific requirements.
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, your program's commands
|
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
<https://www.gnu.org/licenses/>.
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
Additional permission under GNU AGPL version 3 section 7
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
If you modify this program, or any covered work, by linking or
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
combining it with the OpenSSL project's OpenSSL library (or a
|
||||||
Public License instead of this License. But first, please read
|
modified version of that library), containing parts covered by the
|
||||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
||||||
|
grants you additional permission to convey the resulting work.
|
||||||
|
Corresponding Source for a non-source form of such a combination
|
||||||
|
shall include the source code for the parts of OpenSSL used as well
|
||||||
|
as that of the covered work.
|
||||||
|
|
672
LICENSES/AGPL-3.0-only-OpenSSL.txt
Normal file
672
LICENSES/AGPL-3.0-only-OpenSSL.txt
Normal file
|
@ -0,0 +1,672 @@
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Additional permission under GNU AGPL version 3 section 7
|
||||||
|
|
||||||
|
If you modify this program, or any covered work, by linking or
|
||||||
|
combining it with the OpenSSL project's OpenSSL library (or a
|
||||||
|
modified version of that library), containing parts covered by the
|
||||||
|
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
||||||
|
grants you additional permission to convey the resulting work.
|
||||||
|
Corresponding Source for a non-source form of such a combination
|
||||||
|
shall include the source code for the parts of OpenSSL used as well
|
||||||
|
as that of the covered work.
|
80
README.md
80
README.md
|
@ -5,38 +5,38 @@
|
||||||
|
|
||||||
**Disclaimer:** This project is not endorsed or certified by Sony Interactive Entertainment LLC.
|
**Disclaimer:** This project is not endorsed or certified by Sony Interactive Entertainment LLC.
|
||||||
|
|
||||||
[](https://travis-ci.com/thestr4ng3r/chiaki) [](https://ci.appveyor.com/project/thestr4ng3r/chiaki) [](https://builds.sr.ht/~thestr4ng3r/chiaki?)
|
[](https://ci.appveyor.com/project/thestr4ng3r/chiaki) [](https://builds.sr.ht/~thestr4ng3r/chiaki?)
|
||||||
|
|
||||||
Chiaki is a Free and Open Source Software Client for PlayStation 4 Remote Play
|
Chiaki is a Free and Open Source Software Client for PlayStation 4 and PlayStation 5 Remote Play
|
||||||
for Linux, FreeBSD, OpenBSD, Android, macOS, Windows and potentially even more platforms.
|
for Linux, FreeBSD, OpenBSD, NetBSD, Android, macOS, Windows, Nintendo Switch and potentially even more platforms.
|
||||||
|
|
||||||
|
## Project Status and Contributing
|
||||||
|
|
||||||
|
As all relevant features are implemented, this project is considered to be finished and in maintenance mode only.
|
||||||
|
No major updates are planned and contributions are only accepted in special cases such as security issues.
|
||||||
|
The objective is to keep a stable base and not break existing support for less mainstream platforms such as BSDs.
|
||||||
|
|
||||||
|
**For a more active, fast moving and community-oriented project, refer
|
||||||
|
to [chiaki-ng](https://streetpea.github.io/chiaki-ng/) ("next generation").
|
||||||
|
If you would like to contribute, this will likely also be the best place to do so.**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
Everything necessary for a full streaming session, including the initial
|
|
||||||
registration and wakeup of the console, is supported.
|
|
||||||
The following features however are yet to be implemented:
|
|
||||||
* Congestion Control
|
|
||||||
* H264 Error Concealment (FEC and active error recovery however are implemented)
|
|
||||||
* Touchpad support (Triggering the Touchpad Button is currently possible from the keyboard though)
|
|
||||||
* Rumble
|
|
||||||
* Accelerometer/Gyroscope
|
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
You can either download a pre-built release (easier) or build Chiaki from source.
|
You can either download a pre-built release or build Chiaki from source.
|
||||||
|
|
||||||
### Downloading a Release
|
### Downloading a Release
|
||||||
|
|
||||||
Builds are provided for Linux, Android, macOS and Windows.
|
Builds are provided for Linux, Android, macOS, Nintendo Switch and Windows.
|
||||||
|
|
||||||
You can download them [here](https://github.com/thestr4ng3r/chiaki/releases).
|
You can download them [here](https://git.sr.ht/~thestr4ng3r/chiaki/refs).
|
||||||
|
|
||||||
* **Linux**: The provided file is an [AppImage](https://appimage.org/). Simply make it executable (`chmod +x <file>.AppImage`) and run it.
|
* **Linux**: The provided file is an [AppImage](https://appimage.org/). Simply make it executable (`chmod +x <file>.AppImage`) and run it.
|
||||||
* **Android**: Install from [Google Play](https://play.google.com/store/apps/details?id=com.metallic.chiaki), [F-Droid](https://f-droid.org/packages/com.metallic.chiaki/) or download the APK from GitHub.
|
* **Android**: Install from [F-Droid](https://f-droid.org/packages/com.metallic.chiaki/) or download the APK from Sourcehut.
|
||||||
* **macOS**: Drag the application from the `.dmg` into your Applications folder.
|
* **macOS**: Drag the application from the `.dmg` into your Applications folder.
|
||||||
* **Windows**: Extract the `.zip` file and execute `chiaki.exe`.
|
* **Windows**: Extract the `.zip` file and execute `chiaki.exe`.
|
||||||
|
* **Switch**: Download the `.nro` file and copy it into the `switch/` directory on your SD card.
|
||||||
|
|
||||||
### Building from Source
|
### Building from Source
|
||||||
|
|
||||||
|
@ -49,15 +49,15 @@ cmake ..
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
For more detailed platform-specific instructions, see [doc/platform-build.md](doc/platform-build.md).
|
For more detailed platform-specific instructions, see [doc/platform-build.md](doc/platform-build.md) or [switch/](./switch/README.md) for Nintendo Switch.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
If your PS4 is on your local network, is turned on or in standby mode and does not have Discovery explicitly disabled, Chiaki should find it.
|
If your Console is on your local network, is turned on or in standby mode and does not have Discovery explicitly disabled, Chiaki should find it.
|
||||||
Otherwise, you can add it manually.
|
Otherwise, you can add it manually.
|
||||||
To do so, click the "+" icon in the top right, and enter your PS4's IP address.
|
To do so, click the "+" icon in the top right, and enter your Console's IP address.
|
||||||
|
|
||||||
You will then need to register your PS4 with Chiaki. You will need two more pieces of information to do this.
|
You will then need to register your Console with Chiaki. You will need two more pieces of information to do this.
|
||||||
|
|
||||||
### Obtaining your PSN AccountID
|
### Obtaining your PSN AccountID
|
||||||
|
|
||||||
|
@ -68,18 +68,18 @@ Simply run it in a terminal and follow the instructions. Once you know your ID,
|
||||||
|
|
||||||
### Obtaining a Registration PIN
|
### Obtaining a Registration PIN
|
||||||
|
|
||||||
To register a PS4 with a PIN, it must be put into registration mode. To do this, on your PS4, simply go to:
|
To register a Console with a PIN, it must be put into registration mode. To do this on a PS4, simply go to:
|
||||||
Settings -> Remote Play (ensure this is ticked) -> Add Device
|
Settings -> Remote Play -> Add Device, or on a PS5: Settings -> System -> Remote Play -> Link Device.
|
||||||
|
|
||||||
You can now double-click your PS4 in Chiaki's main window to start Remote Play.
|
You can now double-click your Console in Chiaki's main window to start Remote Play.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
This project has only been made possible because of the following Open Source projects:
|
This project has only been made possible because of the following Open Source projects:
|
||||||
[radare2](https://github.com/radare/radare2),
|
[Rizin](https://rizin.re),
|
||||||
[Cutter](https://cutter.re/),
|
[Cutter](https://cutter.re),
|
||||||
[Frida](https://www.frida.re/) and
|
[Frida](https://www.frida.re) and
|
||||||
[x64dbg](https://x64dbg.com/).
|
[x64dbg](https://x64dbg.com).
|
||||||
|
|
||||||
Also thanks to [delroth](https://github.com/delroth) for analyzing the registration and wakeup protocol,
|
Also thanks to [delroth](https://github.com/delroth) for analyzing the registration and wakeup protocol,
|
||||||
[grill2010](https://github.com/grill2010) for analyzing the PSN's OAuth Login,
|
[grill2010](https://github.com/grill2010) for analyzing the PSN's OAuth Login,
|
||||||
|
@ -88,17 +88,27 @@ extremely helpful information about FEC and error correction.
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
Created by Florian Märkl.
|
Created by Florian Märkl
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU Affero General Public License version 3
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
as published by the Free Software Foundation.
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Additional permission under GNU AGPL version 3 section 7
|
||||||
|
|
||||||
|
If you modify this program, or any covered work, by linking or
|
||||||
|
combining it with the OpenSSL project's OpenSSL library (or a
|
||||||
|
modified version of that library), containing parts covered by the
|
||||||
|
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
||||||
|
grants you additional permission to convey the resulting work.
|
||||||
|
Corresponding Source for a non-source form of such a combination
|
||||||
|
shall include the source code for the parts of OpenSSL used as well
|
||||||
|
as that of the covered work.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-parcelize'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
def rootCMakeLists = "../../CMakeLists.txt"
|
def rootCMakeLists = "../../CMakeLists.txt"
|
||||||
|
@ -18,13 +18,12 @@ def chiakiVersion = "$chiakiVersionMajor.$chiakiVersionMinor.$chiakiVersionPatch
|
||||||
println("Determined Chiaki Version: $chiakiVersion")
|
println("Determined Chiaki Version: $chiakiVersion")
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 33
|
||||||
buildToolsVersion "29.0.2"
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.metallic.chiaki"
|
applicationId "com.metallic.chiaki"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 33
|
||||||
versionCode 7
|
versionCode 12
|
||||||
versionName chiakiVersion
|
versionName chiakiVersion
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
@ -33,11 +32,16 @@ android {
|
||||||
"-DCHIAKI_ENABLE_GUI=OFF",
|
"-DCHIAKI_ENABLE_GUI=OFF",
|
||||||
"-DCHIAKI_ENABLE_SETSU=OFF",
|
"-DCHIAKI_ENABLE_SETSU=OFF",
|
||||||
"-DCHIAKI_ENABLE_ANDROID=ON",
|
"-DCHIAKI_ENABLE_ANDROID=ON",
|
||||||
|
"-DCHIAKI_ENABLE_FFMPEG_DECODER=OFF",
|
||||||
|
"-DCHIAKI_ENABLE_PI_DECODER=OFF",
|
||||||
"-DCHIAKI_LIB_ENABLE_OPUS=OFF",
|
"-DCHIAKI_LIB_ENABLE_OPUS=OFF",
|
||||||
"-DCHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT=ON"
|
"-DCHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT=ON"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -47,7 +51,7 @@ android {
|
||||||
}
|
}
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version "3.10.2+"
|
version "3.22.1"
|
||||||
path rootCMakeLists
|
path rootCMakeLists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,21 +65,14 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Properties properties = new Properties()
|
Properties properties = new Properties()
|
||||||
def propertiesFile = file("../local.properties")
|
def propertiesFile = file("../local.properties")
|
||||||
if (propertiesFile.exists()) {
|
if (propertiesFile.exists()) {
|
||||||
properties.load(propertiesFile.newDataInputStream())
|
properties.load(propertiesFile.newDataInputStream())
|
||||||
}
|
}
|
||||||
|
|
||||||
def keystoreFile = file("../../keystore.jks")
|
if(properties.containsKey("chiakiKeystore")) {
|
||||||
if(System.getenv("TRAVIS") == "true" && keystoreFile.exists()) {
|
|
||||||
println("Enabling Signing on Travis")
|
|
||||||
buildTypes.release.signingConfig = signingConfigs.release
|
|
||||||
signingConfigs.release.storeFile = keystoreFile
|
|
||||||
signingConfigs.release.storePassword = System.getenv("android_keystore_pw")
|
|
||||||
signingConfigs.release.keyAlias = "chiaki"
|
|
||||||
signingConfigs.release.keyPassword = System.getenv("android_keystore_alias_pw")
|
|
||||||
} else if(properties.containsKey("chiakiKeystore")) {
|
|
||||||
println("Enabling Local Signing")
|
println("Enabling Local Signing")
|
||||||
buildTypes.release.signingConfig = signingConfigs.release
|
buildTypes.release.signingConfig = signingConfigs.release
|
||||||
buildTypes.debug.signingConfig = signingConfigs.release
|
buildTypes.debug.signingConfig = signingConfigs.release
|
||||||
|
@ -94,31 +91,26 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
androidExtensions {
|
|
||||||
// for @Parcelize
|
|
||||||
experimental = true
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||||
implementation 'androidx.core:core-ktx:1.2.0'
|
implementation 'androidx.core:core-ktx:1.9.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0-beta02'
|
implementation 'com.google.android.material:material:1.8.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.5.1'
|
||||||
implementation "io.reactivex.rxjava2:rxjava:2.2.12"
|
implementation "io.reactivex.rxjava2:rxjava:2.2.20"
|
||||||
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
|
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
def room_version = "2.2.4"
|
def room_version = "2.5.0"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
implementation "androidx.room:room-rxjava2:$room_version"
|
implementation "androidx.room:room-rxjava2:$room_version"
|
||||||
implementation "com.squareup.moshi:moshi:1.9.2"
|
implementation "com.squareup.moshi:moshi:1.14.0"
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -27,7 +29,8 @@
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".main.MainActivity"
|
<activity android:name=".main.MainActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "audio-decoder.h"
|
#include "audio-decoder.h"
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_AUDIO_DECODER_H
|
#ifndef CHIAKI_JNI_AUDIO_DECODER_H
|
||||||
#define CHIAKI_JNI_AUDIO_DECODER_H
|
#define CHIAKI_JNI_AUDIO_DECODER_H
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "audio-output.h"
|
#include "audio-output.h"
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_AUDIO_OUTPUT_H
|
#ifndef CHIAKI_JNI_AUDIO_OUTPUT_H
|
||||||
#define CHIAKI_JNI_AUDIO_OUTPUT_H
|
#define CHIAKI_JNI_AUDIO_OUTPUT_H
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
@ -125,18 +110,18 @@ JNIEXPORT jstring JNICALL JNI_FCN(quitReasonToString)(JNIEnv *env, jobject obj,
|
||||||
return E->NewStringUTF(env, chiaki_quit_reason_string((ChiakiQuitReason)value));
|
return E->NewStringUTF(env, chiaki_quit_reason_string((ChiakiQuitReason)value));
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL JNI_FCN(quitReasonIsStopped)(JNIEnv *env, jobject obj, jint value)
|
JNIEXPORT jboolean JNICALL JNI_FCN(quitReasonIsError)(JNIEnv *env, jobject obj, jint value)
|
||||||
{
|
{
|
||||||
return value == CHIAKI_QUIT_REASON_STOPPED;
|
return chiaki_quit_reason_is_error(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL JNI_FCN(videoProfilePreset)(JNIEnv *env, jobject obj, jint resolution_preset, jint fps_preset)
|
JNIEXPORT jobject JNICALL JNI_FCN(videoProfilePreset)(JNIEnv *env, jobject obj, jint resolution_preset, jint fps_preset, jobject codec)
|
||||||
{
|
{
|
||||||
ChiakiConnectVideoProfile profile = { 0 };
|
ChiakiConnectVideoProfile profile = { 0 };
|
||||||
chiaki_connect_video_profile_preset(&profile, (ChiakiVideoResolutionPreset)resolution_preset, (ChiakiVideoFPSPreset)fps_preset);
|
chiaki_connect_video_profile_preset(&profile, (ChiakiVideoResolutionPreset)resolution_preset, (ChiakiVideoFPSPreset)fps_preset);
|
||||||
jclass profile_class = E->FindClass(env, BASE_PACKAGE"/ConnectVideoProfile");
|
jclass profile_class = E->FindClass(env, BASE_PACKAGE"/ConnectVideoProfile");
|
||||||
jmethodID profile_ctor = E->GetMethodID(env, profile_class, "<init>", "(IIII)V");
|
jmethodID profile_ctor = E->GetMethodID(env, profile_class, "<init>", "(IIIIL"BASE_PACKAGE"/Codec;)V");
|
||||||
return E->NewObject(env, profile_class, profile_ctor, profile.width, profile.height, profile.max_fps, profile.bitrate);
|
return E->NewObject(env, profile_class, profile_ctor, profile.width, profile.height, profile.max_fps, profile.bitrate, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct android_chiaki_session_t
|
typedef struct android_chiaki_session_t
|
||||||
|
@ -148,6 +133,7 @@ typedef struct android_chiaki_session_t
|
||||||
jmethodID java_session_event_connected_meth;
|
jmethodID java_session_event_connected_meth;
|
||||||
jmethodID java_session_event_login_pin_request_meth;
|
jmethodID java_session_event_login_pin_request_meth;
|
||||||
jmethodID java_session_event_quit_meth;
|
jmethodID java_session_event_quit_meth;
|
||||||
|
jmethodID java_session_event_rumble_meth;
|
||||||
jfieldID java_controller_state_buttons;
|
jfieldID java_controller_state_buttons;
|
||||||
jfieldID java_controller_state_l2_state;
|
jfieldID java_controller_state_l2_state;
|
||||||
jfieldID java_controller_state_r2_state;
|
jfieldID java_controller_state_r2_state;
|
||||||
|
@ -155,6 +141,20 @@ typedef struct android_chiaki_session_t
|
||||||
jfieldID java_controller_state_left_y;
|
jfieldID java_controller_state_left_y;
|
||||||
jfieldID java_controller_state_right_x;
|
jfieldID java_controller_state_right_x;
|
||||||
jfieldID java_controller_state_right_y;
|
jfieldID java_controller_state_right_y;
|
||||||
|
jfieldID java_controller_state_touches;
|
||||||
|
jfieldID java_controller_state_gyro_x;
|
||||||
|
jfieldID java_controller_state_gyro_y;
|
||||||
|
jfieldID java_controller_state_gyro_z;
|
||||||
|
jfieldID java_controller_state_accel_x;
|
||||||
|
jfieldID java_controller_state_accel_y;
|
||||||
|
jfieldID java_controller_state_accel_z;
|
||||||
|
jfieldID java_controller_state_orient_x;
|
||||||
|
jfieldID java_controller_state_orient_y;
|
||||||
|
jfieldID java_controller_state_orient_z;
|
||||||
|
jfieldID java_controller_state_orient_w;
|
||||||
|
jfieldID java_controller_touch_x;
|
||||||
|
jfieldID java_controller_touch_y;
|
||||||
|
jfieldID java_controller_touch_id;
|
||||||
|
|
||||||
AndroidChiakiVideoDecoder video_decoder;
|
AndroidChiakiVideoDecoder video_decoder;
|
||||||
AndroidChiakiAudioDecoder audio_decoder;
|
AndroidChiakiAudioDecoder audio_decoder;
|
||||||
|
@ -193,6 +193,14 @@ static void android_chiaki_event_cb(ChiakiEvent *event, void *user)
|
||||||
free(reason_str);
|
free(reason_str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CHIAKI_EVENT_RUMBLE:
|
||||||
|
E->CallVoidMethod(env, session->java_session,
|
||||||
|
session->java_session_event_rumble_meth,
|
||||||
|
(jint)event->rumble.left,
|
||||||
|
(jint)event->rumble.right);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*global_vm)->DetachCurrentThread(global_vm);
|
(*global_vm)->DetachCurrentThread(global_vm);
|
||||||
|
@ -213,13 +221,16 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
||||||
jclass result_class = E->GetObjectClass(env, result);
|
jclass result_class = E->GetObjectClass(env, result);
|
||||||
|
|
||||||
jclass connect_info_class = E->GetObjectClass(env, connect_info_obj);
|
jclass connect_info_class = E->GetObjectClass(env, connect_info_obj);
|
||||||
|
jboolean ps5 = E->GetBooleanField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "ps5", "Z"));
|
||||||
jstring host_string = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "host", "Ljava/lang/String;"));
|
jstring host_string = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "host", "Ljava/lang/String;"));
|
||||||
jbyteArray regist_key_array = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "registKey", "[B"));
|
jbyteArray regist_key_array = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "registKey", "[B"));
|
||||||
jbyteArray morning_array = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "morning", "[B"));
|
jbyteArray morning_array = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "morning", "[B"));
|
||||||
jobject connect_video_profile_obj = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "videoProfile", "L"BASE_PACKAGE"/ConnectVideoProfile;"));
|
jobject connect_video_profile_obj = E->GetObjectField(env, connect_info_obj, E->GetFieldID(env, connect_info_class, "videoProfile", "L"BASE_PACKAGE"/ConnectVideoProfile;"));
|
||||||
jclass connect_video_profile_class = E->GetObjectClass(env, connect_video_profile_obj);
|
jclass connect_video_profile_class = E->GetObjectClass(env, connect_video_profile_obj);
|
||||||
|
|
||||||
ChiakiConnectInfo connect_info;
|
ChiakiConnectInfo connect_info = { 0 };
|
||||||
|
connect_info.ps5 = ps5;
|
||||||
|
|
||||||
const char *str_borrow = E->GetStringUTFChars(env, host_string, NULL);
|
const char *str_borrow = E->GetStringUTFChars(env, host_string, NULL);
|
||||||
connect_info.host = host_str = strdup(str_borrow);
|
connect_info.host = host_str = strdup(str_borrow);
|
||||||
E->ReleaseStringUTFChars(env, host_string, str_borrow);
|
E->ReleaseStringUTFChars(env, host_string, str_borrow);
|
||||||
|
@ -254,6 +265,13 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
||||||
connect_info.video_profile.max_fps = (unsigned int)E->GetIntField(env, connect_video_profile_obj, E->GetFieldID(env, connect_video_profile_class, "maxFPS", "I"));
|
connect_info.video_profile.max_fps = (unsigned int)E->GetIntField(env, connect_video_profile_obj, E->GetFieldID(env, connect_video_profile_class, "maxFPS", "I"));
|
||||||
connect_info.video_profile.bitrate = (unsigned int)E->GetIntField(env, connect_video_profile_obj, E->GetFieldID(env, connect_video_profile_class, "bitrate", "I"));
|
connect_info.video_profile.bitrate = (unsigned int)E->GetIntField(env, connect_video_profile_obj, E->GetFieldID(env, connect_video_profile_class, "bitrate", "I"));
|
||||||
|
|
||||||
|
jobject codec_obj = E->GetObjectField(env, connect_video_profile_obj, E->GetFieldID(env, connect_video_profile_class, "codec", "L"BASE_PACKAGE"/Codec;"));
|
||||||
|
jclass codec_class = E->GetObjectClass(env, codec_obj);
|
||||||
|
jint target_value = E->GetIntField(env, codec_obj, E->GetFieldID(env, codec_class, "value", "I"));
|
||||||
|
connect_info.video_profile.codec = (ChiakiCodec)target_value;
|
||||||
|
|
||||||
|
connect_info.video_profile_auto_downgrade = true;
|
||||||
|
|
||||||
session = CHIAKI_NEW(AndroidChiakiSession);
|
session = CHIAKI_NEW(AndroidChiakiSession);
|
||||||
if(!session)
|
if(!session)
|
||||||
{
|
{
|
||||||
|
@ -262,7 +280,8 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
||||||
}
|
}
|
||||||
memset(session, 0, sizeof(AndroidChiakiSession));
|
memset(session, 0, sizeof(AndroidChiakiSession));
|
||||||
session->log = log;
|
session->log = log;
|
||||||
err = android_chiaki_video_decoder_init(&session->video_decoder, log, connect_info.video_profile.width, connect_info.video_profile.height);
|
err = android_chiaki_video_decoder_init(&session->video_decoder, log, connect_info.video_profile.width, connect_info.video_profile.height,
|
||||||
|
connect_info.ps5 ? connect_info.video_profile.codec : CHIAKI_CODEC_H264);
|
||||||
if(err != CHIAKI_ERR_SUCCESS)
|
if(err != CHIAKI_ERR_SUCCESS)
|
||||||
{
|
{
|
||||||
free(session);
|
free(session);
|
||||||
|
@ -300,6 +319,7 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
||||||
session->java_session_event_connected_meth = E->GetMethodID(env, session->java_session_class, "eventConnected", "()V");
|
session->java_session_event_connected_meth = E->GetMethodID(env, session->java_session_class, "eventConnected", "()V");
|
||||||
session->java_session_event_login_pin_request_meth = E->GetMethodID(env, session->java_session_class, "eventLoginPinRequest", "(Z)V");
|
session->java_session_event_login_pin_request_meth = E->GetMethodID(env, session->java_session_class, "eventLoginPinRequest", "(Z)V");
|
||||||
session->java_session_event_quit_meth = E->GetMethodID(env, session->java_session_class, "eventQuit", "(ILjava/lang/String;)V");
|
session->java_session_event_quit_meth = E->GetMethodID(env, session->java_session_class, "eventQuit", "(ILjava/lang/String;)V");
|
||||||
|
session->java_session_event_rumble_meth = E->GetMethodID(env, session->java_session_class, "eventRumble", "(II)V");
|
||||||
|
|
||||||
jclass controller_state_class = E->FindClass(env, BASE_PACKAGE"/ControllerState");
|
jclass controller_state_class = E->FindClass(env, BASE_PACKAGE"/ControllerState");
|
||||||
session->java_controller_state_buttons = E->GetFieldID(env, controller_state_class, "buttons", "I");
|
session->java_controller_state_buttons = E->GetFieldID(env, controller_state_class, "buttons", "I");
|
||||||
|
@ -309,6 +329,22 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
||||||
session->java_controller_state_left_y = E->GetFieldID(env, controller_state_class, "leftY", "S");
|
session->java_controller_state_left_y = E->GetFieldID(env, controller_state_class, "leftY", "S");
|
||||||
session->java_controller_state_right_x = E->GetFieldID(env, controller_state_class, "rightX", "S");
|
session->java_controller_state_right_x = E->GetFieldID(env, controller_state_class, "rightX", "S");
|
||||||
session->java_controller_state_right_y = E->GetFieldID(env, controller_state_class, "rightY", "S");
|
session->java_controller_state_right_y = E->GetFieldID(env, controller_state_class, "rightY", "S");
|
||||||
|
session->java_controller_state_touches = E->GetFieldID(env, controller_state_class, "touches", "[L"BASE_PACKAGE"/ControllerTouch;");
|
||||||
|
session->java_controller_state_gyro_x = E->GetFieldID(env, controller_state_class, "gyroX", "F");
|
||||||
|
session->java_controller_state_gyro_y = E->GetFieldID(env, controller_state_class, "gyroY", "F");
|
||||||
|
session->java_controller_state_gyro_z = E->GetFieldID(env, controller_state_class, "gyroZ", "F");
|
||||||
|
session->java_controller_state_accel_x = E->GetFieldID(env, controller_state_class, "accelX", "F");
|
||||||
|
session->java_controller_state_accel_y = E->GetFieldID(env, controller_state_class, "accelY", "F");
|
||||||
|
session->java_controller_state_accel_z = E->GetFieldID(env, controller_state_class, "accelZ", "F");
|
||||||
|
session->java_controller_state_orient_x = E->GetFieldID(env, controller_state_class, "orientX", "F");
|
||||||
|
session->java_controller_state_orient_y = E->GetFieldID(env, controller_state_class, "orientY", "F");
|
||||||
|
session->java_controller_state_orient_z = E->GetFieldID(env, controller_state_class, "orientZ", "F");
|
||||||
|
session->java_controller_state_orient_w = E->GetFieldID(env, controller_state_class, "orientW", "F");
|
||||||
|
|
||||||
|
jclass controller_touch_class = E->FindClass(env, BASE_PACKAGE"/ControllerTouch");
|
||||||
|
session->java_controller_touch_x = E->GetFieldID(env, controller_touch_class, "x", "S");
|
||||||
|
session->java_controller_touch_y = E->GetFieldID(env, controller_touch_class, "y", "S");
|
||||||
|
session->java_controller_touch_id = E->GetFieldID(env, controller_touch_class, "id", "B");
|
||||||
|
|
||||||
chiaki_session_set_event_cb(&session->session, android_chiaki_event_cb, session);
|
chiaki_session_set_event_cb(&session->session, android_chiaki_event_cb, session);
|
||||||
chiaki_session_set_video_sample_cb(&session->session, android_chiaki_video_decoder_video_sample, &session->video_decoder);
|
chiaki_session_set_video_sample_cb(&session->session, android_chiaki_video_decoder_video_sample, &session->video_decoder);
|
||||||
|
@ -377,7 +413,8 @@ JNIEXPORT void JNICALL JNI_FCN(sessionSetSurface)(JNIEnv *env, jobject obj, jlon
|
||||||
JNIEXPORT void JNICALL JNI_FCN(sessionSetControllerState)(JNIEnv *env, jobject obj, jlong ptr, jobject controller_state_java)
|
JNIEXPORT void JNICALL JNI_FCN(sessionSetControllerState)(JNIEnv *env, jobject obj, jlong ptr, jobject controller_state_java)
|
||||||
{
|
{
|
||||||
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
|
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
|
||||||
ChiakiControllerState controller_state = { 0 };
|
ChiakiControllerState controller_state;
|
||||||
|
chiaki_controller_state_set_idle(&controller_state);
|
||||||
controller_state.buttons = (uint32_t)E->GetIntField(env, controller_state_java, session->java_controller_state_buttons);
|
controller_state.buttons = (uint32_t)E->GetIntField(env, controller_state_java, session->java_controller_state_buttons);
|
||||||
controller_state.l2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_l2_state);
|
controller_state.l2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_l2_state);
|
||||||
controller_state.r2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_r2_state);
|
controller_state.r2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_r2_state);
|
||||||
|
@ -385,6 +422,34 @@ JNIEXPORT void JNICALL JNI_FCN(sessionSetControllerState)(JNIEnv *env, jobject o
|
||||||
controller_state.left_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_left_y);
|
controller_state.left_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_left_y);
|
||||||
controller_state.right_x = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_x);
|
controller_state.right_x = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_x);
|
||||||
controller_state.right_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_y);
|
controller_state.right_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_y);
|
||||||
|
jobjectArray touch_array = E->GetObjectField(env, controller_state_java, session->java_controller_state_touches);
|
||||||
|
size_t touch_array_len = (size_t)E->GetArrayLength(env, touch_array);
|
||||||
|
for(size_t i = 0; i < CHIAKI_CONTROLLER_TOUCHES_MAX; i++)
|
||||||
|
{
|
||||||
|
if(i < touch_array_len)
|
||||||
|
{
|
||||||
|
jobject touch = E->GetObjectArrayElement(env, touch_array, i);
|
||||||
|
controller_state.touches[i].x = (uint16_t)E->GetShortField(env, touch, session->java_controller_touch_x);
|
||||||
|
controller_state.touches[i].y = (uint16_t)E->GetShortField(env, touch, session->java_controller_touch_y);
|
||||||
|
controller_state.touches[i].id = (int8_t)E->GetByteField(env, touch, session->java_controller_touch_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controller_state.touches[i].x = 0;
|
||||||
|
controller_state.touches[i].y = 0;
|
||||||
|
controller_state.touches[i].id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller_state.gyro_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_x);
|
||||||
|
controller_state.gyro_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_y);
|
||||||
|
controller_state.gyro_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_z);
|
||||||
|
controller_state.accel_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_x);
|
||||||
|
controller_state.accel_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_y);
|
||||||
|
controller_state.accel_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_z);
|
||||||
|
controller_state.orient_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_x);
|
||||||
|
controller_state.orient_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_y);
|
||||||
|
controller_state.orient_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_z);
|
||||||
|
controller_state.orient_w = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_w);
|
||||||
chiaki_session_set_controller_state(&session->session, &controller_state);
|
chiaki_session_set_controller_state(&session->session, &controller_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,11 +654,11 @@ JNIEXPORT void JNICALL JNI_FCN(discoveryServiceFree)(JNIEnv *env, jobject obj, j
|
||||||
free(service);
|
free(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL JNI_FCN(discoveryServiceWakeup)(JNIEnv *env, jobject obj, jlong ptr, jstring host_string, jlong user_credential)
|
JNIEXPORT jint JNICALL JNI_FCN(discoveryServiceWakeup)(JNIEnv *env, jobject obj, jlong ptr, jstring host_string, jlong user_credential, jboolean ps5)
|
||||||
{
|
{
|
||||||
AndroidDiscoveryService *service = (AndroidDiscoveryService *)ptr;
|
AndroidDiscoveryService *service = (AndroidDiscoveryService *)ptr;
|
||||||
const char *host = E->GetStringUTFChars(env, host_string, NULL);
|
const char *host = E->GetStringUTFChars(env, host_string, NULL);
|
||||||
ChiakiErrorCode r = chiaki_discovery_wakeup(&global_log, service ? &service->service.discovery : NULL, host, (uint64_t)user_credential);
|
ChiakiErrorCode r = chiaki_discovery_wakeup(&global_log, service ? &service->service.discovery : NULL, host, (uint64_t)user_credential, ps5);
|
||||||
E->ReleaseStringUTFChars(env, host_string, host);
|
E->ReleaseStringUTFChars(env, host_string, host);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -607,6 +672,8 @@ typedef struct android_chiaki_regist_t
|
||||||
jobject java_regist;
|
jobject java_regist;
|
||||||
jmethodID java_regist_event_meth;
|
jmethodID java_regist_event_meth;
|
||||||
|
|
||||||
|
jclass java_target_class;
|
||||||
|
|
||||||
jobject java_regist_event_canceled;
|
jobject java_regist_event_canceled;
|
||||||
jobject java_regist_event_failed;
|
jobject java_regist_event_failed;
|
||||||
jclass java_regist_event_success_class;
|
jclass java_regist_event_success_class;
|
||||||
|
@ -616,6 +683,12 @@ typedef struct android_chiaki_regist_t
|
||||||
jmethodID java_regist_host_ctor;
|
jmethodID java_regist_host_ctor;
|
||||||
} AndroidChiakiRegist;
|
} AndroidChiakiRegist;
|
||||||
|
|
||||||
|
static jobject create_jni_target(JNIEnv *env, jclass target_class, ChiakiTarget target)
|
||||||
|
{
|
||||||
|
jmethodID meth = E->GetStaticMethodID(env, target_class, "fromValue", "(I)L"BASE_PACKAGE"/Target;");
|
||||||
|
return E->CallStaticObjectMethod(env, target_class, meth, (jint)target);
|
||||||
|
}
|
||||||
|
|
||||||
static void android_chiaki_regist_cb(ChiakiRegistEvent *event, void *user)
|
static void android_chiaki_regist_cb(ChiakiRegistEvent *event, void *user)
|
||||||
{
|
{
|
||||||
AndroidChiakiRegist *regist = user;
|
AndroidChiakiRegist *regist = user;
|
||||||
|
@ -637,12 +710,13 @@ static void android_chiaki_regist_cb(ChiakiRegistEvent *event, void *user)
|
||||||
{
|
{
|
||||||
ChiakiRegisteredHost *host = event->registered_host;
|
ChiakiRegisteredHost *host = event->registered_host;
|
||||||
jobject java_host = E->NewObject(env, regist->java_regist_host_class, regist->java_regist_host_ctor,
|
jobject java_host = E->NewObject(env, regist->java_regist_host_class, regist->java_regist_host_ctor,
|
||||||
|
create_jni_target(env, regist->java_target_class, host->target),
|
||||||
jnistr_from_ascii(env, host->ap_ssid),
|
jnistr_from_ascii(env, host->ap_ssid),
|
||||||
jnistr_from_ascii(env, host->ap_bssid),
|
jnistr_from_ascii(env, host->ap_bssid),
|
||||||
jnistr_from_ascii(env, host->ap_key),
|
jnistr_from_ascii(env, host->ap_key),
|
||||||
jnistr_from_ascii(env, host->ap_name),
|
jnistr_from_ascii(env, host->ap_name),
|
||||||
jnibytearray_create(env, host->ps4_mac, sizeof(host->ps4_mac)),
|
jnibytearray_create(env, host->server_mac, sizeof(host->server_mac)),
|
||||||
jnistr_from_ascii(env, host->ps4_nickname),
|
jnistr_from_ascii(env, host->server_nickname),
|
||||||
jnibytearray_create(env, (const uint8_t *)host->rp_regist_key, sizeof(host->rp_regist_key)),
|
jnibytearray_create(env, (const uint8_t *)host->rp_regist_key, sizeof(host->rp_regist_key)),
|
||||||
(jint)host->rp_key_type,
|
(jint)host->rp_key_type,
|
||||||
jnibytearray_create(env, host->rp_key, sizeof(host->rp_key)));
|
jnibytearray_create(env, host->rp_key, sizeof(host->rp_key)));
|
||||||
|
@ -661,6 +735,7 @@ static void android_chiaki_regist_fini_partial(JNIEnv *env, AndroidChiakiRegist
|
||||||
{
|
{
|
||||||
android_chiaki_jni_log_fini(®ist->log, env);
|
android_chiaki_jni_log_fini(®ist->log, env);
|
||||||
E->DeleteGlobalRef(env, regist->java_regist);
|
E->DeleteGlobalRef(env, regist->java_regist);
|
||||||
|
E->DeleteGlobalRef(env, regist->java_target_class);
|
||||||
E->DeleteGlobalRef(env, regist->java_regist_event_canceled);
|
E->DeleteGlobalRef(env, regist->java_regist_event_canceled);
|
||||||
E->DeleteGlobalRef(env, regist->java_regist_event_failed);
|
E->DeleteGlobalRef(env, regist->java_regist_event_failed);
|
||||||
E->DeleteGlobalRef(env, regist->java_regist_event_success_class);
|
E->DeleteGlobalRef(env, regist->java_regist_event_success_class);
|
||||||
|
@ -683,6 +758,8 @@ JNIEXPORT void JNICALL JNI_FCN(registStart)(JNIEnv *env, jobject obj, jobject re
|
||||||
regist->java_regist = E->NewGlobalRef(env, java_regist);
|
regist->java_regist = E->NewGlobalRef(env, java_regist);
|
||||||
regist->java_regist_event_meth = E->GetMethodID(env, E->GetObjectClass(env, regist->java_regist), "event", "(L"BASE_PACKAGE"/RegistEvent;)V");
|
regist->java_regist_event_meth = E->GetMethodID(env, E->GetObjectClass(env, regist->java_regist), "event", "(L"BASE_PACKAGE"/RegistEvent;)V");
|
||||||
|
|
||||||
|
regist->java_target_class = E->NewGlobalRef(env, E->FindClass(env, BASE_PACKAGE"/Target"));
|
||||||
|
|
||||||
regist->java_regist_event_canceled = E->NewGlobalRef(env, get_kotlin_global_object(env, BASE_PACKAGE"/RegistEventCanceled"));
|
regist->java_regist_event_canceled = E->NewGlobalRef(env, get_kotlin_global_object(env, BASE_PACKAGE"/RegistEventCanceled"));
|
||||||
regist->java_regist_event_failed = E->NewGlobalRef(env, get_kotlin_global_object(env, BASE_PACKAGE"/RegistEventFailed"));
|
regist->java_regist_event_failed = E->NewGlobalRef(env, get_kotlin_global_object(env, BASE_PACKAGE"/RegistEventFailed"));
|
||||||
regist->java_regist_event_success_class = E->NewGlobalRef(env, E->FindClass(env, BASE_PACKAGE"/RegistEventSuccess"));
|
regist->java_regist_event_success_class = E->NewGlobalRef(env, E->FindClass(env, BASE_PACKAGE"/RegistEventSuccess"));
|
||||||
|
@ -690,12 +767,13 @@ JNIEXPORT void JNICALL JNI_FCN(registStart)(JNIEnv *env, jobject obj, jobject re
|
||||||
|
|
||||||
regist->java_regist_host_class = E->NewGlobalRef(env, E->FindClass(env, BASE_PACKAGE"/RegistHost"));
|
regist->java_regist_host_class = E->NewGlobalRef(env, E->FindClass(env, BASE_PACKAGE"/RegistHost"));
|
||||||
regist->java_regist_host_ctor = E->GetMethodID(env, regist->java_regist_host_class, "<init>", "("
|
regist->java_regist_host_ctor = E->GetMethodID(env, regist->java_regist_host_class, "<init>", "("
|
||||||
|
"L"BASE_PACKAGE"/Target;" // target: Target
|
||||||
"Ljava/lang/String;" // apSsid: String
|
"Ljava/lang/String;" // apSsid: String
|
||||||
"Ljava/lang/String;" // apBssid: String
|
"Ljava/lang/String;" // apBssid: String
|
||||||
"Ljava/lang/String;" // apKey: String
|
"Ljava/lang/String;" // apKey: String
|
||||||
"Ljava/lang/String;" // apName: String
|
"Ljava/lang/String;" // apName: String
|
||||||
"[B" // ps4Mac: ByteArray
|
"[B" // serverMac: ByteArray
|
||||||
"Ljava/lang/String;" // ps4Nickname: String
|
"Ljava/lang/String;" // serverNickname: String
|
||||||
"[B" // rpRegistKey: ByteArray
|
"[B" // rpRegistKey: ByteArray
|
||||||
"I" // rpKeyType: UInt
|
"I" // rpKeyType: UInt
|
||||||
"[B" // rpKey: ByteArray
|
"[B" // rpKey: ByteArray
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_H
|
#ifndef CHIAKI_JNI_H
|
||||||
#define CHIAKI_JNI_H
|
#define CHIAKI_JNI_H
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_CIRCULARBUF_HPP
|
#ifndef CHIAKI_JNI_CIRCULARBUF_HPP
|
||||||
#define CHIAKI_JNI_CIRCULARBUF_HPP
|
#define CHIAKI_JNI_CIRCULARBUF_HPP
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_LOG_H
|
#ifndef CHIAKI_JNI_LOG_H
|
||||||
#define CHIAKI_JNI_LOG_H
|
#define CHIAKI_JNI_LOG_H
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0ab5b12a5bc3630a3d6c83b20eed2a669ebf7a24
|
Subproject commit 8740d0fc321a55489dbbf6067298201b7d2e106d
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "video-decoder.h"
|
#include "video-decoder.h"
|
||||||
|
|
||||||
|
@ -29,36 +14,46 @@
|
||||||
|
|
||||||
static void *android_chiaki_video_decoder_output_thread_func(void *user);
|
static void *android_chiaki_video_decoder_output_thread_func(void *user);
|
||||||
|
|
||||||
ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *decoder, ChiakiLog *log, int32_t target_width, int32_t target_height)
|
ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *decoder, ChiakiLog *log, int32_t target_width, int32_t target_height, ChiakiCodec codec)
|
||||||
{
|
{
|
||||||
decoder->log = log;
|
decoder->log = log;
|
||||||
decoder->codec = NULL;
|
decoder->codec = NULL;
|
||||||
decoder->timestamp_cur = 0;
|
decoder->timestamp_cur = 0;
|
||||||
decoder->target_width = target_width;
|
decoder->target_width = target_width;
|
||||||
decoder->target_height = target_height;
|
decoder->target_height = target_height;
|
||||||
|
decoder->target_codec = codec;
|
||||||
|
decoder->shutdown_output = false;
|
||||||
return chiaki_mutex_init(&decoder->codec_mutex, false);
|
return chiaki_mutex_init(&decoder->codec_mutex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kill_decoder(AndroidChiakiVideoDecoder *decoder)
|
||||||
|
{
|
||||||
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
|
decoder->shutdown_output = true;
|
||||||
|
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, 1000);
|
||||||
|
if(codec_buf_index >= 0)
|
||||||
|
{
|
||||||
|
CHIAKI_LOGI(decoder->log, "Video Decoder sending EOS buffer");
|
||||||
|
AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, 0, decoder->timestamp_cur++, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
|
||||||
|
AMediaCodec_stop(decoder->codec);
|
||||||
|
chiaki_mutex_unlock(&decoder->codec_mutex);
|
||||||
|
chiaki_thread_join(&decoder->output_thread, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHIAKI_LOGE(decoder->log, "Failed to get input buffer for shutting down Video Decoder!");
|
||||||
|
AMediaCodec_stop(decoder->codec);
|
||||||
|
chiaki_mutex_unlock(&decoder->codec_mutex);
|
||||||
|
}
|
||||||
|
AMediaCodec_delete(decoder->codec);
|
||||||
|
decoder->codec = NULL;
|
||||||
|
decoder->shutdown_output = false;
|
||||||
|
}
|
||||||
|
|
||||||
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
|
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
|
||||||
{
|
{
|
||||||
if(decoder->codec)
|
if(decoder->codec)
|
||||||
{
|
kill_decoder(decoder);
|
||||||
chiaki_mutex_lock(&decoder->codec_mutex);
|
|
||||||
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, -1);
|
|
||||||
if(codec_buf_index >= 0)
|
|
||||||
{
|
|
||||||
CHIAKI_LOGI(decoder->log, "Video Decoder sending EOS buffer");
|
|
||||||
AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, 0, decoder->timestamp_cur++, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
chiaki_mutex_unlock(&decoder->codec_mutex);
|
|
||||||
chiaki_thread_join(&decoder->output_thread, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHIAKI_LOGE(decoder->log, "Failed to get input buffer for shutting down Video Decoder!");
|
|
||||||
chiaki_mutex_unlock(&decoder->codec_mutex);
|
|
||||||
}
|
|
||||||
AMediaCodec_delete(decoder->codec);
|
|
||||||
}
|
|
||||||
chiaki_mutex_fini(&decoder->codec_mutex);
|
chiaki_mutex_fini(&decoder->codec_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +61,16 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
|
||||||
{
|
{
|
||||||
chiaki_mutex_lock(&decoder->codec_mutex);
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
|
|
||||||
|
if(!surface)
|
||||||
|
{
|
||||||
|
if(decoder->codec)
|
||||||
|
{
|
||||||
|
kill_decoder(decoder);
|
||||||
|
CHIAKI_LOGI(decoder->log, "Decoder shut down after surface was removed");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(decoder->codec)
|
if(decoder->codec)
|
||||||
{
|
{
|
||||||
#if __ANDROID_API__ >= 23
|
#if __ANDROID_API__ >= 23
|
||||||
|
@ -82,7 +87,8 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
|
||||||
|
|
||||||
decoder->window = ANativeWindow_fromSurface(env, surface);
|
decoder->window = ANativeWindow_fromSurface(env, surface);
|
||||||
|
|
||||||
const char *mime = "video/avc";
|
const char *mime = chiaki_codec_is_h265(decoder->target_codec) ? "video/hevc" : "video/avc";
|
||||||
|
CHIAKI_LOGI(decoder->log, "Initializing decoder with mime %s", mime);
|
||||||
|
|
||||||
decoder->codec = AMediaCodec_createDecoderByType(mime);
|
decoder->codec = AMediaCodec_createDecoderByType(mime);
|
||||||
if(!decoder->codec)
|
if(!decoder->codec)
|
||||||
|
@ -196,6 +202,17 @@ static void *android_chiaki_video_decoder_output_thread_func(void *user)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
|
bool shutdown = decoder->shutdown_output;
|
||||||
|
chiaki_mutex_unlock(&decoder->codec_mutex);
|
||||||
|
if(shutdown)
|
||||||
|
{
|
||||||
|
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread detected shutdown after reported error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread exiting");
|
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread exiting");
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHIAKI_JNI_VIDEO_DECODER_H
|
#ifndef CHIAKI_JNI_VIDEO_DECODER_H
|
||||||
#define CHIAKI_JNI_VIDEO_DECODER_H
|
#define CHIAKI_JNI_VIDEO_DECODER_H
|
||||||
|
@ -34,11 +19,13 @@ typedef struct android_chiaki_video_decoder_t
|
||||||
ANativeWindow *window;
|
ANativeWindow *window;
|
||||||
uint64_t timestamp_cur;
|
uint64_t timestamp_cur;
|
||||||
ChiakiThread output_thread;
|
ChiakiThread output_thread;
|
||||||
|
bool shutdown_output;
|
||||||
int32_t target_width;
|
int32_t target_width;
|
||||||
int32_t target_height;
|
int32_t target_height;
|
||||||
|
ChiakiCodec target_codec;
|
||||||
} AndroidChiakiVideoDecoder;
|
} AndroidChiakiVideoDecoder;
|
||||||
|
|
||||||
ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *decoder, ChiakiLog *log, int32_t target_width, int32_t target_height);
|
ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *decoder, ChiakiLog *log, int32_t target_width, int32_t target_height, ChiakiCodec codec);
|
||||||
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder);
|
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder);
|
||||||
void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder, JNIEnv *env, jobject surface);
|
void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder, JNIEnv *env, jobject surface);
|
||||||
bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, void *user);
|
bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, void *user);
|
||||||
|
|
|
@ -1,27 +1,15 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import com.metallic.chiaki.lib.Target
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
version = 1,
|
version = 2,
|
||||||
entities = [RegisteredHost::class, ManualHost::class])
|
entities = [RegisteredHost::class, ManualHost::class])
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class AppDatabase: RoomDatabase()
|
abstract class AppDatabase: RoomDatabase()
|
||||||
|
@ -31,6 +19,18 @@ abstract class AppDatabase: RoomDatabase()
|
||||||
abstract fun importDao(): ImportDao
|
abstract fun importDao(): ImportDao
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MIGRATION_1_2 = object : Migration(1, 2)
|
||||||
|
{
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase)
|
||||||
|
{
|
||||||
|
database.execSQL("ALTER TABLE registered_host ADD target INTEGER NOT NULL DEFAULT 1000")
|
||||||
|
database.execSQL("CREATE TABLE `new_registered_host` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `target` INTEGER NOT NULL, `ap_ssid` TEXT, `ap_bssid` TEXT, `ap_key` TEXT, `ap_name` TEXT, `server_mac` INTEGER NOT NULL, `server_nickname` TEXT, `rp_regist_key` BLOB NOT NULL, `rp_key_type` INTEGER NOT NULL, `rp_key` BLOB NOT NULL)");
|
||||||
|
database.execSQL("INSERT INTO `new_registered_host` SELECT `id`, `target`, `ap_ssid`, `ap_bssid`, `ap_key`, `ap_name`, `ps4_mac`, `ps4_nickname`, `rp_regist_key`, `rp_key_type`, `rp_key` FROM `registered_host`")
|
||||||
|
database.execSQL("DROP TABLE registered_host")
|
||||||
|
database.execSQL("ALTER TABLE new_registered_host RENAME TO registered_host")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var database: AppDatabase? = null
|
private var database: AppDatabase? = null
|
||||||
fun getDatabase(context: Context): AppDatabase
|
fun getDatabase(context: Context): AppDatabase
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,9 @@ fun getDatabase(context: Context): AppDatabase
|
||||||
val db = Room.databaseBuilder(
|
val db = Room.databaseBuilder(
|
||||||
context.applicationContext,
|
context.applicationContext,
|
||||||
AppDatabase::class.java,
|
AppDatabase::class.java,
|
||||||
"chiaki").build()
|
"chiaki")
|
||||||
|
.addMigrations(MIGRATION_1_2)
|
||||||
|
.build()
|
||||||
database = db
|
database = db
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
@ -52,4 +54,10 @@ private class Converters
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun macToValue(addr: MacAddress) = addr.value
|
fun macToValue(addr: MacAddress) = addr.value
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun targetFromValue(v: Int) = Target.fromValue(v)
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun targetToValue(target: Target) = target.value
|
||||||
}
|
}
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
@ -25,6 +10,7 @@ sealed class DisplayHost
|
||||||
abstract val host: String
|
abstract val host: String
|
||||||
abstract val name: String?
|
abstract val name: String?
|
||||||
abstract val id: String?
|
abstract val id: String?
|
||||||
|
abstract val isPS5: Boolean
|
||||||
|
|
||||||
val isRegistered get() = registeredHost != null
|
val isRegistered get() = registeredHost != null
|
||||||
}
|
}
|
||||||
|
@ -35,8 +21,9 @@ class DiscoveredDisplayHost(
|
||||||
): DisplayHost()
|
): DisplayHost()
|
||||||
{
|
{
|
||||||
override val host get() = discoveredHost.hostAddr ?: ""
|
override val host get() = discoveredHost.hostAddr ?: ""
|
||||||
override val name get() = discoveredHost.hostName ?: registeredHost?.ps4Nickname
|
override val name get() = discoveredHost.hostName ?: registeredHost?.serverNickname
|
||||||
override val id get() = discoveredHost.hostId ?: registeredHost?.ps4Mac?.toString()
|
override val id get() = discoveredHost.hostId ?: registeredHost?.serverMac?.toString()
|
||||||
|
override val isPS5 get() = discoveredHost.isPS5
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
if(other !is DiscoveredDisplayHost)
|
if(other !is DiscoveredDisplayHost)
|
||||||
|
@ -55,8 +42,9 @@ class ManualDisplayHost(
|
||||||
): DisplayHost()
|
): DisplayHost()
|
||||||
{
|
{
|
||||||
override val host get() = manualHost.host
|
override val host get() = manualHost.host
|
||||||
override val name get() = registeredHost?.ps4Nickname
|
override val name get() = registeredHost?.serverNickname
|
||||||
override val id get() = registeredHost?.ps4Mac?.toString()
|
override val id get() = registeredHost?.serverMac?.toString()
|
||||||
|
override val isPS5: Boolean get() = registeredHost?.target?.isPS5 ?: false
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
override fun equals(other: Any?): Boolean =
|
||||||
if(other !is ManualDisplayHost)
|
if(other !is ManualDisplayHost)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,9 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import androidx.room.ForeignKey.SET_NULL
|
import androidx.room.ForeignKey.Companion.SET_NULL
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
@ -22,6 +7,7 @@ import android.content.SharedPreferences
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
|
import com.metallic.chiaki.lib.Codec
|
||||||
import com.metallic.chiaki.lib.ConnectVideoProfile
|
import com.metallic.chiaki.lib.ConnectVideoProfile
|
||||||
import com.metallic.chiaki.lib.VideoFPSPreset
|
import com.metallic.chiaki.lib.VideoFPSPreset
|
||||||
import com.metallic.chiaki.lib.VideoResolutionPreset
|
import com.metallic.chiaki.lib.VideoResolutionPreset
|
||||||
|
@ -46,12 +32,20 @@ class Preferences(context: Context)
|
||||||
FPS_60("60", R.string.preferences_fps_title_60, VideoFPSPreset.FPS_60)
|
FPS_60("60", R.string.preferences_fps_title_60, VideoFPSPreset.FPS_60)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Codec(val value: String, @StringRes val title: Int, val codec: com.metallic.chiaki.lib.Codec)
|
||||||
|
{
|
||||||
|
CODEC_H264("h264", R.string.preferences_codec_title_h264, com.metallic.chiaki.lib.Codec.CODEC_H264),
|
||||||
|
CODEC_H265("h265", R.string.preferences_codec_title_h265, com.metallic.chiaki.lib.Codec.CODEC_H265)
|
||||||
|
}
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
{
|
{
|
||||||
val resolutionDefault = Resolution.RES_720P
|
val resolutionDefault = Resolution.RES_720P
|
||||||
val resolutionAll = Resolution.values()
|
val resolutionAll = Resolution.values()
|
||||||
val fpsDefault = FPS.FPS_60
|
val fpsDefault = FPS.FPS_60
|
||||||
val fpsAll = FPS.values()
|
val fpsAll = FPS.values()
|
||||||
|
val codecDefault = Codec.CODEC_H265
|
||||||
|
val codecAll = Codec.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -74,11 +68,26 @@ class Preferences(context: Context)
|
||||||
get() = sharedPreferences.getBoolean(onScreenControlsEnabledKey, true)
|
get() = sharedPreferences.getBoolean(onScreenControlsEnabledKey, true)
|
||||||
set(value) { sharedPreferences.edit().putBoolean(onScreenControlsEnabledKey, value).apply() }
|
set(value) { sharedPreferences.edit().putBoolean(onScreenControlsEnabledKey, value).apply() }
|
||||||
|
|
||||||
val touchpadOnlyEnabledKey get() = resources.getString(R.string.preferences_touchpad_only_key)
|
val touchpadOnlyEnabledKey get() = resources.getString(R.string.preferences_touchpad_only_enabled_key)
|
||||||
var touchpadOnlyEnabled
|
var touchpadOnlyEnabled
|
||||||
get() = sharedPreferences.getBoolean(touchpadOnlyEnabledKey, false)
|
get() = sharedPreferences.getBoolean(touchpadOnlyEnabledKey, false)
|
||||||
set(value) { sharedPreferences.edit().putBoolean(touchpadOnlyEnabledKey, value).apply() }
|
set(value) { sharedPreferences.edit().putBoolean(touchpadOnlyEnabledKey, value).apply() }
|
||||||
|
|
||||||
|
val rumbleEnabledKey get() = resources.getString(R.string.preferences_rumble_enabled_key)
|
||||||
|
var rumbleEnabled
|
||||||
|
get() = sharedPreferences.getBoolean(rumbleEnabledKey, true)
|
||||||
|
set(value) { sharedPreferences.edit().putBoolean(rumbleEnabledKey, value).apply() }
|
||||||
|
|
||||||
|
val motionEnabledKey get() = resources.getString(R.string.preferences_motion_enabled_key)
|
||||||
|
var motionEnabled
|
||||||
|
get() = sharedPreferences.getBoolean(motionEnabledKey, true)
|
||||||
|
set(value) { sharedPreferences.edit().putBoolean(motionEnabledKey, value).apply() }
|
||||||
|
|
||||||
|
val buttonHapticEnabledKey get() = resources.getString(R.string.preferences_button_haptic_enabled_key)
|
||||||
|
var buttonHapticEnabled
|
||||||
|
get() = sharedPreferences.getBoolean(buttonHapticEnabledKey, true)
|
||||||
|
set(value) { sharedPreferences.edit().putBoolean(buttonHapticEnabledKey, value).apply() }
|
||||||
|
|
||||||
val logVerboseKey get() = resources.getString(R.string.preferences_log_verbose_key)
|
val logVerboseKey get() = resources.getString(R.string.preferences_log_verbose_key)
|
||||||
var logVerbose
|
var logVerbose
|
||||||
get() = sharedPreferences.getBoolean(logVerboseKey, false)
|
get() = sharedPreferences.getBoolean(logVerboseKey, false)
|
||||||
|
@ -112,12 +121,19 @@ class Preferences(context: Context)
|
||||||
private val bitrateAutoSubject by lazy { BehaviorSubject.createDefault(bitrateAuto) }
|
private val bitrateAutoSubject by lazy { BehaviorSubject.createDefault(bitrateAuto) }
|
||||||
val bitrateAutoObservable: Observable<Int> get() = bitrateAutoSubject
|
val bitrateAutoObservable: Observable<Int> get() = bitrateAutoSubject
|
||||||
|
|
||||||
private val videoProfileDefaultBitrate get() = ConnectVideoProfile.preset(resolution.preset, fps.preset)
|
val codecKey get() = resources.getString(R.string.preferences_codec_key)
|
||||||
|
var codec
|
||||||
|
get() = sharedPreferences.getString(codecKey, codecDefault.value)?.let { value ->
|
||||||
|
Codec.values().firstOrNull { it.value == value }
|
||||||
|
} ?: codecDefault
|
||||||
|
set(value) { sharedPreferences.edit().putString(codecKey, value.value).apply() }
|
||||||
|
|
||||||
|
private val videoProfileDefaultBitrate get() = ConnectVideoProfile.preset(resolution.preset, fps.preset, codec.codec)
|
||||||
val videoProfile get() = videoProfileDefaultBitrate.let {
|
val videoProfile get() = videoProfileDefaultBitrate.let {
|
||||||
val bitrate = bitrate
|
val bitrate = bitrate
|
||||||
if(bitrate == null)
|
if(bitrate == null)
|
||||||
it
|
it
|
||||||
else
|
else
|
||||||
ConnectVideoProfile(it.width, it.height, it.maxFPS, bitrate)
|
it.copy(bitrate = bitrate)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,25 +1,11 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import androidx.room.ColumnInfo.BLOB
|
import androidx.room.ColumnInfo.Companion.BLOB
|
||||||
import com.metallic.chiaki.lib.RegistHost
|
import com.metallic.chiaki.lib.RegistHost
|
||||||
|
import com.metallic.chiaki.lib.Target
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
|
@ -28,24 +14,26 @@ import io.reactivex.Single
|
||||||
@Entity(tableName = "registered_host")
|
@Entity(tableName = "registered_host")
|
||||||
data class RegisteredHost(
|
data class RegisteredHost(
|
||||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||||
|
@ColumnInfo(name = "target") val target: Target,
|
||||||
@ColumnInfo(name = "ap_ssid") val apSsid: String?,
|
@ColumnInfo(name = "ap_ssid") val apSsid: String?,
|
||||||
@ColumnInfo(name = "ap_bssid") val apBssid: String?,
|
@ColumnInfo(name = "ap_bssid") val apBssid: String?,
|
||||||
@ColumnInfo(name = "ap_key") val apKey: String?,
|
@ColumnInfo(name = "ap_key") val apKey: String?,
|
||||||
@ColumnInfo(name = "ap_name") val apName: String?,
|
@ColumnInfo(name = "ap_name") val apName: String?,
|
||||||
@ColumnInfo(name = "ps4_mac") val ps4Mac: MacAddress,
|
@ColumnInfo(name = "server_mac") val serverMac: MacAddress,
|
||||||
@ColumnInfo(name = "ps4_nickname") val ps4Nickname: String?,
|
@ColumnInfo(name = "server_nickname") val serverNickname: String?,
|
||||||
@ColumnInfo(name = "rp_regist_key", typeAffinity = BLOB) val rpRegistKey: ByteArray, // CHIAKI_SESSION_AUTH_SIZE
|
@ColumnInfo(name = "rp_regist_key", typeAffinity = BLOB) val rpRegistKey: ByteArray, // CHIAKI_SESSION_AUTH_SIZE
|
||||||
@ColumnInfo(name = "rp_key_type") val rpKeyType: Int,
|
@ColumnInfo(name = "rp_key_type") val rpKeyType: Int,
|
||||||
@ColumnInfo(name = "rp_key", typeAffinity = BLOB) val rpKey: ByteArray // 0x10
|
@ColumnInfo(name = "rp_key", typeAffinity = BLOB) val rpKey: ByteArray // 0x10
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
constructor(registHost: RegistHost) : this(
|
constructor(registHost: RegistHost) : this(
|
||||||
|
target = registHost.target,
|
||||||
apSsid = registHost.apSsid,
|
apSsid = registHost.apSsid,
|
||||||
apBssid = registHost.apBssid,
|
apBssid = registHost.apBssid,
|
||||||
apKey = registHost.apKey,
|
apKey = registHost.apKey,
|
||||||
apName = registHost.apName,
|
apName = registHost.apName,
|
||||||
ps4Mac = MacAddress(registHost.ps4Mac),
|
serverMac = MacAddress(registHost.serverMac),
|
||||||
ps4Nickname = registHost.ps4Nickname,
|
serverNickname = registHost.serverNickname,
|
||||||
rpRegistKey = registHost.rpRegistKey,
|
rpRegistKey = registHost.rpRegistKey,
|
||||||
rpKeyType = registHost.rpKeyType.toInt(),
|
rpKeyType = registHost.rpKeyType.toInt(),
|
||||||
rpKey = registHost.rpKey
|
rpKey = registHost.rpKey
|
||||||
|
@ -59,12 +47,13 @@ data class RegisteredHost(
|
||||||
other as RegisteredHost
|
other as RegisteredHost
|
||||||
|
|
||||||
if(id != other.id) return false
|
if(id != other.id) return false
|
||||||
|
if(target != other.target) return false
|
||||||
if(apSsid != other.apSsid) return false
|
if(apSsid != other.apSsid) return false
|
||||||
if(apBssid != other.apBssid) return false
|
if(apBssid != other.apBssid) return false
|
||||||
if(apKey != other.apKey) return false
|
if(apKey != other.apKey) return false
|
||||||
if(apName != other.apName) return false
|
if(apName != other.apName) return false
|
||||||
if(ps4Mac != other.ps4Mac) return false
|
if(serverMac != other.serverMac) return false
|
||||||
if(ps4Nickname != other.ps4Nickname) return false
|
if(serverNickname != other.serverNickname) return false
|
||||||
if(!rpRegistKey.contentEquals(other.rpRegistKey)) return false
|
if(!rpRegistKey.contentEquals(other.rpRegistKey)) return false
|
||||||
if(rpKeyType != other.rpKeyType) return false
|
if(rpKeyType != other.rpKeyType) return false
|
||||||
if(!rpKey.contentEquals(other.rpKey)) return false
|
if(!rpKey.contentEquals(other.rpKey)) return false
|
||||||
|
@ -75,12 +64,13 @@ data class RegisteredHost(
|
||||||
override fun hashCode(): Int
|
override fun hashCode(): Int
|
||||||
{
|
{
|
||||||
var result = id.hashCode()
|
var result = id.hashCode()
|
||||||
|
result = 31 * result + target.hashCode()
|
||||||
result = 31 * result + (apSsid?.hashCode() ?: 0)
|
result = 31 * result + (apSsid?.hashCode() ?: 0)
|
||||||
result = 31 * result + (apBssid?.hashCode() ?: 0)
|
result = 31 * result + (apBssid?.hashCode() ?: 0)
|
||||||
result = 31 * result + (apKey?.hashCode() ?: 0)
|
result = 31 * result + (apKey?.hashCode() ?: 0)
|
||||||
result = 31 * result + (apName?.hashCode() ?: 0)
|
result = 31 * result + (apName?.hashCode() ?: 0)
|
||||||
result = 31 * result + ps4Mac.hashCode()
|
result = 31 * result + serverMac.hashCode()
|
||||||
result = 31 * result + (ps4Nickname?.hashCode() ?: 0)
|
result = 31 * result + (serverNickname?.hashCode() ?: 0)
|
||||||
result = 31 * result + rpRegistKey.contentHashCode()
|
result = 31 * result + rpRegistKey.contentHashCode()
|
||||||
result = 31 * result + rpKeyType
|
result = 31 * result + rpKeyType
|
||||||
result = 31 * result + rpKey.contentHashCode()
|
result = 31 * result + rpKey.contentHashCode()
|
||||||
|
@ -94,10 +84,10 @@ interface RegisteredHostDao
|
||||||
@Query("SELECT * FROM registered_host")
|
@Query("SELECT * FROM registered_host")
|
||||||
fun getAll(): Flowable<List<RegisteredHost>>
|
fun getAll(): Flowable<List<RegisteredHost>>
|
||||||
|
|
||||||
@Query("SELECT * FROM registered_host WHERE ps4_mac == :mac LIMIT 1")
|
@Query("SELECT * FROM registered_host WHERE server_mac == :mac LIMIT 1")
|
||||||
fun getByMac(mac: MacAddress): Maybe<RegisteredHost>
|
fun getByMac(mac: MacAddress): Maybe<RegisteredHost>
|
||||||
|
|
||||||
@Query("DELETE FROM registered_host WHERE ps4_mac == :mac")
|
@Query("DELETE FROM registered_host WHERE server_mac == :mac")
|
||||||
fun deleteByMac(mac: MacAddress): Completable
|
fun deleteByMac(mac: MacAddress): Completable
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common
|
package com.metallic.chiaki.common
|
||||||
|
|
||||||
|
@ -27,6 +12,7 @@ import androidx.core.content.FileProvider
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
|
import com.metallic.chiaki.lib.Target
|
||||||
import com.squareup.moshi.*
|
import com.squareup.moshi.*
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
|
@ -39,28 +25,32 @@ import io.reactivex.rxkotlin.addTo
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import okio.Okio
|
import okio.Okio
|
||||||
|
import okio.buffer
|
||||||
|
import okio.source
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
class SerializedRegisteredHost(
|
class SerializedRegisteredHost(
|
||||||
|
@Json(name = "target") val target: Target,
|
||||||
@Json(name = "ap_ssid") val apSsid: String?,
|
@Json(name = "ap_ssid") val apSsid: String?,
|
||||||
@Json(name = "ap_bssid") val apBssid: String?,
|
@Json(name = "ap_bssid") val apBssid: String?,
|
||||||
@Json(name = "ap_key") val apKey: String?,
|
@Json(name = "ap_key") val apKey: String?,
|
||||||
@Json(name = "ap_name") val apName: String?,
|
@Json(name = "ap_name") val apName: String?,
|
||||||
@Json(name = "ps4_mac") val ps4Mac: MacAddress,
|
@Json(name = "server_mac") val serverMac: MacAddress,
|
||||||
@Json(name = "ps4_nickname") val ps4Nickname: String?,
|
@Json(name = "server_nickname") val serverNickname: String?,
|
||||||
@Json(name = "rp_regist_key") val rpRegistKey: ByteArray,
|
@Json(name = "rp_regist_key") val rpRegistKey: ByteArray,
|
||||||
@Json(name = "rp_key_type") val rpKeyType: Int,
|
@Json(name = "rp_key_type") val rpKeyType: Int,
|
||||||
@Json(name = "rp_key") val rpKey: ByteArray
|
@Json(name = "rp_key") val rpKey: ByteArray
|
||||||
){
|
){
|
||||||
constructor(registeredHost: RegisteredHost) : this(
|
constructor(registeredHost: RegisteredHost) : this(
|
||||||
|
registeredHost.target,
|
||||||
registeredHost.apSsid,
|
registeredHost.apSsid,
|
||||||
registeredHost.apBssid,
|
registeredHost.apBssid,
|
||||||
registeredHost.apKey,
|
registeredHost.apKey,
|
||||||
registeredHost.apName,
|
registeredHost.apName,
|
||||||
registeredHost.ps4Mac,
|
registeredHost.serverMac,
|
||||||
registeredHost.ps4Nickname,
|
registeredHost.serverNickname,
|
||||||
registeredHost.rpRegistKey,
|
registeredHost.rpRegistKey,
|
||||||
registeredHost.rpKeyType,
|
registeredHost.rpKeyType,
|
||||||
registeredHost.rpKey
|
registeredHost.rpKey
|
||||||
|
@ -70,7 +60,7 @@ class SerializedRegisteredHost(
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
class SerializedManualHost(
|
class SerializedManualHost(
|
||||||
@Json(name = "host") val host: String,
|
@Json(name = "host") val host: String,
|
||||||
@Json(name = "ps4_mac") val ps4Mac: MacAddress?
|
@Json(name = "server_mac") val serverMac: MacAddress?
|
||||||
)
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
@ -92,7 +82,7 @@ data class SerializedSettings(
|
||||||
manualHost.host,
|
manualHost.host,
|
||||||
manualHost.registeredHost?.let { registeredHostId ->
|
manualHost.registeredHost?.let { registeredHostId ->
|
||||||
registeredHosts.firstOrNull { it.id == registeredHostId }
|
registeredHosts.firstOrNull { it.id == registeredHostId }
|
||||||
}?.ps4Mac
|
}?.serverMac
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -118,7 +108,7 @@ private fun Moshi.serializedSettingsAdapter() =
|
||||||
private const val KEY_FORMAT = "format"
|
private const val KEY_FORMAT = "format"
|
||||||
private const val FORMAT = "chiaki-settings"
|
private const val FORMAT = "chiaki-settings"
|
||||||
private const val KEY_VERSION = "version"
|
private const val KEY_VERSION = "version"
|
||||||
private const val VERSION = 1
|
private const val VERSION = 2
|
||||||
private const val KEY_SETTINGS = "settings"
|
private const val KEY_SETTINGS = "settings"
|
||||||
|
|
||||||
fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
|
fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
|
||||||
|
@ -176,7 +166,7 @@ fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDis
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException()
|
val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException()
|
||||||
val buffer = Okio.buffer(Okio.source(inputStream))
|
val buffer = inputStream.source().buffer()
|
||||||
val reader = JsonReader.of(buffer)
|
val reader = JsonReader.of(buffer)
|
||||||
val adapter = moshi().serializedSettingsAdapter()
|
val adapter = moshi().serializedSettingsAdapter()
|
||||||
|
|
||||||
|
@ -212,13 +202,13 @@ fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDis
|
||||||
if(it.isEmpty())
|
if(it.isEmpty())
|
||||||
"-"
|
"-"
|
||||||
else
|
else
|
||||||
it.joinToString(separator = "") { host -> "\n - ${host.ps4Nickname ?: "?"} / ${host.ps4Mac}" }
|
it.joinToString(separator = "") { host -> "\n - ${host.serverNickname ?: "?"} / ${host.serverMac}" }
|
||||||
},
|
},
|
||||||
settings.manualHosts.let {
|
settings.manualHosts.let {
|
||||||
if(it.isEmpty())
|
if(it.isEmpty())
|
||||||
"-"
|
"-"
|
||||||
else
|
else
|
||||||
it.joinToString(separator = "") { host -> "\n - ${host.host} / ${host.ps4Mac ?: "unregistered"}" }
|
it.joinToString(separator = "") { host -> "\n - ${host.host} / ${host.serverMac ?: "unregistered"}" }
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
.setTitle(R.string.alert_title_import)
|
.setTitle(R.string.alert_title_import)
|
||||||
|
@ -257,7 +247,7 @@ abstract class ImportDao
|
||||||
|
|
||||||
class IdWithMac(val id: Long, val mac: MacAddress)
|
class IdWithMac(val id: Long, val mac: MacAddress)
|
||||||
|
|
||||||
@Query("SELECT id, ps4_mac AS mac FROM registered_host WHERE ps4_mac IN (:macs)")
|
@Query("SELECT id, server_mac AS mac FROM registered_host WHERE server_mac IN (:macs)")
|
||||||
abstract fun registeredHostsByMac(macs: List<MacAddress>): List<IdWithMac>
|
abstract fun registeredHostsByMac(macs: List<MacAddress>): List<IdWithMac>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
|
@ -266,19 +256,20 @@ abstract class ImportDao
|
||||||
insertRegisteredHosts(
|
insertRegisteredHosts(
|
||||||
settings.registeredHosts.map {
|
settings.registeredHosts.map {
|
||||||
RegisteredHost(
|
RegisteredHost(
|
||||||
|
target = it.target,
|
||||||
apSsid = it.apSsid,
|
apSsid = it.apSsid,
|
||||||
apBssid = it.apBssid,
|
apBssid = it.apBssid,
|
||||||
apKey = it.apKey,
|
apKey = it.apKey,
|
||||||
apName = it.apName,
|
apName = it.apName,
|
||||||
ps4Mac = it.ps4Mac,
|
serverMac = it.serverMac,
|
||||||
ps4Nickname = it.ps4Nickname,
|
serverNickname = it.serverNickname,
|
||||||
rpRegistKey = it.rpRegistKey,
|
rpRegistKey = it.rpRegistKey,
|
||||||
rpKeyType = it.rpKeyType,
|
rpKeyType = it.rpKeyType,
|
||||||
rpKey = it.rpKey
|
rpKey = it.rpKey
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
val macs = settings.manualHosts.mapNotNull { it.ps4Mac }
|
val macs = settings.manualHosts.mapNotNull { it.serverMac }
|
||||||
val idMacs =
|
val idMacs =
|
||||||
if(macs.isNotEmpty())
|
if(macs.isNotEmpty())
|
||||||
registeredHostsByMac(macs)
|
registeredHostsByMac(macs)
|
||||||
|
@ -289,7 +280,7 @@ abstract class ImportDao
|
||||||
settings.manualHosts.map {
|
settings.manualHosts.map {
|
||||||
ManualHost(
|
ManualHost(
|
||||||
host = it.host,
|
host = it.host,
|
||||||
registeredHost = idMacs.firstOrNull { regHost -> regHost.mac == it.ps4Mac }?.id
|
registeredHost = idMacs.firstOrNull { regHost -> regHost.mac == it.serverMac }?.id
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common.ext
|
package com.metallic.chiaki.common.ext
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common.ext
|
package com.metallic.chiaki.common.ext
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common.ext
|
package com.metallic.chiaki.common.ext
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.common.ext
|
package com.metallic.chiaki.common.ext
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.discovery
|
package com.metallic.chiaki.discovery
|
||||||
|
|
||||||
|
@ -36,7 +21,7 @@ import java.nio.charset.Charset
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
val DiscoveryHost.ps4Mac get() = this.hostId?.hexToByteArray()?.let {
|
val DiscoveryHost.serverMac get() = this.hostId?.hexToByteArray()?.let {
|
||||||
if(it.size == MacAddress.LENGTH)
|
if(it.size == MacAddress.LENGTH)
|
||||||
MacAddress(it)
|
MacAddress(it)
|
||||||
else
|
else
|
||||||
|
@ -107,14 +92,14 @@ class DiscoveryManager
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendWakeup(host: String, registKey: ByteArray)
|
fun sendWakeup(host: String, registKey: ByteArray, ps5: Boolean)
|
||||||
{
|
{
|
||||||
val registKeyString = registKey.indexOfFirst { it == 0.toByte() }.let { end -> registKey.copyOfRange(0, if(end >= 0) end else registKey.size) }.toString(StandardCharsets.UTF_8)
|
val registKeyString = registKey.indexOfFirst { it == 0.toByte() }.let { end -> registKey.copyOfRange(0, if(end >= 0) end else registKey.size) }.toString(StandardCharsets.UTF_8)
|
||||||
val credential = try { registKeyString.toULong(16) } catch(e: NumberFormatException) {
|
val credential = try { registKeyString.toULong(16) } catch(e: NumberFormatException) {
|
||||||
Log.e("DiscoveryManager", "Failed to convert registKey to int", e)
|
Log.e("DiscoveryManager", "Failed to convert registKey to int", e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
DiscoveryService.wakeup(discoveryService, host, credential)
|
DiscoveryService.wakeup(discoveryService, host, credential, ps5)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateService()
|
private fun updateService()
|
||||||
|
|
|
@ -3,17 +3,27 @@ package com.metallic.chiaki.lib
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import kotlinx.android.parcel.IgnoredOnParcel
|
import kotlinx.parcelize.Parcelize
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
enum class Target(val value: Int)
|
enum class Target(val value: Int)
|
||||||
{
|
{
|
||||||
|
PS4_UNKNOWN(0),
|
||||||
PS4_8(800),
|
PS4_8(800),
|
||||||
PS4_9(900),
|
PS4_9(900),
|
||||||
PS4_10(1000)
|
PS4_10(1000),
|
||||||
|
PS5_UNKNOWN(1000000),
|
||||||
|
PS5_1(1000100);
|
||||||
|
|
||||||
|
companion object
|
||||||
|
{
|
||||||
|
@JvmStatic
|
||||||
|
fun fromValue(value: Int) = values().firstOrNull { it.value == value } ?: PS4_10
|
||||||
|
}
|
||||||
|
|
||||||
|
val isPS5 get() = value >= PS5_UNKNOWN.value
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class VideoResolutionPreset(val value: Int)
|
enum class VideoResolutionPreset(val value: Int)
|
||||||
|
@ -30,23 +40,32 @@ enum class VideoFPSPreset(val value: Int)
|
||||||
FPS_60(60)
|
FPS_60(60)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Codec(val value: Int)
|
||||||
|
{
|
||||||
|
CODEC_H264(0),
|
||||||
|
CODEC_H265(1),
|
||||||
|
CODEC_H265_HDR(2)
|
||||||
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class ConnectVideoProfile(
|
data class ConnectVideoProfile(
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val maxFPS: Int,
|
val maxFPS: Int,
|
||||||
val bitrate: Int
|
val bitrate: Int,
|
||||||
|
val codec: Codec
|
||||||
): Parcelable
|
): Parcelable
|
||||||
{
|
{
|
||||||
companion object
|
companion object
|
||||||
{
|
{
|
||||||
fun preset(resolutionPreset: VideoResolutionPreset, fpsPreset: VideoFPSPreset)
|
fun preset(resolutionPreset: VideoResolutionPreset, fpsPreset: VideoFPSPreset, codec: Codec)
|
||||||
= ChiakiNative.videoProfilePreset(resolutionPreset.value, fpsPreset.value)
|
= ChiakiNative.videoProfilePreset(resolutionPreset.value, fpsPreset.value, codec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class ConnectInfo(
|
data class ConnectInfo(
|
||||||
|
val ps5: Boolean,
|
||||||
val host: String,
|
val host: String,
|
||||||
val registKey: ByteArray,
|
val registKey: ByteArray,
|
||||||
val morning: ByteArray,
|
val morning: ByteArray,
|
||||||
|
@ -64,19 +83,19 @@ private class ChiakiNative
|
||||||
}
|
}
|
||||||
@JvmStatic external fun errorCodeToString(value: Int): String
|
@JvmStatic external fun errorCodeToString(value: Int): String
|
||||||
@JvmStatic external fun quitReasonToString(value: Int): String
|
@JvmStatic external fun quitReasonToString(value: Int): String
|
||||||
@JvmStatic external fun quitReasonIsStopped(value: Int): Boolean
|
@JvmStatic external fun quitReasonIsError(value: Int): Boolean
|
||||||
@JvmStatic external fun videoProfilePreset(resolutionPreset: Int, fpsPreset: Int): ConnectVideoProfile
|
@JvmStatic external fun videoProfilePreset(resolutionPreset: Int, fpsPreset: Int, codec: Codec): ConnectVideoProfile
|
||||||
@JvmStatic external fun sessionCreate(result: CreateResult, connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean, javaSession: Session)
|
@JvmStatic external fun sessionCreate(result: CreateResult, connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean, javaSession: Session)
|
||||||
@JvmStatic external fun sessionFree(ptr: Long)
|
@JvmStatic external fun sessionFree(ptr: Long)
|
||||||
@JvmStatic external fun sessionStart(ptr: Long): Int
|
@JvmStatic external fun sessionStart(ptr: Long): Int
|
||||||
@JvmStatic external fun sessionStop(ptr: Long): Int
|
@JvmStatic external fun sessionStop(ptr: Long): Int
|
||||||
@JvmStatic external fun sessionJoin(ptr: Long): Int
|
@JvmStatic external fun sessionJoin(ptr: Long): Int
|
||||||
@JvmStatic external fun sessionSetSurface(ptr: Long, surface: Surface)
|
@JvmStatic external fun sessionSetSurface(ptr: Long, surface: Surface?)
|
||||||
@JvmStatic external fun sessionSetControllerState(ptr: Long, controllerState: ControllerState)
|
@JvmStatic external fun sessionSetControllerState(ptr: Long, controllerState: ControllerState)
|
||||||
@JvmStatic external fun sessionSetLoginPin(ptr: Long, pin: String)
|
@JvmStatic external fun sessionSetLoginPin(ptr: Long, pin: String)
|
||||||
@JvmStatic external fun discoveryServiceCreate(result: CreateResult, options: DiscoveryServiceOptions, javaService: DiscoveryService)
|
@JvmStatic external fun discoveryServiceCreate(result: CreateResult, options: DiscoveryServiceOptions, javaService: DiscoveryService)
|
||||||
@JvmStatic external fun discoveryServiceFree(ptr: Long)
|
@JvmStatic external fun discoveryServiceFree(ptr: Long)
|
||||||
@JvmStatic external fun discoveryServiceWakeup(ptr: Long, host: String, userCredential: Long)
|
@JvmStatic external fun discoveryServiceWakeup(ptr: Long, host: String, userCredential: Long, ps5: Boolean)
|
||||||
@JvmStatic external fun registStart(result: CreateResult, registInfo: RegistInfo, javaLog: ChiakiLog, javaRegist: Regist)
|
@JvmStatic external fun registStart(result: CreateResult, registInfo: RegistInfo, javaLog: ChiakiLog, javaRegist: Regist)
|
||||||
@JvmStatic external fun registStop(ptr: Long)
|
@JvmStatic external fun registStop(ptr: Long)
|
||||||
@JvmStatic external fun registFree(ptr: Long)
|
@JvmStatic external fun registFree(ptr: Long)
|
||||||
|
@ -130,6 +149,14 @@ class ChiakiLog(val levelMask: Int, val callback: (level: Int, text: String) ->
|
||||||
|
|
||||||
private fun maxAbs(a: Short, b: Short) = if(abs(a.toInt()) > abs(b.toInt())) a else b
|
private fun maxAbs(a: Short, b: Short) = if(abs(a.toInt()) > abs(b.toInt())) a else b
|
||||||
|
|
||||||
|
private val CONTROLLER_TOUCHES_MAX = 2 // must be the same as CHIAKI_CONTROLLER_TOUCHES_MAX
|
||||||
|
|
||||||
|
data class ControllerTouch(
|
||||||
|
var x: UShort = 0U,
|
||||||
|
var y: UShort = 0U,
|
||||||
|
var id: Byte = -1 // -1 = up
|
||||||
|
)
|
||||||
|
|
||||||
data class ControllerState constructor(
|
data class ControllerState constructor(
|
||||||
var buttons: UInt = 0U,
|
var buttons: UInt = 0U,
|
||||||
var l2State: UByte = 0U,
|
var l2State: UByte = 0U,
|
||||||
|
@ -137,26 +164,40 @@ data class ControllerState constructor(
|
||||||
var leftX: Short = 0,
|
var leftX: Short = 0,
|
||||||
var leftY: Short = 0,
|
var leftY: Short = 0,
|
||||||
var rightX: Short = 0,
|
var rightX: Short = 0,
|
||||||
var rightY: Short = 0
|
var rightY: Short = 0,
|
||||||
|
private var touchIdNext: UByte = 0U,
|
||||||
|
var touches: Array<ControllerTouch> = arrayOf(ControllerTouch(), ControllerTouch()),
|
||||||
|
var gyroX: Float = 0.0f,
|
||||||
|
var gyroY: Float = 0.0f,
|
||||||
|
var gyroZ: Float = 0.0f,
|
||||||
|
var accelX: Float = 0.0f,
|
||||||
|
var accelY: Float = 1.0f,
|
||||||
|
var accelZ: Float = 0.0f,
|
||||||
|
var orientX: Float = 0.0f,
|
||||||
|
var orientY: Float = 0.0f,
|
||||||
|
var orientZ: Float = 0.0f,
|
||||||
|
var orientW: Float = 1.0f
|
||||||
){
|
){
|
||||||
companion object
|
companion object
|
||||||
{
|
{
|
||||||
val BUTTON_CROSS = (1 shl 0).toUInt()
|
val BUTTON_CROSS = (1 shl 0).toUInt()
|
||||||
val BUTTON_MOON = (1 shl 1).toUInt()
|
val BUTTON_MOON = (1 shl 1).toUInt()
|
||||||
val BUTTON_BOX = (1 shl 2).toUInt()
|
val BUTTON_BOX = (1 shl 2).toUInt()
|
||||||
val BUTTON_PYRAMID = (1 shl 3).toUInt()
|
val BUTTON_PYRAMID = (1 shl 3).toUInt()
|
||||||
val BUTTON_DPAD_LEFT = (1 shl 4).toUInt()
|
val BUTTON_DPAD_LEFT = (1 shl 4).toUInt()
|
||||||
val BUTTON_DPAD_RIGHT = (1 shl 5).toUInt()
|
val BUTTON_DPAD_RIGHT = (1 shl 5).toUInt()
|
||||||
val BUTTON_DPAD_UP = (1 shl 6).toUInt()
|
val BUTTON_DPAD_UP = (1 shl 6).toUInt()
|
||||||
val BUTTON_DPAD_DOWN = (1 shl 7).toUInt()
|
val BUTTON_DPAD_DOWN = (1 shl 7).toUInt()
|
||||||
val BUTTON_L1 = (1 shl 8).toUInt()
|
val BUTTON_L1 = (1 shl 8).toUInt()
|
||||||
val BUTTON_R1 = (1 shl 9).toUInt()
|
val BUTTON_R1 = (1 shl 9).toUInt()
|
||||||
val BUTTON_L3 = (1 shl 10).toUInt()
|
val BUTTON_L3 = (1 shl 10).toUInt()
|
||||||
val BUTTON_R3 = (1 shl 11).toUInt()
|
val BUTTON_R3 = (1 shl 11).toUInt()
|
||||||
val BUTTON_OPTIONS = (1 shl 12).toUInt()
|
val BUTTON_OPTIONS = (1 shl 12).toUInt()
|
||||||
val BUTTON_SHARE = (1 shl 13).toUInt()
|
val BUTTON_SHARE = (1 shl 13).toUInt()
|
||||||
val BUTTON_TOUCHPAD = (1 shl 14).toUInt()
|
val BUTTON_TOUCHPAD = (1 shl 14).toUInt()
|
||||||
val BUTTON_PS = (1 shl 15).toUInt()
|
val BUTTON_PS = (1 shl 15).toUInt()
|
||||||
|
val TOUCHPAD_WIDTH: UShort = 1920U
|
||||||
|
val TOUCHPAD_HEIGHT: UShort = 942U
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun or(o: ControllerState) = ControllerState(
|
infix fun or(o: ControllerState) = ControllerState(
|
||||||
|
@ -166,24 +207,116 @@ data class ControllerState constructor(
|
||||||
leftX = maxAbs(leftX, o.leftX),
|
leftX = maxAbs(leftX, o.leftX),
|
||||||
leftY = maxAbs(leftY, o.leftY),
|
leftY = maxAbs(leftY, o.leftY),
|
||||||
rightX = maxAbs(rightX, o.rightX),
|
rightX = maxAbs(rightX, o.rightX),
|
||||||
rightY = maxAbs(rightY, o.rightY)
|
rightY = maxAbs(rightY, o.rightY),
|
||||||
|
touches = touches.zip(o.touches) { a, b -> if(a.id >= 0) a else b }.toTypedArray(),
|
||||||
|
gyroX = gyroX,
|
||||||
|
gyroY = gyroY,
|
||||||
|
gyroZ = gyroZ,
|
||||||
|
accelX = accelX,
|
||||||
|
accelY = accelY,
|
||||||
|
accelZ = accelZ,
|
||||||
|
orientX = orientX,
|
||||||
|
orientY = orientY,
|
||||||
|
orientZ = orientZ,
|
||||||
|
orientW = orientW
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean
|
||||||
|
{
|
||||||
|
if(this === other) return true
|
||||||
|
if(javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as ControllerState
|
||||||
|
|
||||||
|
if(buttons != other.buttons) return false
|
||||||
|
if(l2State != other.l2State) return false
|
||||||
|
if(r2State != other.r2State) return false
|
||||||
|
if(leftX != other.leftX) return false
|
||||||
|
if(leftY != other.leftY) return false
|
||||||
|
if(rightX != other.rightX) return false
|
||||||
|
if(rightY != other.rightY) return false
|
||||||
|
if(touchIdNext != other.touchIdNext) return false
|
||||||
|
if(!touches.contentEquals(other.touches)) return false
|
||||||
|
if(gyroX != other.gyroX) return false
|
||||||
|
if(gyroY != other.gyroY) return false
|
||||||
|
if(gyroZ != other.gyroZ) return false
|
||||||
|
if(accelX != other.accelX) return false
|
||||||
|
if(accelY != other.accelY) return false
|
||||||
|
if(accelZ != other.accelZ) return false
|
||||||
|
if(orientX != other.orientX) return false
|
||||||
|
if(orientY != other.orientY) return false
|
||||||
|
if(orientZ != other.orientZ) return false
|
||||||
|
if(orientW != other.orientW) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int
|
||||||
|
{
|
||||||
|
var result = buttons.hashCode()
|
||||||
|
result = 31 * result + l2State.hashCode()
|
||||||
|
result = 31 * result + r2State.hashCode()
|
||||||
|
result = 31 * result + leftX
|
||||||
|
result = 31 * result + leftY
|
||||||
|
result = 31 * result + rightX
|
||||||
|
result = 31 * result + rightY
|
||||||
|
result = 31 * result + touchIdNext.hashCode()
|
||||||
|
result = 31 * result + touches.contentHashCode()
|
||||||
|
result = 31 * result + gyroX.hashCode()
|
||||||
|
result = 31 * result + gyroY.hashCode()
|
||||||
|
result = 31 * result + gyroZ.hashCode()
|
||||||
|
result = 31 * result + accelX.hashCode()
|
||||||
|
result = 31 * result + accelY.hashCode()
|
||||||
|
result = 31 * result + accelZ.hashCode()
|
||||||
|
result = 31 * result + orientX.hashCode()
|
||||||
|
result = 31 * result + orientY.hashCode()
|
||||||
|
result = 31 * result + orientZ.hashCode()
|
||||||
|
result = 31 * result + orientW.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startTouch(x: UShort, y: UShort): UByte? =
|
||||||
|
touches
|
||||||
|
.find { it.id < 0 }
|
||||||
|
?.also {
|
||||||
|
it.id = touchIdNext.toByte()
|
||||||
|
it.x = x
|
||||||
|
it.y = y
|
||||||
|
touchIdNext = ((touchIdNext + 1U) and 0x7fU).toUByte()
|
||||||
|
}?.id?.toUByte()
|
||||||
|
|
||||||
|
fun stopTouch(id: UByte)
|
||||||
|
{
|
||||||
|
touches.find {
|
||||||
|
it.id >= 0 && it.id == id.toByte()
|
||||||
|
}?.let {
|
||||||
|
it.id = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTouchPos(id: UByte, x: UShort, y: UShort): Boolean
|
||||||
|
= touches.find {
|
||||||
|
it.id >= 0 && it.id == id.toByte()
|
||||||
|
}?.let {
|
||||||
|
val r = it.x != x || it.y != y
|
||||||
|
it.x = x
|
||||||
|
it.y = y
|
||||||
|
r
|
||||||
|
} ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuitReason(val value: Int)
|
class QuitReason(val value: Int)
|
||||||
{
|
{
|
||||||
override fun toString() = ChiakiNative.quitReasonToString(value)
|
override fun toString() = ChiakiNative.quitReasonToString(value)
|
||||||
|
|
||||||
/**
|
val isError = ChiakiNative.quitReasonIsError(value)
|
||||||
* whether the reason is CHIAKI_QUIT_REASON_STOPPED
|
|
||||||
*/
|
|
||||||
val isStopped = ChiakiNative.quitReasonIsStopped(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Event
|
sealed class Event
|
||||||
object ConnectedEvent: Event()
|
object ConnectedEvent: Event()
|
||||||
data class LoginPinRequestEvent(val pinIncorrect: Boolean): Event()
|
data class LoginPinRequestEvent(val pinIncorrect: Boolean): Event()
|
||||||
data class QuitEvent(val reason: QuitReason, val reasonString: String?): Event()
|
data class QuitEvent(val reason: QuitReason, val reasonString: String?): Event()
|
||||||
|
data class RumbleEvent(val left: UByte, val right: UByte): Event()
|
||||||
|
|
||||||
class CreateError(val errorCode: ErrorCode): Exception("Failed to create a native object: $errorCode")
|
class CreateError(val errorCode: ErrorCode): Exception("Failed to create a native object: $errorCode")
|
||||||
|
|
||||||
|
@ -239,7 +372,12 @@ class Session(connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean)
|
||||||
event(QuitEvent(QuitReason(reasonValue), reasonString))
|
event(QuitEvent(QuitReason(reasonValue), reasonString))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSurface(surface: Surface)
|
private fun eventRumble(left: Int, right: Int)
|
||||||
|
{
|
||||||
|
event(RumbleEvent(left.toUByte(), right.toUByte()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSurface(surface: Surface?)
|
||||||
{
|
{
|
||||||
ChiakiNative.sessionSetSurface(nativePtr, surface)
|
ChiakiNative.sessionSetSurface(nativePtr, surface)
|
||||||
}
|
}
|
||||||
|
@ -273,6 +411,8 @@ data class DiscoveryHost(
|
||||||
READY,
|
READY,
|
||||||
STANDBY
|
STANDBY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isPS5 get() = deviceDiscoveryProtocolVersion == "00030010"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,8 +429,8 @@ class DiscoveryService(
|
||||||
{
|
{
|
||||||
companion object
|
companion object
|
||||||
{
|
{
|
||||||
fun wakeup(service: DiscoveryService?, host: String, userCredential: ULong) =
|
fun wakeup(service: DiscoveryService?, host: String, userCredential: ULong, ps5: Boolean) =
|
||||||
ChiakiNative.discoveryServiceWakeup(service?.nativePtr ?: 0, host, userCredential.toLong())
|
ChiakiNative.discoveryServiceWakeup(service?.nativePtr ?: 0, host, userCredential.toLong(), ps5)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var nativePtr: Long
|
private var nativePtr: Long
|
||||||
|
@ -339,12 +479,13 @@ data class RegistInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RegistHost(
|
data class RegistHost(
|
||||||
|
val target: Target,
|
||||||
val apSsid: String,
|
val apSsid: String,
|
||||||
val apBssid: String,
|
val apBssid: String,
|
||||||
val apKey: String,
|
val apKey: String,
|
||||||
val apName: String,
|
val apName: String,
|
||||||
val ps4Mac: ByteArray,
|
val serverMac: ByteArray,
|
||||||
val ps4Nickname: String,
|
val serverNickname: String,
|
||||||
val rpRegistKey: ByteArray,
|
val rpRegistKey: ByteArray,
|
||||||
val rpKeyType: UInt,
|
val rpKeyType: UInt,
|
||||||
val rpKey: ByteArray
|
val rpKey: ByteArray
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.main
|
package com.metallic.chiaki.main
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
|
@ -31,8 +17,8 @@ import com.metallic.chiaki.common.DiscoveredDisplayHost
|
||||||
import com.metallic.chiaki.common.DisplayHost
|
import com.metallic.chiaki.common.DisplayHost
|
||||||
import com.metallic.chiaki.common.ManualDisplayHost
|
import com.metallic.chiaki.common.ManualDisplayHost
|
||||||
import com.metallic.chiaki.common.ext.inflate
|
import com.metallic.chiaki.common.ext.inflate
|
||||||
|
import com.metallic.chiaki.databinding.ItemDisplayHostBinding
|
||||||
import com.metallic.chiaki.lib.DiscoveryHost
|
import com.metallic.chiaki.lib.DiscoveryHost
|
||||||
import kotlinx.android.synthetic.main.item_display_host.view.*
|
|
||||||
|
|
||||||
class DisplayHostDiffCallback(val old: List<DisplayHost>, val new: List<DisplayHost>): DiffUtil.Callback()
|
class DisplayHostDiffCallback(val old: List<DisplayHost>, val new: List<DisplayHost>): DiffUtil.Callback()
|
||||||
{
|
{
|
||||||
|
@ -57,10 +43,10 @@ class DisplayHostRecyclerViewAdapter(
|
||||||
diff.dispatchUpdatesTo(this)
|
diff.dispatchUpdatesTo(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
class ViewHolder(val binding: ItemDisplayHostBinding): RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
|
||||||
= ViewHolder(parent.inflate(R.layout.item_display_host))
|
= ViewHolder(ItemDisplayHostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
||||||
override fun getItemCount() = hosts.count()
|
override fun getItemCount() = hosts.count()
|
||||||
|
|
||||||
|
@ -68,7 +54,7 @@ class DisplayHostRecyclerViewAdapter(
|
||||||
{
|
{
|
||||||
val context = holder.itemView.context
|
val context = holder.itemView.context
|
||||||
val host = hosts[position]
|
val host = hosts[position]
|
||||||
holder.itemView.also {
|
holder.binding.also {
|
||||||
it.nameTextView.text = host.name
|
it.nameTextView.text = host.name
|
||||||
it.hostTextView.text = context.getString(R.string.display_host_host, host.host)
|
it.hostTextView.text = context.getString(R.string.display_host_host, host.host)
|
||||||
val id = host.id
|
val id = host.id
|
||||||
|
@ -90,16 +76,19 @@ class DisplayHostRecyclerViewAdapter(
|
||||||
} ?: ""
|
} ?: ""
|
||||||
it.discoveredIndicatorLayout.visibility = if(host is DiscoveredDisplayHost) View.VISIBLE else View.GONE
|
it.discoveredIndicatorLayout.visibility = if(host is DiscoveredDisplayHost) View.VISIBLE else View.GONE
|
||||||
it.stateIndicatorImageView.setImageResource(
|
it.stateIndicatorImageView.setImageResource(
|
||||||
if(host is DiscoveredDisplayHost)
|
when
|
||||||
when(host.discoveredHost.state)
|
{
|
||||||
|
host is DiscoveredDisplayHost -> when(host.discoveredHost.state)
|
||||||
{
|
{
|
||||||
DiscoveryHost.State.STANDBY -> R.drawable.ic_console_standby
|
DiscoveryHost.State.STANDBY -> if(host.isPS5) R.drawable.ic_console_ps5_standby else R.drawable.ic_console_standby
|
||||||
DiscoveryHost.State.READY -> R.drawable.ic_console_ready
|
DiscoveryHost.State.READY -> if(host.isPS5) R.drawable.ic_console_ps5_ready else R.drawable.ic_console_ready
|
||||||
else -> R.drawable.ic_console
|
else -> if(host.isPS5) R.drawable.ic_console_ps5 else R.drawable.ic_console
|
||||||
}
|
}
|
||||||
else
|
host.isPS5 -> R.drawable.ic_console_ps5
|
||||||
R.drawable.ic_console)
|
else -> R.drawable.ic_console
|
||||||
it.setOnClickListener { clickCallback(host) }
|
}
|
||||||
|
)
|
||||||
|
it.root.setOnClickListener { clickCallback(host) }
|
||||||
|
|
||||||
val canWakeup = host.registeredHost != null
|
val canWakeup = host.registeredHost != null
|
||||||
val canEditDelete = host is ManualDisplayHost
|
val canEditDelete = host is ManualDisplayHost
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.main
|
package com.metallic.chiaki.main
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.main
|
package com.metallic.chiaki.main
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.main
|
package com.metallic.chiaki.main
|
||||||
|
|
||||||
|
@ -32,52 +17,54 @@ import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.*
|
import com.metallic.chiaki.common.*
|
||||||
import com.metallic.chiaki.common.ext.putRevealExtra
|
import com.metallic.chiaki.common.ext.putRevealExtra
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
|
import com.metallic.chiaki.databinding.ActivityMainBinding
|
||||||
import com.metallic.chiaki.lib.ConnectInfo
|
import com.metallic.chiaki.lib.ConnectInfo
|
||||||
import com.metallic.chiaki.lib.DiscoveryHost
|
import com.metallic.chiaki.lib.DiscoveryHost
|
||||||
import com.metallic.chiaki.manualconsole.EditManualConsoleActivity
|
import com.metallic.chiaki.manualconsole.EditManualConsoleActivity
|
||||||
import com.metallic.chiaki.regist.RegistActivity
|
import com.metallic.chiaki.regist.RegistActivity
|
||||||
import com.metallic.chiaki.settings.SettingsActivity
|
import com.metallic.chiaki.settings.SettingsActivity
|
||||||
import com.metallic.chiaki.stream.StreamActivity
|
import com.metallic.chiaki.stream.StreamActivity
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity()
|
class MainActivity : AppCompatActivity()
|
||||||
{
|
{
|
||||||
private lateinit var viewModel: MainViewModel
|
private lateinit var viewModel: MainViewModel
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityMainBinding
|
||||||
private var discoveryMenuItem: MenuItem? = null
|
private var discoveryMenuItem: MenuItem? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
title = ""
|
title = ""
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
floatingActionButton.setOnClickListener {
|
binding.floatingActionButton.setOnClickListener {
|
||||||
expandFloatingActionButton(!floatingActionButton.isExpanded)
|
expandFloatingActionButton(!binding.floatingActionButton.isExpanded)
|
||||||
}
|
}
|
||||||
floatingActionButtonDialBackground.setOnClickListener {
|
binding.floatingActionButtonDialBackground.setOnClickListener {
|
||||||
expandFloatingActionButton(false)
|
expandFloatingActionButton(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
addManualButton.setOnClickListener { addManualConsole() }
|
binding.addManualButton.setOnClickListener { addManualConsole() }
|
||||||
addManualLabelButton.setOnClickListener { addManualConsole() }
|
binding.addManualLabelButton.setOnClickListener { addManualConsole() }
|
||||||
|
|
||||||
registerButton.setOnClickListener { showRegistration() }
|
binding.registerButton.setOnClickListener { showRegistration() }
|
||||||
registerLabelButton.setOnClickListener { showRegistration() }
|
binding.registerLabelButton.setOnClickListener { showRegistration() }
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory { MainViewModel(getDatabase(this), Preferences(this)) })
|
viewModel = ViewModelProvider(this, viewModelFactory { MainViewModel(getDatabase(this), Preferences(this)) })
|
||||||
.get(MainViewModel::class.java)
|
.get(MainViewModel::class.java)
|
||||||
|
|
||||||
val recyclerViewAdapter = DisplayHostRecyclerViewAdapter(this::hostTriggered, this::wakeupHost, this::editHost, this::deleteHost)
|
val recyclerViewAdapter = DisplayHostRecyclerViewAdapter(this::hostTriggered, this::wakeupHost, this::editHost, this::deleteHost)
|
||||||
hostsRecyclerView.adapter = recyclerViewAdapter
|
binding.hostsRecyclerView.adapter = recyclerViewAdapter
|
||||||
hostsRecyclerView.layoutManager = LinearLayoutManager(this)
|
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
viewModel.displayHosts.observe(this, Observer {
|
viewModel.displayHosts.observe(this, Observer {
|
||||||
val top = hostsRecyclerView.computeVerticalScrollOffset() == 0
|
val top = binding.hostsRecyclerView.computeVerticalScrollOffset() == 0
|
||||||
recyclerViewAdapter.hosts = it
|
recyclerViewAdapter.hosts = it
|
||||||
if(top)
|
if(top)
|
||||||
hostsRecyclerView.scrollToPosition(0)
|
binding.hostsRecyclerView.scrollToPosition(0)
|
||||||
updateEmptyInfo()
|
updateEmptyInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -91,19 +78,19 @@ class MainActivity : AppCompatActivity()
|
||||||
{
|
{
|
||||||
if(viewModel.displayHosts.value?.isEmpty() ?: true)
|
if(viewModel.displayHosts.value?.isEmpty() ?: true)
|
||||||
{
|
{
|
||||||
emptyInfoLayout.visibility = View.VISIBLE
|
binding.emptyInfoLayout.visibility = View.VISIBLE
|
||||||
val discoveryActive = viewModel.discoveryActive.value ?: false
|
val discoveryActive = viewModel.discoveryActive.value ?: false
|
||||||
emptyInfoImageView.setImageResource(if(discoveryActive) R.drawable.ic_discover_on else R.drawable.ic_discover_off)
|
binding.emptyInfoImageView.setImageResource(if(discoveryActive) R.drawable.ic_discover_on else R.drawable.ic_discover_off)
|
||||||
emptyInfoTextView.setText(if(discoveryActive) R.string.display_hosts_empty_discovery_on_info else R.string.display_hosts_empty_discovery_off_info)
|
binding.emptyInfoTextView.setText(if(discoveryActive) R.string.display_hosts_empty_discovery_on_info else R.string.display_hosts_empty_discovery_off_info)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
emptyInfoLayout.visibility = View.GONE
|
binding.emptyInfoLayout.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun expandFloatingActionButton(expand: Boolean)
|
private fun expandFloatingActionButton(expand: Boolean)
|
||||||
{
|
{
|
||||||
floatingActionButton.isExpanded = expand
|
binding.floatingActionButton.isExpanded = expand
|
||||||
floatingActionButton.isActivated = floatingActionButton.isExpanded
|
binding.floatingActionButton.isActivated = binding.floatingActionButton.isExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart()
|
override fun onStart()
|
||||||
|
@ -120,7 +107,7 @@ class MainActivity : AppCompatActivity()
|
||||||
|
|
||||||
override fun onBackPressed()
|
override fun onBackPressed()
|
||||||
{
|
{
|
||||||
if(floatingActionButton.isExpanded)
|
if(binding.floatingActionButton.isExpanded)
|
||||||
{
|
{
|
||||||
expandFloatingActionButton(false)
|
expandFloatingActionButton(false)
|
||||||
return
|
return
|
||||||
|
@ -166,7 +153,7 @@ class MainActivity : AppCompatActivity()
|
||||||
private fun addManualConsole()
|
private fun addManualConsole()
|
||||||
{
|
{
|
||||||
Intent(this, EditManualConsoleActivity::class.java).also {
|
Intent(this, EditManualConsoleActivity::class.java).also {
|
||||||
it.putRevealExtra(addManualButton, rootLayout)
|
it.putRevealExtra(binding.addManualButton, binding.rootLayout)
|
||||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +161,7 @@ class MainActivity : AppCompatActivity()
|
||||||
private fun showRegistration()
|
private fun showRegistration()
|
||||||
{
|
{
|
||||||
Intent(this, RegistActivity::class.java).also {
|
Intent(this, RegistActivity::class.java).also {
|
||||||
it.putRevealExtra(registerButton, rootLayout)
|
it.putRevealExtra(binding.registerButton, binding.rootLayout)
|
||||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +172,7 @@ class MainActivity : AppCompatActivity()
|
||||||
if(registeredHost != null)
|
if(registeredHost != null)
|
||||||
{
|
{
|
||||||
fun connect() {
|
fun connect() {
|
||||||
val connectInfo = ConnectInfo(host.host, registeredHost.rpRegistKey, registeredHost.rpKey, Preferences(this).videoProfile)
|
val connectInfo = ConnectInfo(host.isPS5, host.host, registeredHost.rpRegistKey, registeredHost.rpKey, Preferences(this).videoProfile)
|
||||||
Intent(this, StreamActivity::class.java).let {
|
Intent(this, StreamActivity::class.java).let {
|
||||||
it.putExtra(StreamActivity.EXTRA_CONNECT_INFO, connectInfo)
|
it.putExtra(StreamActivity.EXTRA_CONNECT_INFO, connectInfo)
|
||||||
startActivity(it)
|
startActivity(it)
|
||||||
|
@ -224,7 +211,7 @@ class MainActivity : AppCompatActivity()
|
||||||
private fun wakeupHost(host: DisplayHost)
|
private fun wakeupHost(host: DisplayHost)
|
||||||
{
|
{
|
||||||
val registeredHost = host.registeredHost ?: return
|
val registeredHost = host.registeredHost ?: return
|
||||||
viewModel.discoveryManager.sendWakeup(host.host, registeredHost.rpRegistKey)
|
viewModel.discoveryManager.sendWakeup(host.host, registeredHost.rpRegistKey, registeredHost.target.isPS5)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editHost(host: DisplayHost)
|
private fun editHost(host: DisplayHost)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.main
|
package com.metallic.chiaki.main
|
||||||
|
|
||||||
|
@ -21,7 +6,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import com.metallic.chiaki.common.*
|
import com.metallic.chiaki.common.*
|
||||||
import com.metallic.chiaki.common.ext.toLiveData
|
import com.metallic.chiaki.common.ext.toLiveData
|
||||||
import com.metallic.chiaki.discovery.DiscoveryManager
|
import com.metallic.chiaki.discovery.DiscoveryManager
|
||||||
import com.metallic.chiaki.discovery.ps4Mac
|
import com.metallic.chiaki.discovery.serverMac
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxkotlin.Observables
|
import io.reactivex.rxkotlin.Observables
|
||||||
|
@ -46,10 +31,10 @@ class MainViewModel(val database: AppDatabase, val preferences: Preferences): Vi
|
||||||
database.registeredHostDao().getAll().toObservable(),
|
database.registeredHostDao().getAll().toObservable(),
|
||||||
discoveryManager.discoveredHosts)
|
discoveryManager.discoveredHosts)
|
||||||
{ manualHosts, registeredHosts, discoveredHosts ->
|
{ manualHosts, registeredHosts, discoveredHosts ->
|
||||||
val macRegisteredHosts = registeredHosts.associateBy { it.ps4Mac }
|
val macRegisteredHosts = registeredHosts.associateBy { it.serverMac }
|
||||||
val idRegisteredHosts = registeredHosts.associateBy { it.id }
|
val idRegisteredHosts = registeredHosts.associateBy { it.id }
|
||||||
discoveredHosts.map {
|
discoveredHosts.map {
|
||||||
DiscoveredDisplayHost(it.ps4Mac?.let { mac -> macRegisteredHosts[mac] }, it)
|
DiscoveredDisplayHost(it.serverMac?.let { mac -> macRegisteredHosts[mac] }, it)
|
||||||
} +
|
} +
|
||||||
manualHosts.map {
|
manualHosts.map {
|
||||||
ManualDisplayHost(it.registeredHost?.let { id -> idRegisteredHosts[id] }, it)
|
ManualDisplayHost(it.registeredHost?.let { id -> idRegisteredHosts[id] }, it)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.manualconsole
|
package com.metallic.chiaki.manualconsole
|
||||||
|
|
||||||
|
@ -31,10 +16,10 @@ import com.metallic.chiaki.common.RegisteredHost
|
||||||
import com.metallic.chiaki.common.ext.RevealActivity
|
import com.metallic.chiaki.common.ext.RevealActivity
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
import com.metallic.chiaki.common.getDatabase
|
import com.metallic.chiaki.common.getDatabase
|
||||||
|
import com.metallic.chiaki.databinding.ActivityEditManualBinding
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxkotlin.addTo
|
import io.reactivex.rxkotlin.addTo
|
||||||
import kotlinx.android.synthetic.main.activity_edit_manual.*
|
|
||||||
|
|
||||||
class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
||||||
{
|
{
|
||||||
|
@ -43,18 +28,20 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
||||||
const val EXTRA_MANUAL_HOST_ID = "manual_host_id"
|
const val EXTRA_MANUAL_HOST_ID = "manual_host_id"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val revealIntent: Intent get() = intent
|
|
||||||
override val revealRootLayout: View get() = rootLayout
|
|
||||||
override val revealWindow: Window get() = window
|
|
||||||
|
|
||||||
private lateinit var viewModel: EditManualConsoleViewModel
|
private lateinit var viewModel: EditManualConsoleViewModel
|
||||||
|
private lateinit var binding: ActivityEditManualBinding
|
||||||
|
|
||||||
|
override val revealIntent: Intent get() = intent
|
||||||
|
override val revealRootLayout: View get() = binding.rootLayout
|
||||||
|
override val revealWindow: Window get() = window
|
||||||
|
|
||||||
private val disposable = CompositeDisposable()
|
private val disposable = CompositeDisposable()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_edit_manual)
|
binding = ActivityEditManualBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
handleReveal()
|
handleReveal()
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory {
|
viewModel = ViewModelProvider(this, viewModelFactory {
|
||||||
|
@ -67,17 +54,17 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
||||||
.get(EditManualConsoleViewModel::class.java)
|
.get(EditManualConsoleViewModel::class.java)
|
||||||
|
|
||||||
viewModel.existingHost?.observe(this, Observer {
|
viewModel.existingHost?.observe(this, Observer {
|
||||||
hostEditText.setText(it.host)
|
binding.hostEditText.setText(it.host)
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.selectedRegisteredHost.observe(this, Observer {
|
viewModel.selectedRegisteredHost.observe(this, Observer {
|
||||||
registeredHostTextView.setText(titleForRegisteredHost(it))
|
binding.registeredHostTextView.setText(titleForRegisteredHost(it))
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.registeredHosts.observe(this, Observer { hosts ->
|
viewModel.registeredHosts.observe(this, Observer { hosts ->
|
||||||
registeredHostTextView.setAdapter(ArrayAdapter<String>(this, R.layout.dropdown_menu_popup_item,
|
binding.registeredHostTextView.setAdapter(ArrayAdapter<String>(this, R.layout.dropdown_menu_popup_item,
|
||||||
hosts.map { titleForRegisteredHost(it) }))
|
hosts.map { titleForRegisteredHost(it) }))
|
||||||
registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
|
binding.registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
|
||||||
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
|
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
|
||||||
{
|
{
|
||||||
if(position >= hosts.size)
|
if(position >= hosts.size)
|
||||||
|
@ -88,26 +75,25 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.saveButton.setOnClickListener { saveHost() }
|
||||||
saveButton.setOnClickListener { saveHost() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun titleForRegisteredHost(registeredHost: RegisteredHost?) =
|
private fun titleForRegisteredHost(registeredHost: RegisteredHost?) =
|
||||||
if(registeredHost == null)
|
if(registeredHost == null)
|
||||||
getString(R.string.add_manual_regist_on_connect)
|
getString(R.string.add_manual_regist_on_connect)
|
||||||
else
|
else
|
||||||
"${registeredHost.ps4Nickname ?: ""} (${registeredHost.ps4Mac})"
|
"${registeredHost.serverNickname ?: ""} (${registeredHost.serverMac})"
|
||||||
|
|
||||||
private fun saveHost()
|
private fun saveHost()
|
||||||
{
|
{
|
||||||
val host = hostEditText.text.toString().trim()
|
val host = binding.hostEditText.text.toString().trim()
|
||||||
if(host.isEmpty())
|
if(host.isEmpty())
|
||||||
{
|
{
|
||||||
hostEditText.error = getString(R.string.entered_host_invalid)
|
binding.hostEditText.error = getString(R.string.entered_host_invalid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
saveButton.isEnabled = false
|
binding.saveButton.isEnabled = false
|
||||||
viewModel.saveHost(host)
|
viewModel.saveHost(host)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe {
|
.subscribe {
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.manualconsole
|
package com.metallic.chiaki.manualconsole
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.regist
|
package com.metallic.chiaki.regist
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.regist
|
package com.metallic.chiaki.regist
|
||||||
|
|
||||||
|
@ -27,9 +12,9 @@ import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.ext.RevealActivity
|
import com.metallic.chiaki.common.ext.RevealActivity
|
||||||
|
import com.metallic.chiaki.databinding.ActivityRegistBinding
|
||||||
import com.metallic.chiaki.lib.RegistInfo
|
import com.metallic.chiaki.lib.RegistInfo
|
||||||
import com.metallic.chiaki.lib.Target
|
import com.metallic.chiaki.lib.Target
|
||||||
import kotlinx.android.synthetic.main.activity_regist.*
|
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
class RegistActivity: AppCompatActivity(), RevealActivity
|
class RegistActivity: AppCompatActivity(), RevealActivity
|
||||||
|
@ -45,96 +30,103 @@ class RegistActivity: AppCompatActivity(), RevealActivity
|
||||||
private const val REQUEST_REGIST = 1
|
private const val REQUEST_REGIST = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var viewModel: RegistViewModel
|
||||||
|
private lateinit var binding: ActivityRegistBinding
|
||||||
|
|
||||||
override val revealWindow: Window get() = window
|
override val revealWindow: Window get() = window
|
||||||
override val revealIntent: Intent get() = intent
|
override val revealIntent: Intent get() = intent
|
||||||
override val revealRootLayout: View get() = rootLayout
|
override val revealRootLayout: View get() = binding.rootLayout
|
||||||
|
|
||||||
private lateinit var viewModel: RegistViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_regist)
|
binding = ActivityRegistBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
handleReveal()
|
handleReveal()
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this).get(RegistViewModel::class.java)
|
viewModel = ViewModelProvider(this).get(RegistViewModel::class.java)
|
||||||
|
|
||||||
hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
|
binding.hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
|
||||||
broadcastCheckBox.isChecked = intent.getBooleanExtra(EXTRA_BROADCAST, true)
|
binding.broadcastCheckBox.isChecked = intent.getBooleanExtra(EXTRA_BROADCAST, true)
|
||||||
|
|
||||||
registButton.setOnClickListener { doRegist() }
|
binding.registButton.setOnClickListener { doRegist() }
|
||||||
|
|
||||||
ps4VersionRadioGroup.check(when(viewModel.ps4Version.value ?: RegistViewModel.PS4Version.GE_8) {
|
binding.ps4VersionRadioGroup.check(when(viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5) {
|
||||||
RegistViewModel.PS4Version.GE_8 -> R.id.ps4VersionGE8RadioButton
|
RegistViewModel.ConsoleVersion.PS5 -> R.id.ps5RadioButton
|
||||||
RegistViewModel.PS4Version.GE_7 -> R.id.ps4VersionGE7RadioButton
|
RegistViewModel.ConsoleVersion.PS4_GE_8 -> R.id.ps4VersionGE8RadioButton
|
||||||
RegistViewModel.PS4Version.LT_7 -> R.id.ps4VersionLT7RadioButton
|
RegistViewModel.ConsoleVersion.PS4_GE_7 -> R.id.ps4VersionGE7RadioButton
|
||||||
|
RegistViewModel.ConsoleVersion.PS4_LT_7 -> R.id.ps4VersionLT7RadioButton
|
||||||
})
|
})
|
||||||
|
|
||||||
ps4VersionRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
binding.ps4VersionRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||||
viewModel.ps4Version.value = when(checkedId)
|
viewModel.ps4Version.value = when(checkedId)
|
||||||
{
|
{
|
||||||
R.id.ps4VersionGE8RadioButton -> RegistViewModel.PS4Version.GE_8
|
R.id.ps5RadioButton -> RegistViewModel.ConsoleVersion.PS5
|
||||||
R.id.ps4VersionGE7RadioButton -> RegistViewModel.PS4Version.GE_7
|
R.id.ps4VersionGE8RadioButton -> RegistViewModel.ConsoleVersion.PS4_GE_8
|
||||||
R.id.ps4VersionLT7RadioButton -> RegistViewModel.PS4Version.LT_7
|
R.id.ps4VersionGE7RadioButton -> RegistViewModel.ConsoleVersion.PS4_GE_7
|
||||||
else -> RegistViewModel.PS4Version.GE_7
|
R.id.ps4VersionLT7RadioButton -> RegistViewModel.ConsoleVersion.PS4_LT_7
|
||||||
|
else -> RegistViewModel.ConsoleVersion.PS5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.ps4Version.observe(this, Observer {
|
viewModel.ps4Version.observe(this, Observer {
|
||||||
psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.PS4Version.LT_7) View.GONE else View.VISIBLE
|
binding.psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.ConsoleVersion.PS4_LT_7) View.GONE else View.VISIBLE
|
||||||
psnIdTextInputLayout.hint = getString(when(it!!)
|
binding.psnIdTextInputLayout.hint = getString(when(it!!)
|
||||||
{
|
{
|
||||||
RegistViewModel.PS4Version.LT_7 -> R.string.hint_regist_psn_online_id
|
RegistViewModel.ConsoleVersion.PS4_LT_7 -> R.string.hint_regist_psn_online_id
|
||||||
else -> R.string.hint_regist_psn_account_id
|
else -> R.string.hint_regist_psn_account_id
|
||||||
})
|
})
|
||||||
|
binding.pinHelpBeforeTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_before else R.string.regist_pin_instructions_ps4_before)
|
||||||
|
binding.pinHelpNavigationTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_navigation else R.string.regist_pin_instructions_ps4_navigation)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doRegist()
|
private fun doRegist()
|
||||||
{
|
{
|
||||||
val ps4Version = viewModel.ps4Version.value ?: RegistViewModel.PS4Version.GE_7
|
val ps4Version = viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5
|
||||||
|
|
||||||
val host = hostEditText.text.toString().trim()
|
val host = binding.hostEditText.text.toString().trim()
|
||||||
val hostValid = host.isNotEmpty()
|
val hostValid = host.isNotEmpty()
|
||||||
val broadcast = broadcastCheckBox.isChecked
|
val broadcast = binding.broadcastCheckBox.isChecked
|
||||||
|
|
||||||
val psnId = psnIdEditText.text.toString().trim()
|
val psnId = binding.psnIdEditText.text.toString().trim()
|
||||||
val psnOnlineId: String? = if(ps4Version == RegistViewModel.PS4Version.LT_7) psnId else null
|
val psnOnlineId: String? = if(ps4Version == RegistViewModel.ConsoleVersion.PS4_LT_7) psnId else null
|
||||||
val psnAccountId: ByteArray? =
|
val psnAccountId: ByteArray? =
|
||||||
if(ps4Version != RegistViewModel.PS4Version.LT_7)
|
if(ps4Version != RegistViewModel.ConsoleVersion.PS4_LT_7)
|
||||||
try { Base64.decode(psnId, Base64.DEFAULT) } catch(e: IllegalArgumentException) { null }
|
try { Base64.decode(psnId, Base64.DEFAULT) } catch(e: IllegalArgumentException) { null }
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
val psnIdValid = when(ps4Version)
|
val psnIdValid = when(ps4Version)
|
||||||
{
|
{
|
||||||
RegistViewModel.PS4Version.LT_7 -> psnOnlineId?.isNotEmpty() ?: false
|
RegistViewModel.ConsoleVersion.PS4_LT_7 -> psnOnlineId?.isNotEmpty() ?: false
|
||||||
else -> psnAccountId != null && psnAccountId.size == RegistInfo.ACCOUNT_ID_SIZE
|
else -> psnAccountId != null && psnAccountId.size == RegistInfo.ACCOUNT_ID_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val pin = pinEditText.text.toString()
|
val pin = binding.pinEditText.text.toString()
|
||||||
val pinValid = pin.length == PIN_LENGTH
|
val pinValid = pin.length == PIN_LENGTH
|
||||||
|
|
||||||
hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
|
binding.hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
|
||||||
psnIdEditText.error =
|
binding.psnIdEditText.error =
|
||||||
if(!psnIdValid)
|
if(!psnIdValid)
|
||||||
getString(when(ps4Version)
|
getString(when(ps4Version)
|
||||||
{
|
{
|
||||||
RegistViewModel.PS4Version.LT_7 -> R.string.regist_psn_online_id_invalid
|
RegistViewModel.ConsoleVersion.PS4_LT_7 -> R.string.regist_psn_online_id_invalid
|
||||||
else -> R.string.regist_psn_account_id_invalid
|
else -> R.string.regist_psn_account_id_invalid
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
pinEditText.error = if(!pinValid) getString(R.string.regist_pin_invalid, PIN_LENGTH) else null
|
binding.pinEditText.error = if(!pinValid) getString(R.string.regist_pin_invalid, PIN_LENGTH) else null
|
||||||
|
|
||||||
if(!hostValid || !psnIdValid || !pinValid)
|
if(!hostValid || !psnIdValid || !pinValid)
|
||||||
return
|
return
|
||||||
|
|
||||||
val target = when(ps4Version)
|
val target = when(ps4Version)
|
||||||
{
|
{
|
||||||
RegistViewModel.PS4Version.GE_8 -> Target.PS4_10
|
RegistViewModel.ConsoleVersion.PS5 -> Target.PS5_1
|
||||||
RegistViewModel.PS4Version.GE_7 -> Target.PS4_9
|
RegistViewModel.ConsoleVersion.PS4_GE_8 -> Target.PS4_10
|
||||||
RegistViewModel.PS4Version.LT_7 -> Target.PS4_8
|
RegistViewModel.ConsoleVersion.PS4_GE_7 -> Target.PS4_9
|
||||||
|
RegistViewModel.ConsoleVersion.PS4_LT_7 -> Target.PS4_8
|
||||||
}
|
}
|
||||||
|
|
||||||
val registInfo = RegistInfo(target, host, broadcast, psnOnlineId, psnAccountId, pin.toInt())
|
val registInfo = RegistInfo(target, host, broadcast, psnOnlineId, psnAccountId, pin.toInt())
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.regist
|
package com.metallic.chiaki.regist
|
||||||
|
|
||||||
|
@ -31,8 +16,8 @@ import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.MacAddress
|
import com.metallic.chiaki.common.MacAddress
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
import com.metallic.chiaki.common.getDatabase
|
import com.metallic.chiaki.common.getDatabase
|
||||||
|
import com.metallic.chiaki.databinding.ActivityRegistExecuteBinding
|
||||||
import com.metallic.chiaki.lib.RegistInfo
|
import com.metallic.chiaki.lib.RegistInfo
|
||||||
import kotlinx.android.synthetic.main.activity_regist_execute.*
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class RegistExecuteActivity: AppCompatActivity()
|
class RegistExecuteActivity: AppCompatActivity()
|
||||||
|
@ -46,55 +31,57 @@ class RegistExecuteActivity: AppCompatActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var viewModel: RegistExecuteViewModel
|
private lateinit var viewModel: RegistExecuteViewModel
|
||||||
|
private lateinit var binding: ActivityRegistExecuteBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_regist_execute)
|
binding = ActivityRegistExecuteBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory { RegistExecuteViewModel(getDatabase(this)) })
|
viewModel = ViewModelProvider(this, viewModelFactory { RegistExecuteViewModel(getDatabase(this)) })
|
||||||
.get(RegistExecuteViewModel::class.java)
|
.get(RegistExecuteViewModel::class.java)
|
||||||
|
|
||||||
logTextView.setHorizontallyScrolling(true)
|
binding.logTextView.setHorizontallyScrolling(true)
|
||||||
logTextView.movementMethod = ScrollingMovementMethod()
|
binding.logTextView.movementMethod = ScrollingMovementMethod()
|
||||||
viewModel.logText.observe(this, Observer {
|
viewModel.logText.observe(this, Observer {
|
||||||
val textLayout = logTextView.layout ?: return@Observer
|
val textLayout = binding.logTextView.layout ?: return@Observer
|
||||||
val lineCount = textLayout.lineCount
|
val lineCount = textLayout.lineCount
|
||||||
if(lineCount < 1)
|
if(lineCount < 1)
|
||||||
return@Observer
|
return@Observer
|
||||||
logTextView.text = it
|
binding.logTextView.text = it
|
||||||
val scrollY = textLayout.getLineBottom(lineCount - 1) - logTextView.height + logTextView.paddingTop + logTextView.paddingBottom
|
val scrollY = textLayout.getLineBottom(lineCount - 1) - binding.logTextView.height + binding.logTextView.paddingTop + binding.logTextView.paddingBottom
|
||||||
logTextView.scrollTo(0, max(scrollY, 0))
|
binding.logTextView.scrollTo(0, max(scrollY, 0))
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.state.observe(this, Observer {
|
viewModel.state.observe(this, Observer {
|
||||||
progressBar.visibility = if(it == RegistExecuteViewModel.State.RUNNING) View.VISIBLE else View.GONE
|
binding.progressBar.visibility = if(it == RegistExecuteViewModel.State.RUNNING) View.VISIBLE else View.GONE
|
||||||
when(it)
|
when(it)
|
||||||
{
|
{
|
||||||
RegistExecuteViewModel.State.FAILED ->
|
RegistExecuteViewModel.State.FAILED ->
|
||||||
{
|
{
|
||||||
infoTextView.visibility = View.VISIBLE
|
binding.infoTextView.visibility = View.VISIBLE
|
||||||
infoTextView.setText(R.string.regist_info_failed)
|
binding.infoTextView.setText(R.string.regist_info_failed)
|
||||||
setResult(RESULT_FAILED)
|
setResult(RESULT_FAILED)
|
||||||
}
|
}
|
||||||
RegistExecuteViewModel.State.SUCCESSFUL, RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE ->
|
RegistExecuteViewModel.State.SUCCESSFUL, RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE ->
|
||||||
{
|
{
|
||||||
infoTextView.visibility = View.VISIBLE
|
binding.infoTextView.visibility = View.VISIBLE
|
||||||
infoTextView.setText(R.string.regist_info_success)
|
binding.infoTextView.setText(R.string.regist_info_success)
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
if(it == RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE)
|
if(it == RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE)
|
||||||
showDuplicateDialog()
|
showDuplicateDialog()
|
||||||
}
|
}
|
||||||
RegistExecuteViewModel.State.STOPPED ->
|
RegistExecuteViewModel.State.STOPPED ->
|
||||||
{
|
{
|
||||||
infoTextView.visibility = View.GONE
|
binding.infoTextView.visibility = View.GONE
|
||||||
setResult(Activity.RESULT_CANCELED)
|
setResult(Activity.RESULT_CANCELED)
|
||||||
}
|
}
|
||||||
else -> infoTextView.visibility = View.GONE
|
else -> binding.infoTextView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
shareLogButton.setOnClickListener {
|
binding.shareLogButton.setOnClickListener {
|
||||||
val log = viewModel.logText.value ?: ""
|
val log = viewModel.logText.value ?: ""
|
||||||
Intent(Intent.ACTION_SEND).also {
|
Intent(Intent.ACTION_SEND).also {
|
||||||
it.type = "text/plain"
|
it.type = "text/plain"
|
||||||
|
@ -129,7 +116,7 @@ class RegistExecuteActivity: AppCompatActivity()
|
||||||
if(dialog != null)
|
if(dialog != null)
|
||||||
return
|
return
|
||||||
|
|
||||||
val macStr = viewModel.host?.ps4Mac?.let { MacAddress(it).toString() } ?: ""
|
val macStr = viewModel.host?.serverMac?.let { MacAddress(it).toString() } ?: ""
|
||||||
|
|
||||||
dialog = MaterialAlertDialogBuilder(this)
|
dialog = MaterialAlertDialogBuilder(this)
|
||||||
.setMessage(getString(R.string.alert_regist_duplicate, macStr))
|
.setMessage(getString(R.string.alert_regist_duplicate, macStr))
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.regist
|
package com.metallic.chiaki.regist
|
||||||
|
|
||||||
|
@ -93,7 +78,7 @@ class RegistExecuteViewModel(val database: AppDatabase): ViewModel()
|
||||||
private fun registSuccess(host: RegistHost)
|
private fun registSuccess(host: RegistHost)
|
||||||
{
|
{
|
||||||
this.host = host
|
this.host = host
|
||||||
database.registeredHostDao().getByMac(MacAddress(host.ps4Mac))
|
database.registeredHostDao().getByMac(MacAddress(host.serverMac))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnSuccess {
|
.doOnSuccess {
|
||||||
|
@ -113,7 +98,7 @@ class RegistExecuteViewModel(val database: AppDatabase): ViewModel()
|
||||||
val dao = database.registeredHostDao()
|
val dao = database.registeredHostDao()
|
||||||
val manualHostDao = database.manualHostDao()
|
val manualHostDao = database.manualHostDao()
|
||||||
val registeredHost = RegisteredHost(host)
|
val registeredHost = RegisteredHost(host)
|
||||||
dao.deleteByMac(registeredHost.ps4Mac)
|
dao.deleteByMac(registeredHost.serverMac)
|
||||||
.andThen(dao.insert(registeredHost))
|
.andThen(dao.insert(registeredHost))
|
||||||
.let {
|
.let {
|
||||||
if(assignManualHostId != null)
|
if(assignManualHostId != null)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.regist
|
package com.metallic.chiaki.regist
|
||||||
|
|
||||||
|
@ -22,11 +7,14 @@ import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
class RegistViewModel: ViewModel()
|
class RegistViewModel: ViewModel()
|
||||||
{
|
{
|
||||||
enum class PS4Version {
|
enum class ConsoleVersion {
|
||||||
GE_8,
|
PS5,
|
||||||
GE_7,
|
PS4_GE_8,
|
||||||
LT_7
|
PS4_GE_7,
|
||||||
|
PS4_LT_7;
|
||||||
|
|
||||||
|
val isPS5 get() = this == PS5
|
||||||
}
|
}
|
||||||
|
|
||||||
val ps4Version = MutableLiveData<PS4Version>(PS4Version.GE_8)
|
val ps4Version = MutableLiveData<ConsoleVersion>(ConsoleVersion.PS5)
|
||||||
}
|
}
|
|
@ -1,19 +1,37 @@
|
||||||
package com.metallic.chiaki.session
|
package com.metallic.chiaki.session
|
||||||
|
|
||||||
import android.util.Log
|
import android.content.Context
|
||||||
import android.view.InputDevice
|
import android.hardware.*
|
||||||
import android.view.KeyEvent
|
import android.view.*
|
||||||
import android.view.MotionEvent
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent
|
||||||
import com.metallic.chiaki.common.Preferences
|
import com.metallic.chiaki.common.Preferences
|
||||||
import com.metallic.chiaki.lib.ControllerState
|
import com.metallic.chiaki.lib.ControllerState
|
||||||
|
|
||||||
class StreamInput(val preferences: Preferences)
|
class StreamInput(val context: Context, val preferences: Preferences)
|
||||||
{
|
{
|
||||||
var controllerStateChangedCallback: ((ControllerState) -> Unit)? = null
|
var controllerStateChangedCallback: ((ControllerState) -> Unit)? = null
|
||||||
|
|
||||||
val controllerState: ControllerState get()
|
val controllerState: ControllerState get()
|
||||||
{
|
{
|
||||||
val controllerState = keyControllerState or motionControllerState
|
val controllerState = sensorControllerState or keyControllerState or motionControllerState
|
||||||
|
|
||||||
|
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
when(windowManager.defaultDisplay.rotation)
|
||||||
|
{
|
||||||
|
Surface.ROTATION_90 -> {
|
||||||
|
controllerState.accelX *= -1.0f
|
||||||
|
controllerState.accelZ *= -1.0f
|
||||||
|
controllerState.gyroX *= -1.0f
|
||||||
|
controllerState.gyroZ *= -1.0f
|
||||||
|
controllerState.orientX *= -1.0f
|
||||||
|
controllerState.orientZ *= -1.0f
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
// prioritize motion controller's l2 and r2 over key
|
// prioritize motion controller's l2 and r2 over key
|
||||||
// (some controllers send only key, others both but key earlier than full press)
|
// (some controllers send only key, others both but key earlier than full press)
|
||||||
|
@ -25,6 +43,7 @@ class StreamInput(val preferences: Preferences)
|
||||||
return controllerState or touchControllerState
|
return controllerState or touchControllerState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val sensorControllerState = ControllerState() // from Motion Sensors
|
||||||
private val keyControllerState = ControllerState() // from KeyEvents
|
private val keyControllerState = ControllerState() // from KeyEvents
|
||||||
private val motionControllerState = ControllerState() // from MotionEvents
|
private val motionControllerState = ControllerState() // from MotionEvents
|
||||||
var touchControllerState = ControllerState()
|
var touchControllerState = ControllerState()
|
||||||
|
@ -36,6 +55,66 @@ class StreamInput(val preferences: Preferences)
|
||||||
|
|
||||||
private val swapCrossMoon = preferences.swapCrossMoon
|
private val swapCrossMoon = preferences.swapCrossMoon
|
||||||
|
|
||||||
|
private val sensorEventListener = object: SensorEventListener {
|
||||||
|
override fun onSensorChanged(event: SensorEvent)
|
||||||
|
{
|
||||||
|
when(event.sensor.type)
|
||||||
|
{
|
||||||
|
Sensor.TYPE_ACCELEROMETER -> {
|
||||||
|
sensorControllerState.accelX = event.values[1] / SensorManager.GRAVITY_EARTH
|
||||||
|
sensorControllerState.accelY = event.values[2] / SensorManager.GRAVITY_EARTH
|
||||||
|
sensorControllerState.accelZ = event.values[0] / SensorManager.GRAVITY_EARTH
|
||||||
|
}
|
||||||
|
Sensor.TYPE_GYROSCOPE -> {
|
||||||
|
sensorControllerState.gyroX = event.values[1]
|
||||||
|
sensorControllerState.gyroY = event.values[2]
|
||||||
|
sensorControllerState.gyroZ = event.values[0]
|
||||||
|
}
|
||||||
|
Sensor.TYPE_ROTATION_VECTOR -> {
|
||||||
|
val q = floatArrayOf(0f, 0f, 0f, 0f)
|
||||||
|
SensorManager.getQuaternionFromVector(q, event.values)
|
||||||
|
sensorControllerState.orientX = q[2]
|
||||||
|
sensorControllerState.orientY = q[3]
|
||||||
|
sensorControllerState.orientZ = q[1]
|
||||||
|
sensorControllerState.orientW = q[0]
|
||||||
|
}
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
controllerStateUpdated()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val motionLifecycleObserver = object: LifecycleObserver {
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
|
fun onResume()
|
||||||
|
{
|
||||||
|
val samplingPeriodUs = 4000
|
||||||
|
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
listOfNotNull(
|
||||||
|
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
|
||||||
|
sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
|
||||||
|
sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||||
|
).forEach {
|
||||||
|
sensorManager.registerListener(sensorEventListener, it, samplingPeriodUs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||||
|
fun onPause()
|
||||||
|
{
|
||||||
|
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
sensorManager.unregisterListener(sensorEventListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observe(lifecycleOwner: LifecycleOwner)
|
||||||
|
{
|
||||||
|
if(preferences.motionEnabled)
|
||||||
|
lifecycleOwner.lifecycle.addObserver(motionLifecycleObserver)
|
||||||
|
}
|
||||||
|
|
||||||
private fun controllerStateUpdated()
|
private fun controllerStateUpdated()
|
||||||
{
|
{
|
||||||
controllerStateChangedCallback?.let { it(controllerState) }
|
controllerStateChangedCallback?.let { it(controllerState) }
|
||||||
|
@ -98,7 +177,7 @@ class StreamInput(val preferences: Preferences)
|
||||||
{
|
{
|
||||||
if(event.source and InputDevice.SOURCE_CLASS_JOYSTICK != InputDevice.SOURCE_CLASS_JOYSTICK)
|
if(event.source and InputDevice.SOURCE_CLASS_JOYSTICK != InputDevice.SOURCE_CLASS_JOYSTICK)
|
||||||
return false
|
return false
|
||||||
fun Float.signedAxis() = (this * Short.MAX_VALUE).toShort()
|
fun Float.signedAxis() = (this * Short.MAX_VALUE).toInt().toShort()
|
||||||
fun Float.unsignedAxis() = (this * UByte.MAX_VALUE.toFloat()).toUInt().toUByte()
|
fun Float.unsignedAxis() = (this * UByte.MAX_VALUE.toFloat()).toUInt().toUByte()
|
||||||
motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
|
motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
|
||||||
motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()
|
motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.session
|
package com.metallic.chiaki.session
|
||||||
|
|
||||||
|
@ -40,8 +25,11 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
||||||
|
|
||||||
private val _state = MutableLiveData<StreamState>(StreamStateIdle)
|
private val _state = MutableLiveData<StreamState>(StreamStateIdle)
|
||||||
val state: LiveData<StreamState> get() = _state
|
val state: LiveData<StreamState> get() = _state
|
||||||
|
private val _rumbleState = MutableLiveData<RumbleEvent>(RumbleEvent(0U, 0U))
|
||||||
|
val rumbleState: LiveData<RumbleEvent> get() = _rumbleState
|
||||||
|
|
||||||
var surfaceTexture: SurfaceTexture? = null
|
private var surfaceTexture: SurfaceTexture? = null
|
||||||
|
private var surface: Surface? = null
|
||||||
|
|
||||||
init
|
init
|
||||||
{
|
{
|
||||||
|
@ -74,9 +62,9 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
||||||
_state.value = StreamStateConnecting
|
_state.value = StreamStateConnecting
|
||||||
session.eventCallback = this::eventCallback
|
session.eventCallback = this::eventCallback
|
||||||
session.start()
|
session.start()
|
||||||
val surfaceTexture = surfaceTexture
|
val surface = surface
|
||||||
if(surfaceTexture != null)
|
if(surface != null)
|
||||||
session.setSurface(Surface(surfaceTexture))
|
session.setSurface(surface)
|
||||||
this.session = session
|
this.session = session
|
||||||
}
|
}
|
||||||
catch(e: CreateError)
|
catch(e: CreateError)
|
||||||
|
@ -101,9 +89,30 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
||||||
event.pinIncorrect
|
event.pinIncorrect
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
is RumbleEvent -> _rumbleState.postValue(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun attachToSurfaceView(surfaceView: SurfaceView)
|
||||||
|
{
|
||||||
|
surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
|
||||||
|
override fun surfaceCreated(holder: SurfaceHolder) { }
|
||||||
|
|
||||||
|
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int)
|
||||||
|
{
|
||||||
|
val surface = holder.surface
|
||||||
|
this@StreamSession.surface = surface
|
||||||
|
session?.setSurface(surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun surfaceDestroyed(holder: SurfaceHolder)
|
||||||
|
{
|
||||||
|
this@StreamSession.surface = null
|
||||||
|
session?.setSurface(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fun attachToTextureView(textureView: TextureView)
|
fun attachToTextureView(textureView: TextureView)
|
||||||
{
|
{
|
||||||
textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
|
textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
|
||||||
|
@ -112,6 +121,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
||||||
if(surfaceTexture != null)
|
if(surfaceTexture != null)
|
||||||
return
|
return
|
||||||
surfaceTexture = surface
|
surfaceTexture = surface
|
||||||
|
this@StreamSession.surface = Surface(surfaceTexture)
|
||||||
session?.setSurface(Surface(surface))
|
session?.setSurface(Surface(surface))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +137,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
||||||
|
|
||||||
val surfaceTexture = surfaceTexture
|
val surfaceTexture = surfaceTexture
|
||||||
if(surfaceTexture != null)
|
if(surfaceTexture != null)
|
||||||
textureView.surfaceTexture = surfaceTexture
|
textureView.setSurfaceTexture(surfaceTexture)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLoginPin(pin: String)
|
fun setLoginPin(pin: String)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
@ -24,7 +9,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import kotlinx.android.synthetic.main.activity_settings.*
|
import com.metallic.chiaki.databinding.ActivitySettingsBinding
|
||||||
|
|
||||||
interface TitleFragment
|
interface TitleFragment
|
||||||
{
|
{
|
||||||
|
@ -33,20 +18,23 @@ interface TitleFragment
|
||||||
|
|
||||||
class SettingsActivity: AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
|
class SettingsActivity: AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
|
||||||
{
|
{
|
||||||
|
private lateinit var binding: ActivitySettingsBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_settings)
|
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
title = ""
|
title = ""
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
val rootFragment = SettingsFragment()
|
val rootFragment = SettingsFragment()
|
||||||
replaceFragment(rootFragment, false)
|
replaceFragment(rootFragment, false)
|
||||||
supportFragmentManager.addOnBackStackChangedListener {
|
supportFragmentManager.addOnBackStackChangedListener {
|
||||||
val titleFragment = supportFragmentManager.findFragmentById(R.id.settingsFragment) as? TitleFragment ?: return@addOnBackStackChangedListener
|
val titleFragment = supportFragmentManager.findFragmentById(R.id.settingsFragment) as? TitleFragment ?: return@addOnBackStackChangedListener
|
||||||
titleTextView.text = titleFragment.getTitle(resources)
|
binding.titleTextView.text = titleFragment.getTitle(resources)
|
||||||
}
|
}
|
||||||
titleTextView.text = rootFragment.getTitle(resources)
|
binding.titleTextView.text = rootFragment.getTitle(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference) = when(pref.fragment)
|
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference) = when(pref.fragment)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
@ -40,6 +25,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
{
|
{
|
||||||
preferences.logVerboseKey -> preferences.logVerbose
|
preferences.logVerboseKey -> preferences.logVerbose
|
||||||
preferences.swapCrossMoonKey -> preferences.swapCrossMoon
|
preferences.swapCrossMoonKey -> preferences.swapCrossMoon
|
||||||
|
preferences.rumbleEnabledKey -> preferences.rumbleEnabled
|
||||||
|
preferences.motionEnabledKey -> preferences.motionEnabled
|
||||||
|
preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled
|
||||||
else -> defValue
|
else -> defValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +37,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
{
|
{
|
||||||
preferences.logVerboseKey -> preferences.logVerbose = value
|
preferences.logVerboseKey -> preferences.logVerbose = value
|
||||||
preferences.swapCrossMoonKey -> preferences.swapCrossMoon = value
|
preferences.swapCrossMoonKey -> preferences.swapCrossMoon = value
|
||||||
|
preferences.rumbleEnabledKey -> preferences.rumbleEnabled = value
|
||||||
|
preferences.motionEnabledKey -> preferences.motionEnabled = value
|
||||||
|
preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +48,7 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
preferences.resolutionKey -> preferences.resolution.value
|
preferences.resolutionKey -> preferences.resolution.value
|
||||||
preferences.fpsKey -> preferences.fps.value
|
preferences.fpsKey -> preferences.fps.value
|
||||||
preferences.bitrateKey -> preferences.bitrate?.toString() ?: ""
|
preferences.bitrateKey -> preferences.bitrate?.toString() ?: ""
|
||||||
|
preferences.codecKey -> preferences.codec.value
|
||||||
else -> defValue
|
else -> defValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +67,11 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
||||||
preferences.fps = fps
|
preferences.fps = fps
|
||||||
}
|
}
|
||||||
preferences.bitrateKey -> preferences.bitrate = value?.toIntOrNull()
|
preferences.bitrateKey -> preferences.bitrate = value?.toIntOrNull()
|
||||||
|
preferences.codecKey ->
|
||||||
|
{
|
||||||
|
val codec = Preferences.Codec.values().firstOrNull { it.value == value } ?: return
|
||||||
|
preferences.codec = codec
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +123,11 @@ class SettingsFragment: PreferenceFragmentCompat(), TitleFragment
|
||||||
bitratePreference?.summaryProvider = bitrateSummaryProvider
|
bitratePreference?.summaryProvider = bitrateSummaryProvider
|
||||||
})
|
})
|
||||||
|
|
||||||
|
preferenceScreen.findPreference<ListPreference>(getString(R.string.preferences_codec_key))?.let {
|
||||||
|
it.entryValues = Preferences.codecAll.map { codec -> codec.value }.toTypedArray()
|
||||||
|
it.entries = Preferences.codecAll.map { codec -> getString(codec.title) }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
val registeredHostsPreference = preferenceScreen.findPreference<Preference>("registered_hosts")
|
val registeredHostsPreference = preferenceScreen.findPreference<Preference>("registered_hosts")
|
||||||
viewModel.registeredHostsCount.observe(this, Observer {
|
viewModel.registeredHostsCount.observe(this, Observer {
|
||||||
registeredHostsPreference?.summary = getString(R.string.preferences_registered_hosts_summary, it)
|
registeredHostsPreference?.summary = getString(R.string.preferences_registered_hosts_summary, it)
|
||||||
|
|
|
@ -1,29 +1,14 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
import android.view.View
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.LogFile
|
import com.metallic.chiaki.common.LogFile
|
||||||
import com.metallic.chiaki.common.ext.inflate
|
import com.metallic.chiaki.common.ext.inflate
|
||||||
import kotlinx.android.synthetic.main.item_log_file.view.*
|
import com.metallic.chiaki.databinding.ItemLogFileBinding
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -35,7 +20,7 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
|
||||||
private val dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
|
private val dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||||
private val timeFormat = SimpleDateFormat("HH:mm:ss:SSS", Locale.getDefault())
|
private val timeFormat = SimpleDateFormat("HH:mm:ss:SSS", Locale.getDefault())
|
||||||
|
|
||||||
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
class ViewHolder(val binding: ItemLogFileBinding): RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
var logFiles: List<LogFile> = listOf()
|
var logFiles: List<LogFile> = listOf()
|
||||||
set(value)
|
set(value)
|
||||||
|
@ -44,16 +29,16 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(parent.inflate(R.layout.item_log_file))
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
|
ViewHolder(ItemLogFileBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
||||||
override fun getItemCount() = logFiles.size
|
override fun getItemCount() = logFiles.size
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int)
|
override fun onBindViewHolder(holder: ViewHolder, position: Int)
|
||||||
{
|
{
|
||||||
val view = holder.itemView
|
|
||||||
val logFile = logFiles[position]
|
val logFile = logFiles[position]
|
||||||
view.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
|
holder.binding.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
|
||||||
view.summaryTextView.text = logFile.filename
|
holder.binding.summaryTextView.text = logFile.filename
|
||||||
view.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
|
holder.binding.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
@ -36,29 +21,35 @@ import com.metallic.chiaki.common.LogFile
|
||||||
import com.metallic.chiaki.common.LogManager
|
import com.metallic.chiaki.common.LogManager
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
import com.metallic.chiaki.common.fileProviderAuthority
|
import com.metallic.chiaki.common.fileProviderAuthority
|
||||||
import kotlinx.android.synthetic.main.fragment_settings_logs.*
|
import com.metallic.chiaki.databinding.FragmentSettingsLogsBinding
|
||||||
|
|
||||||
class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
|
class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
|
||||||
{
|
{
|
||||||
private lateinit var viewModel: SettingsLogsViewModel
|
private lateinit var viewModel: SettingsLogsViewModel
|
||||||
|
|
||||||
|
private var _binding: FragmentSettingsLogsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
inflater.inflate(R.layout.fragment_settings_logs, container, false)
|
FragmentSettingsLogsBinding.inflate(inflater, container, false).let {
|
||||||
|
_binding = it
|
||||||
|
it.root
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
val context = context!!
|
val context = requireContext()
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory { SettingsLogsViewModel(LogManager(context)) })
|
viewModel = ViewModelProvider(this, viewModelFactory { SettingsLogsViewModel(LogManager(context)) })
|
||||||
.get(SettingsLogsViewModel::class.java)
|
.get(SettingsLogsViewModel::class.java)
|
||||||
|
|
||||||
val adapter = SettingsLogsAdapter()
|
val adapter = SettingsLogsAdapter()
|
||||||
logsRecyclerView.layoutManager = LinearLayoutManager(context)
|
binding.logsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||||
logsRecyclerView.adapter = adapter
|
binding.logsRecyclerView.adapter = adapter
|
||||||
adapter.shareCallback = this::shareLogFile
|
adapter.shareCallback = this::shareLogFile
|
||||||
viewModel.sessionLogs.observe(viewLifecycleOwner, Observer {
|
viewModel.sessionLogs.observe(viewLifecycleOwner, Observer {
|
||||||
adapter.logFiles = it
|
adapter.logFiles = it
|
||||||
emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
|
binding.emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
|
||||||
})
|
})
|
||||||
|
|
||||||
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
|
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
|
||||||
|
@ -70,7 +61,7 @@ class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
|
||||||
viewModel.deleteLog(file)
|
viewModel.deleteLog(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(logsRecyclerView)
|
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(binding.logsRecyclerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTitle(resources: Resources): String = resources.getString(R.string.preferences_logs_title)
|
override fun getTitle(resources: Resources): String = resources.getString(R.string.preferences_logs_title)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,16 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
import android.view.View
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.metallic.chiaki.R
|
|
||||||
import com.metallic.chiaki.common.RegisteredHost
|
import com.metallic.chiaki.common.RegisteredHost
|
||||||
import com.metallic.chiaki.common.ext.inflate
|
import com.metallic.chiaki.databinding.ItemRegisteredHostBinding
|
||||||
import kotlinx.android.synthetic.main.item_registered_host.view.*
|
|
||||||
|
|
||||||
class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHostsAdapter.ViewHolder>()
|
class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHostsAdapter.ViewHolder>()
|
||||||
{
|
{
|
||||||
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
|
class ViewHolder(val binding: ItemRegisteredHostBinding): RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
var hosts: List<RegisteredHost> = listOf()
|
var hosts: List<RegisteredHost> = listOf()
|
||||||
set(value)
|
set(value)
|
||||||
|
@ -36,15 +19,15 @@ class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHos
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(parent.inflate(R.layout.item_registered_host))
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
|
||||||
|
= ViewHolder(ItemRegisteredHostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
||||||
override fun getItemCount() = hosts.size
|
override fun getItemCount() = hosts.size
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int)
|
override fun onBindViewHolder(holder: ViewHolder, position: Int)
|
||||||
{
|
{
|
||||||
val view = holder.itemView
|
|
||||||
val host = hosts[position]
|
val host = hosts[position]
|
||||||
view.nameTextView.text = host.ps4Nickname
|
holder.binding.nameTextView.text = "${host.serverNickname} (${if(host.target.isPS5) "PS5" else "PS4"})"
|
||||||
view.summaryTextView.text = host.ps4Mac.toString()
|
holder.binding.summaryTextView.text = host.serverMac.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
@ -35,32 +20,39 @@ import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.ext.putRevealExtra
|
import com.metallic.chiaki.common.ext.putRevealExtra
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
import com.metallic.chiaki.common.getDatabase
|
import com.metallic.chiaki.common.getDatabase
|
||||||
|
import com.metallic.chiaki.databinding.FragmentSettingsRegisteredHostsBinding
|
||||||
import com.metallic.chiaki.regist.RegistActivity
|
import com.metallic.chiaki.regist.RegistActivity
|
||||||
import kotlinx.android.synthetic.main.fragment_settings_registered_hosts.*
|
|
||||||
|
|
||||||
class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
|
class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
|
||||||
{
|
{
|
||||||
private lateinit var viewModel: SettingsRegisteredHostsViewModel
|
private lateinit var viewModel: SettingsRegisteredHostsViewModel
|
||||||
|
|
||||||
|
private var _binding: FragmentSettingsRegisteredHostsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
inflater.inflate(R.layout.fragment_settings_registered_hosts, container, false)
|
FragmentSettingsRegisteredHostsBinding.inflate(inflater, container, false).let {
|
||||||
|
_binding = it
|
||||||
|
it.root
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context!!)) })
|
val context = requireContext()
|
||||||
|
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context)) })
|
||||||
.get(SettingsRegisteredHostsViewModel::class.java)
|
.get(SettingsRegisteredHostsViewModel::class.java)
|
||||||
|
|
||||||
val adapter = SettingsRegisteredHostsAdapter()
|
val adapter = SettingsRegisteredHostsAdapter()
|
||||||
hostsRecyclerView.layoutManager = LinearLayoutManager(context)
|
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||||
hostsRecyclerView.adapter = adapter
|
binding.hostsRecyclerView.adapter = adapter
|
||||||
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context!!)
|
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
|
||||||
{
|
{
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int)
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int)
|
||||||
{
|
{
|
||||||
val pos = viewHolder.adapterPosition
|
val pos = viewHolder.adapterPosition
|
||||||
val host = viewModel.registeredHosts.value?.getOrNull(pos) ?: return
|
val host = viewModel.registeredHosts.value?.getOrNull(pos) ?: return
|
||||||
MaterialAlertDialogBuilder(viewHolder.itemView.context)
|
MaterialAlertDialogBuilder(viewHolder.itemView.context)
|
||||||
.setMessage(getString(R.string.alert_message_delete_registered_host, host.ps4Nickname, host.ps4Mac.toString()))
|
.setMessage(getString(R.string.alert_message_delete_registered_host, host.serverNickname, host.serverMac.toString()))
|
||||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
.setPositiveButton(R.string.action_delete) { _, _ ->
|
||||||
viewModel.deleteHost(host)
|
viewModel.deleteHost(host)
|
||||||
}
|
}
|
||||||
|
@ -71,15 +63,15 @@ class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(hostsRecyclerView)
|
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(binding.hostsRecyclerView)
|
||||||
viewModel.registeredHosts.observe(this, Observer {
|
viewModel.registeredHosts.observe(this, Observer {
|
||||||
adapter.hosts = it
|
adapter.hosts = it
|
||||||
emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
|
binding.emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
|
||||||
})
|
})
|
||||||
|
|
||||||
floatingActionButton.setOnClickListener {
|
binding.floatingActionButton.setOnClickListener {
|
||||||
Intent(context, RegistActivity::class.java).also {
|
Intent(context, RegistActivity::class.java).also {
|
||||||
it.putRevealExtra(floatingActionButton, rootLayout)
|
it.putRevealExtra(binding.floatingActionButton, binding.rootLayout)
|
||||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle())
|
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.settings
|
package com.metallic.chiaki.settings
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.metallic.chiaki.stream
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
// see ExoPlayer's AspectRatioFrameLayout
|
||||||
|
class AspectRatioFrameLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): FrameLayout(context, attrs)
|
||||||
|
{
|
||||||
|
companion object
|
||||||
|
{
|
||||||
|
private const val MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f
|
||||||
|
}
|
||||||
|
|
||||||
|
var aspectRatio = 0f
|
||||||
|
set(value)
|
||||||
|
{
|
||||||
|
if(field != value)
|
||||||
|
{
|
||||||
|
field = value
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode: TransformMode = TransformMode.FIT
|
||||||
|
set(value)
|
||||||
|
{
|
||||||
|
if(field != value)
|
||||||
|
{
|
||||||
|
field = value
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
|
||||||
|
{
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
if(aspectRatio <= 0)
|
||||||
|
{
|
||||||
|
// Aspect ratio not set.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var width = measuredWidth
|
||||||
|
var height = measuredHeight
|
||||||
|
val viewAspectRatio = width.toFloat() / height
|
||||||
|
val aspectDeformation = aspectRatio / viewAspectRatio - 1
|
||||||
|
if(Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION)
|
||||||
|
return
|
||||||
|
when(mode)
|
||||||
|
{
|
||||||
|
TransformMode.ZOOM ->
|
||||||
|
if(aspectDeformation > 0)
|
||||||
|
width = (height * aspectRatio).toInt()
|
||||||
|
else
|
||||||
|
height = (width / aspectRatio).toInt()
|
||||||
|
TransformMode.FIT ->
|
||||||
|
if(aspectDeformation > 0)
|
||||||
|
height = (width / aspectRatio).toInt()
|
||||||
|
else
|
||||||
|
width = (height * aspectRatio).toInt()
|
||||||
|
TransformMode.STRETCH -> {}
|
||||||
|
}
|
||||||
|
super.onMeasure(
|
||||||
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.stream
|
package com.metallic.chiaki.stream
|
||||||
|
|
||||||
|
@ -21,12 +6,8 @@ import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.os.Bundle
|
import android.os.*
|
||||||
import android.os.Handler
|
import android.view.*
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.TextureView
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
@ -35,15 +16,18 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import com.metallic.chiaki.common.LogManager
|
|
||||||
import com.metallic.chiaki.common.Preferences
|
import com.metallic.chiaki.common.Preferences
|
||||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||||
|
import com.metallic.chiaki.databinding.ActivityStreamBinding
|
||||||
import com.metallic.chiaki.lib.ConnectInfo
|
import com.metallic.chiaki.lib.ConnectInfo
|
||||||
import com.metallic.chiaki.lib.ConnectVideoProfile
|
import com.metallic.chiaki.lib.ConnectVideoProfile
|
||||||
import com.metallic.chiaki.session.*
|
import com.metallic.chiaki.session.*
|
||||||
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
|
import com.metallic.chiaki.touchcontrols.DefaultTouchControlsFragment
|
||||||
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
|
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
|
||||||
import kotlinx.android.synthetic.main.activity_stream.*
|
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.rxkotlin.addTo
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
private sealed class DialogContents
|
private sealed class DialogContents
|
||||||
private object StreamQuitDialog: DialogContents()
|
private object StreamQuitDialog: DialogContents()
|
||||||
|
@ -59,6 +43,8 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var viewModel: StreamViewModel
|
private lateinit var viewModel: StreamViewModel
|
||||||
|
private lateinit var binding: ActivityStreamBinding
|
||||||
|
|
||||||
private val uiVisibilityHandler = Handler()
|
private val uiVisibilityHandler = Handler()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
|
@ -73,64 +59,76 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel = ViewModelProvider(this, viewModelFactory {
|
viewModel = ViewModelProvider(this, viewModelFactory {
|
||||||
StreamViewModel(Preferences(this), LogManager(this), connectInfo)
|
StreamViewModel(application, connectInfo)
|
||||||
})[StreamViewModel::class.java]
|
})[StreamViewModel::class.java]
|
||||||
|
|
||||||
setContentView(R.layout.activity_stream)
|
viewModel.input.observe(this)
|
||||||
|
|
||||||
|
binding = ActivityStreamBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
window.decorView.setOnSystemUiVisibilityChangeListener(this)
|
window.decorView.setOnSystemUiVisibilityChangeListener(this)
|
||||||
|
|
||||||
viewModel.onScreenControlsEnabled.observe(this, Observer {
|
viewModel.onScreenControlsEnabled.observe(this, Observer {
|
||||||
if(onScreenControlsSwitch.isChecked != it)
|
if(binding.onScreenControlsSwitch.isChecked != it)
|
||||||
onScreenControlsSwitch.isChecked = it
|
binding.onScreenControlsSwitch.isChecked = it
|
||||||
if(onScreenControlsSwitch.isChecked)
|
if(binding.onScreenControlsSwitch.isChecked)
|
||||||
touchpadOnlySwitch.isChecked = false
|
binding.touchpadOnlySwitch.isChecked = false
|
||||||
})
|
})
|
||||||
onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
viewModel.setOnScreenControlsEnabled(isChecked)
|
viewModel.setOnScreenControlsEnabled(isChecked)
|
||||||
showOverlay()
|
showOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.touchpadOnlyEnabled.observe(this, Observer {
|
viewModel.touchpadOnlyEnabled.observe(this, Observer {
|
||||||
if(touchpadOnlySwitch.isChecked != it)
|
if(binding.touchpadOnlySwitch.isChecked != it)
|
||||||
touchpadOnlySwitch.isChecked = it
|
binding.touchpadOnlySwitch.isChecked = it
|
||||||
if(touchpadOnlySwitch.isChecked)
|
if(binding.touchpadOnlySwitch.isChecked)
|
||||||
onScreenControlsSwitch.isChecked = false
|
binding.onScreenControlsSwitch.isChecked = false
|
||||||
})
|
})
|
||||||
touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
viewModel.setTouchpadOnlyEnabled(isChecked)
|
viewModel.setTouchpadOnlyEnabled(isChecked)
|
||||||
showOverlay()
|
showOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
displayModeToggle.addOnButtonCheckedListener { _, checkedId, _ ->
|
binding.displayModeToggle.addOnButtonCheckedListener { _, _, _ ->
|
||||||
// following 'if' is a workaround until selectionRequired for MaterialButtonToggleGroup
|
adjustStreamViewAspect()
|
||||||
// comes out of alpha.
|
|
||||||
// See https://stackoverflow.com/questions/56164004/required-single-selection-on-materialbuttontogglegroup
|
|
||||||
if (displayModeToggle.checkedButtonId == -1)
|
|
||||||
displayModeToggle.check(checkedId)
|
|
||||||
|
|
||||||
adjustTextureViewAspect()
|
|
||||||
showOverlay()
|
showOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.session.attachToTextureView(textureView)
|
//viewModel.session.attachToTextureView(textureView)
|
||||||
|
viewModel.session.attachToSurfaceView(binding.surfaceView)
|
||||||
viewModel.session.state.observe(this, Observer { this.stateChanged(it) })
|
viewModel.session.state.observe(this, Observer { this.stateChanged(it) })
|
||||||
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
adjustStreamViewAspect()
|
||||||
adjustTextureViewAspect()
|
|
||||||
|
if(Preferences(this).rumbleEnabled)
|
||||||
|
{
|
||||||
|
val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
|
||||||
|
viewModel.session.rumbleState.observe(this, Observer {
|
||||||
|
val amplitude = min(255, (it.left.toInt() + it.right.toInt()) / 2)
|
||||||
|
vibrator.cancel()
|
||||||
|
if(amplitude == 0)
|
||||||
|
return@Observer
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
vibrator.vibrate(VibrationEffect.createOneShot(1000, amplitude))
|
||||||
|
else
|
||||||
|
vibrator.vibrate(1000)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val controlsDisposable = CompositeDisposable()
|
||||||
|
|
||||||
override fun onAttachFragment(fragment: Fragment)
|
override fun onAttachFragment(fragment: Fragment)
|
||||||
{
|
{
|
||||||
super.onAttachFragment(fragment)
|
super.onAttachFragment(fragment)
|
||||||
if(fragment is TouchControlsFragment)
|
if(fragment is TouchControlsFragment)
|
||||||
{
|
{
|
||||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
fragment.controllerState
|
||||||
|
.subscribe { viewModel.input.touchControllerState = it }
|
||||||
|
.addTo(controlsDisposable)
|
||||||
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
|
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
|
||||||
}
|
if(fragment is TouchpadOnlyFragment)
|
||||||
if(fragment is TouchpadOnlyFragment)
|
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
||||||
{
|
|
||||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
|
||||||
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +145,12 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
viewModel.session.pause()
|
viewModel.session.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy()
|
||||||
|
{
|
||||||
|
super.onDestroy()
|
||||||
|
controlsDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
private fun reconnect()
|
private fun reconnect()
|
||||||
{
|
{
|
||||||
viewModel.session.shutdown()
|
viewModel.session.shutdown()
|
||||||
|
@ -165,14 +169,14 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
|
|
||||||
private fun showOverlay()
|
private fun showOverlay()
|
||||||
{
|
{
|
||||||
overlay.isVisible = true
|
binding.overlay.isVisible = true
|
||||||
overlay.animate()
|
binding.overlay.animate()
|
||||||
.alpha(1.0f)
|
.alpha(1.0f)
|
||||||
.setListener(object: AnimatorListenerAdapter()
|
.setListener(object: AnimatorListenerAdapter()
|
||||||
{
|
{
|
||||||
override fun onAnimationEnd(animation: Animator?)
|
override fun onAnimationEnd(animation: Animator)
|
||||||
{
|
{
|
||||||
overlay.alpha = 1.0f
|
binding.overlay.alpha = 1.0f
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
uiVisibilityHandler.removeCallbacks(hideSystemUIRunnable)
|
uiVisibilityHandler.removeCallbacks(hideSystemUIRunnable)
|
||||||
|
@ -181,13 +185,13 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
|
|
||||||
private fun hideOverlay()
|
private fun hideOverlay()
|
||||||
{
|
{
|
||||||
overlay.animate()
|
binding.overlay.animate()
|
||||||
.alpha(0.0f)
|
.alpha(0.0f)
|
||||||
.setListener(object: AnimatorListenerAdapter()
|
.setListener(object: AnimatorListenerAdapter()
|
||||||
{
|
{
|
||||||
override fun onAnimationEnd(animation: Animator?)
|
override fun onAnimationEnd(animation: Animator)
|
||||||
{
|
{
|
||||||
overlay.isGone = true
|
binding.overlay.isGone = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -220,34 +224,39 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
|
|
||||||
private fun stateChanged(state: StreamState)
|
private fun stateChanged(state: StreamState)
|
||||||
{
|
{
|
||||||
progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
|
binding.progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
when(state)
|
when(state)
|
||||||
{
|
{
|
||||||
is StreamStateQuit ->
|
is StreamStateQuit ->
|
||||||
{
|
{
|
||||||
if(!state.reason.isStopped && dialogContents != StreamQuitDialog)
|
if(dialogContents != StreamQuitDialog)
|
||||||
{
|
{
|
||||||
dialog?.dismiss()
|
if(state.reason.isError)
|
||||||
val reasonStr = state.reasonString
|
{
|
||||||
val dialog = MaterialAlertDialogBuilder(this)
|
dialog?.dismiss()
|
||||||
.setMessage(getString(R.string.alert_message_session_quit, state.reason.toString())
|
val reasonStr = state.reasonString
|
||||||
+ (if(reasonStr != null) "\n$reasonStr" else ""))
|
val dialog = MaterialAlertDialogBuilder(this)
|
||||||
.setPositiveButton(R.string.action_reconnect) { _, _ ->
|
.setMessage(getString(R.string.alert_message_session_quit, state.reason.toString())
|
||||||
dialog = null
|
+ (if(reasonStr != null) "\n$reasonStr" else ""))
|
||||||
reconnect()
|
.setPositiveButton(R.string.action_reconnect) { _, _ ->
|
||||||
}
|
dialog = null
|
||||||
.setOnCancelListener {
|
reconnect()
|
||||||
dialog = null
|
}
|
||||||
finish()
|
.setOnCancelListener {
|
||||||
}
|
dialog = null
|
||||||
.setNegativeButton(R.string.action_quit_session) { _, _ ->
|
finish()
|
||||||
dialog = null
|
}
|
||||||
finish()
|
.setNegativeButton(R.string.action_quit_session) { _, _ ->
|
||||||
}
|
dialog = null
|
||||||
.create()
|
finish()
|
||||||
dialogContents = StreamQuitDialog
|
}
|
||||||
dialog.show()
|
.create()
|
||||||
|
dialogContents = StreamQuitDialog
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,77 +311,89 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustTextureViewAspect()
|
private fun adjustTextureViewAspect(textureView: TextureView)
|
||||||
{
|
{
|
||||||
val displayInfo = DisplayInfo(viewModel.session.connectInfo.videoProfile, textureView)
|
val trans = TextureViewTransform(viewModel.session.connectInfo.videoProfile, textureView)
|
||||||
val resolution = displayInfo.computeResolutionFor(displayModeToggle.checkedButtonId)
|
val resolution = trans.resolutionFor(TransformMode.fromButton(binding.displayModeToggle.checkedButtonId))
|
||||||
|
|
||||||
Matrix().also {
|
Matrix().also {
|
||||||
textureView.getTransform(it)
|
textureView.getTransform(it)
|
||||||
it.setScale(resolution.width / displayInfo.viewWidth, resolution.height / displayInfo.viewHeight)
|
it.setScale(resolution.width / trans.viewWidth, resolution.height / trans.viewHeight)
|
||||||
it.postTranslate((displayInfo.viewWidth - resolution.width) * 0.5f, (displayInfo.viewHeight - resolution.height) * 0.5f)
|
it.postTranslate((trans.viewWidth - resolution.width) * 0.5f, (trans.viewHeight - resolution.height) * 0.5f)
|
||||||
textureView.setTransform(it)
|
textureView.setTransform(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun adjustSurfaceViewAspect()
|
||||||
|
{
|
||||||
|
val videoProfile = viewModel.session.connectInfo.videoProfile
|
||||||
|
binding.aspectRatioLayout.aspectRatio = videoProfile.width.toFloat() / videoProfile.height.toFloat()
|
||||||
|
binding.aspectRatioLayout.mode = TransformMode.fromButton(binding.displayModeToggle.checkedButtonId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun adjustStreamViewAspect() = adjustSurfaceViewAspect()
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent) = viewModel.input.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
|
override fun dispatchKeyEvent(event: KeyEvent) = viewModel.input.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
|
||||||
override fun onGenericMotionEvent(event: MotionEvent) = viewModel.input.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
|
override fun onGenericMotionEvent(event: MotionEvent) = viewModel.input.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TransformMode
|
||||||
class DisplayInfo constructor(val videoProfile: ConnectVideoProfile, val textureView: TextureView)
|
|
||||||
{
|
{
|
||||||
val contentWidth : Float get() = videoProfile.width.toFloat()
|
FIT,
|
||||||
val contentHeight : Float get() = videoProfile.height.toFloat()
|
STRETCH,
|
||||||
|
ZOOM;
|
||||||
|
|
||||||
|
companion object
|
||||||
|
{
|
||||||
|
fun fromButton(displayModeButtonId: Int)
|
||||||
|
= when (displayModeButtonId)
|
||||||
|
{
|
||||||
|
R.id.display_mode_stretch_button -> STRETCH
|
||||||
|
R.id.display_mode_zoom_button -> ZOOM
|
||||||
|
else -> FIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextureViewTransform(private val videoProfile: ConnectVideoProfile, private val textureView: TextureView)
|
||||||
|
{
|
||||||
|
private val contentWidth : Float get() = videoProfile.width.toFloat()
|
||||||
|
private val contentHeight : Float get() = videoProfile.height.toFloat()
|
||||||
val viewWidth : Float get() = textureView.width.toFloat()
|
val viewWidth : Float get() = textureView.width.toFloat()
|
||||||
val viewHeight : Float get() = textureView.height.toFloat()
|
val viewHeight : Float get() = textureView.height.toFloat()
|
||||||
val contentAspect : Float get() = contentHeight / contentWidth
|
private val contentAspect : Float get() = contentHeight / contentWidth
|
||||||
|
|
||||||
fun computeResolutionFor(displayModeButtonId: Int) : Resolution
|
fun resolutionFor(mode: TransformMode): Resolution
|
||||||
{
|
= when(mode)
|
||||||
when (displayModeButtonId)
|
|
||||||
{
|
{
|
||||||
R.id.display_mode_stretch_button -> return computeStrechedResolution()
|
TransformMode.STRETCH -> strechedResolution
|
||||||
R.id.display_mode_zoom_button -> return computeZoomedResolution()
|
TransformMode.ZOOM -> zoomedResolution
|
||||||
else -> return computeNormalResolution()
|
TransformMode.FIT -> normalResolution
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeStrechedResolution(): Resolution
|
private val strechedResolution get() = Resolution(viewWidth, viewHeight)
|
||||||
{
|
|
||||||
return Resolution(viewWidth, viewHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeZoomedResolution(): Resolution
|
private val zoomedResolution get() =
|
||||||
{
|
if(viewHeight > viewWidth * contentAspect)
|
||||||
if (viewHeight > viewWidth * contentAspect)
|
|
||||||
{
|
{
|
||||||
val zoomFactor = viewHeight / contentHeight
|
val zoomFactor = viewHeight / contentHeight
|
||||||
return Resolution(contentWidth * zoomFactor, viewHeight)
|
Resolution(contentWidth * zoomFactor, viewHeight)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val zoomFactor = viewWidth / contentWidth
|
val zoomFactor = viewWidth / contentWidth
|
||||||
return Resolution(viewWidth, contentHeight * zoomFactor)
|
Resolution(viewWidth, contentHeight * zoomFactor)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeNormalResolution(): Resolution
|
private val normalResolution get() =
|
||||||
{
|
if(viewHeight > viewWidth * contentAspect)
|
||||||
if (viewHeight > viewWidth * contentAspect)
|
Resolution(viewWidth, viewWidth * contentAspect)
|
||||||
{
|
|
||||||
return Resolution(viewWidth, viewWidth * contentAspect)
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
Resolution(viewHeight / contentAspect, viewHeight)
|
||||||
return Resolution(viewHeight / contentAspect, viewHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,23 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.stream
|
package com.metallic.chiaki.stream
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import android.app.Application
|
||||||
import androidx.lifecycle.MutableLiveData
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.*
|
||||||
import com.metallic.chiaki.common.LogManager
|
import com.metallic.chiaki.common.LogManager
|
||||||
import com.metallic.chiaki.session.StreamSession
|
import com.metallic.chiaki.session.StreamSession
|
||||||
import com.metallic.chiaki.common.Preferences
|
import com.metallic.chiaki.common.Preferences
|
||||||
import com.metallic.chiaki.lib.*
|
import com.metallic.chiaki.lib.*
|
||||||
import com.metallic.chiaki.session.StreamInput
|
import com.metallic.chiaki.session.StreamInput
|
||||||
|
|
||||||
class StreamViewModel(val preferences: Preferences, val logManager: LogManager, val connectInfo: ConnectInfo): ViewModel()
|
class StreamViewModel(val application: Application, val connectInfo: ConnectInfo): ViewModel()
|
||||||
{
|
{
|
||||||
|
val preferences = Preferences(application)
|
||||||
|
val logManager = LogManager(application)
|
||||||
|
|
||||||
private var _session: StreamSession? = null
|
private var _session: StreamSession? = null
|
||||||
val input = StreamInput(preferences)
|
val input = StreamInput(application, preferences)
|
||||||
val session = StreamSession(connectInfo, logManager, preferences.logVerbose, input)
|
val session = StreamSession(connectInfo, logManager, preferences.logVerbose, input)
|
||||||
|
|
||||||
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)
|
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.metallic.chiaki.common.Preferences
|
||||||
|
|
||||||
|
class ButtonHaptics(val context: Context)
|
||||||
|
{
|
||||||
|
private val enabled = Preferences(context).buttonHapticEnabled
|
||||||
|
|
||||||
|
fun trigger(harder: Boolean = false)
|
||||||
|
{
|
||||||
|
if(!enabled)
|
||||||
|
return
|
||||||
|
val vibrator = context.getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
vibrator.vibrate(
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
|
VibrationEffect.createPredefined(if(harder) VibrationEffect.EFFECT_CLICK else VibrationEffect.EFFECT_TICK)
|
||||||
|
else
|
||||||
|
VibrationEffect.createOneShot(10, if(harder) 200 else 100)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
vibrator.vibrate(10)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
@ -21,14 +6,19 @@ import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.children
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
|
|
||||||
class ButtonView @JvmOverloads constructor(
|
class ButtonView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : View(context, attrs, defStyleAttr)
|
) : View(context, attrs, defStyleAttr)
|
||||||
{
|
{
|
||||||
|
private val haptics = ButtonHaptics(context)
|
||||||
|
|
||||||
var buttonPressed = false
|
var buttonPressed = false
|
||||||
private set(value)
|
private set(value)
|
||||||
{
|
{
|
||||||
|
@ -36,6 +26,8 @@ class ButtonView @JvmOverloads constructor(
|
||||||
field = value
|
field = value
|
||||||
if(diff)
|
if(diff)
|
||||||
{
|
{
|
||||||
|
if(value)
|
||||||
|
haptics.trigger()
|
||||||
invalidate()
|
invalidate()
|
||||||
buttonPressedCallback?.let { it(field) }
|
buttonPressedCallback?.let { it(field) }
|
||||||
}
|
}
|
||||||
|
@ -61,16 +53,39 @@ class ButtonView @JvmOverloads constructor(
|
||||||
{
|
{
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
val drawable = if(buttonPressed) drawablePressed else drawableIdle
|
val drawable = if(buttonPressed) drawablePressed else drawableIdle
|
||||||
drawable?.setBounds(0, 0, width, height)
|
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||||
drawable?.draw(canvas)
|
drawable?.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this button overlaps with others in the same layout,
|
||||||
|
* let the one whose center is closest to the touch handle it.
|
||||||
|
*/
|
||||||
|
private fun bestFittingTouchView(x: Float, y: Float): View
|
||||||
|
{
|
||||||
|
val loc = locationOnScreen + Vector(x, y)
|
||||||
|
return (parent as? ViewGroup)?.children?.filter {
|
||||||
|
it is ButtonView
|
||||||
|
}?.filter {
|
||||||
|
val pos = it.locationOnScreen
|
||||||
|
loc.x >= pos.x && loc.x < pos.x + it.width && loc.y >= pos.y && loc.y < pos.y + it.height
|
||||||
|
}?.sortedBy {
|
||||||
|
(loc - (it.locationOnScreen + Vector(it.width.toFloat(), it.height.toFloat()) * 0.5f)).lengthSq
|
||||||
|
}?.firstOrNull() ?: this
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean
|
override fun onTouchEvent(event: MotionEvent): Boolean
|
||||||
{
|
{
|
||||||
when(event.action)
|
when(event.actionMasked)
|
||||||
{
|
{
|
||||||
MotionEvent.ACTION_DOWN -> buttonPressed = true
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
MotionEvent.ACTION_UP -> buttonPressed = false
|
if(bestFittingTouchView(event.getX(event.actionIndex), event.getY(event.actionIndex)) != this)
|
||||||
|
return false
|
||||||
|
buttonPressed = true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||||
|
buttonPressed = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
@ -31,6 +16,8 @@ class DPadView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : View(context, attrs, defStyleAttr)
|
) : View(context, attrs, defStyleAttr)
|
||||||
{
|
{
|
||||||
|
private val haptics = ButtonHaptics(context)
|
||||||
|
|
||||||
enum class Direction {
|
enum class Direction {
|
||||||
LEFT,
|
LEFT,
|
||||||
RIGHT,
|
RIGHT,
|
||||||
|
@ -86,7 +73,7 @@ class DPadView @JvmOverloads constructor(
|
||||||
else
|
else
|
||||||
drawable = dpadIdleDrawable
|
drawable = dpadIdleDrawable
|
||||||
|
|
||||||
drawable?.setBounds(0, 0, width, height)
|
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||||
//drawable?.alpha = 127
|
//drawable?.alpha = 127
|
||||||
drawable?.draw(canvas)
|
drawable?.draw(canvas)
|
||||||
}
|
}
|
||||||
|
@ -128,6 +115,8 @@ class DPadView @JvmOverloads constructor(
|
||||||
|
|
||||||
if(state != newState)
|
if(state != newState)
|
||||||
{
|
{
|
||||||
|
if(newState != null)
|
||||||
|
haptics.trigger()
|
||||||
state = newState
|
state = newState
|
||||||
invalidate()
|
invalidate()
|
||||||
stateChangeCallback?.let { it(state) }
|
stateChangeCallback?.let { it(state) }
|
||||||
|
|
|
@ -1,91 +1,99 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.databinding.FragmentControlsBinding
|
||||||
import com.metallic.chiaki.lib.ControllerState
|
import com.metallic.chiaki.lib.ControllerState
|
||||||
import kotlinx.android.synthetic.main.fragment_controls.*
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.rxkotlin.Observables.combineLatest
|
||||||
|
import io.reactivex.subjects.BehaviorSubject
|
||||||
|
import io.reactivex.subjects.Subject
|
||||||
|
|
||||||
class TouchControlsFragment : Fragment()
|
abstract class TouchControlsFragment : Fragment()
|
||||||
{
|
{
|
||||||
private var controllerState = ControllerState()
|
protected var ownControllerState = ControllerState()
|
||||||
private set(value)
|
set(value)
|
||||||
{
|
{
|
||||||
val diff = field != value
|
val diff = field != value
|
||||||
field = value
|
field = value
|
||||||
if(diff)
|
if(diff)
|
||||||
controllerStateCallback?.let { it(value) }
|
ownControllerStateSubject.onNext(ownControllerState)
|
||||||
}
|
}
|
||||||
|
|
||||||
var controllerStateCallback: ((ControllerState) -> Unit)? = null
|
protected val ownControllerStateSubject: Subject<ControllerState>
|
||||||
var onScreenControlsEnabled: LiveData<Boolean>? = null
|
= BehaviorSubject.create<ControllerState>().also { it.onNext(ownControllerState) }
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
// to delay attaching to the touchpadView until it's available
|
||||||
= inflater.inflate(R.layout.fragment_controls, container, false)
|
protected val controllerStateProxy: Subject<Observable<ControllerState>>
|
||||||
|
= BehaviorSubject.create<Observable<ControllerState>>().also { it.onNext(ownControllerStateSubject) }
|
||||||
|
val controllerState: Observable<ControllerState> get() =
|
||||||
|
controllerStateProxy.flatMap { it }
|
||||||
|
|
||||||
|
var onScreenControlsEnabled: LiveData<Boolean>? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultTouchControlsFragment : TouchControlsFragment()
|
||||||
|
{
|
||||||
|
private var _binding: FragmentControlsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
|
FragmentControlsBinding.inflate(inflater, container, false).let {
|
||||||
|
_binding = it
|
||||||
|
controllerStateProxy.onNext(
|
||||||
|
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
|
||||||
|
)
|
||||||
|
it.root
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
dpadView.stateChangeCallback = this::dpadStateChanged
|
binding.dpadView.stateChangeCallback = this::dpadStateChanged
|
||||||
crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
|
binding.crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
|
||||||
moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
|
binding.moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
|
||||||
pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
|
binding.pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
|
||||||
boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
|
binding.boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
|
||||||
l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
|
binding.l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
|
||||||
r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
|
binding.r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
|
||||||
optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
|
binding.l3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L3)
|
||||||
shareButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_SHARE)
|
binding.r3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R3)
|
||||||
psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
|
binding.optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
|
||||||
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
binding.shareButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_SHARE)
|
||||||
|
binding.psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
|
||||||
|
|
||||||
l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
binding.l2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
||||||
r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
binding.r2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
||||||
|
|
||||||
val quantizeStick = { f: Float ->
|
val quantizeStick = { f: Float ->
|
||||||
(Short.MAX_VALUE * f).toShort()
|
(Short.MAX_VALUE * f).toInt().toShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
leftAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
binding.leftAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
|
||||||
leftX = quantizeStick(it.x)
|
leftX = quantizeStick(it.x)
|
||||||
leftY = quantizeStick(it.y)
|
leftY = quantizeStick(it.y)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
binding.rightAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
|
||||||
rightX = quantizeStick(it.x)
|
rightX = quantizeStick(it.x)
|
||||||
rightY = quantizeStick(it.y)
|
rightY = quantizeStick(it.y)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
onScreenControlsEnabled?.observe(this, Observer {
|
onScreenControlsEnabled?.observe(viewLifecycleOwner, Observer {
|
||||||
view.visibility = if(it) View.VISIBLE else View.GONE
|
view.visibility = if(it) View.VISIBLE else View.GONE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun dpadStateChanged(direction: DPadView.Direction?)
|
private fun dpadStateChanged(direction: DPadView.Direction?)
|
||||||
{
|
{
|
||||||
controllerState = controllerState.copy().apply {
|
ownControllerState = ownControllerState.copy().apply {
|
||||||
buttons = ((buttons
|
buttons = ((buttons
|
||||||
and ControllerState.BUTTON_DPAD_LEFT.inv()
|
and ControllerState.BUTTON_DPAD_LEFT.inv()
|
||||||
and ControllerState.BUTTON_DPAD_RIGHT.inv()
|
and ControllerState.BUTTON_DPAD_RIGHT.inv()
|
||||||
|
@ -107,7 +115,7 @@ class TouchControlsFragment : Fragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
||||||
controllerState = controllerState.copy().apply {
|
ownControllerState = ownControllerState.copy().apply {
|
||||||
buttons =
|
buttons =
|
||||||
if(pressed)
|
if(pressed)
|
||||||
buttons or buttonMask
|
buttons or buttonMask
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
@ -41,7 +26,7 @@ class TouchTracker
|
||||||
if(pointerId == null)
|
if(pointerId == null)
|
||||||
{
|
{
|
||||||
pointerId = event.getPointerId(event.actionIndex)
|
pointerId = event.getPointerId(event.actionIndex)
|
||||||
currentPosition = Vector(event.x, event.y)
|
currentPosition = Vector(event.getX(event.actionIndex), event.getY(event.actionIndex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
@ -21,49 +6,42 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.databinding.FragmentTouchpadOnlyBinding
|
||||||
import com.metallic.chiaki.lib.ControllerState
|
import io.reactivex.rxkotlin.Observables.combineLatest
|
||||||
import kotlinx.android.synthetic.main.fragment_controls.*
|
|
||||||
|
|
||||||
class TouchpadOnlyFragment : Fragment()
|
class TouchpadOnlyFragment : TouchControlsFragment()
|
||||||
{
|
{
|
||||||
private var controllerState = ControllerState()
|
|
||||||
private set(value)
|
|
||||||
{
|
|
||||||
val diff = field != value
|
|
||||||
field = value
|
|
||||||
if(diff)
|
|
||||||
controllerStateCallback?.let { it(value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
var controllerStateCallback: ((ControllerState) -> Unit)? = null
|
|
||||||
var touchpadOnlyEnabled: LiveData<Boolean>? = null
|
var touchpadOnlyEnabled: LiveData<Boolean>? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
private var _binding: FragmentTouchpadOnlyBinding? = null
|
||||||
= inflater.inflate(R.layout.fragment_touchpad_only, container, false)
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
|
FragmentTouchpadOnlyBinding.inflate(inflater, container, false).let {
|
||||||
|
_binding = it
|
||||||
|
controllerStateProxy.onNext(
|
||||||
|
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
|
||||||
|
)
|
||||||
|
it.root
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
touchpadOnlyEnabled?.observe(viewLifecycleOwner, Observer {
|
||||||
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
|
||||||
|
|
||||||
touchpadOnlyEnabled?.observe(this, Observer {
|
|
||||||
view.visibility = if(it) View.VISIBLE else View.GONE
|
view.visibility = if(it) View.VISIBLE else View.GONE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
||||||
controllerState = controllerState.copy().apply {
|
ownControllerState = ownControllerState.copy().apply {
|
||||||
buttons =
|
buttons =
|
||||||
if(pressed)
|
if(pressed)
|
||||||
buttons or buttonMask
|
buttons or buttonMask
|
||||||
else
|
else
|
||||||
buttons and buttonMask.inv()
|
buttons and buttonMask.inv()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
|
|
||||||
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import com.metallic.chiaki.R
|
||||||
|
import com.metallic.chiaki.lib.ControllerState
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.subjects.BehaviorSubject
|
||||||
|
import io.reactivex.subjects.Subject
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class TouchpadView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr)
|
||||||
|
{
|
||||||
|
companion object
|
||||||
|
{
|
||||||
|
private const val BUTTON_PRESS_MAX_MOVE_DIST_DP = 32.0f
|
||||||
|
private const val SHORT_BUTTON_PRESS_DURATION_MS = 200L
|
||||||
|
private const val BUTTON_HOLD_DELAY_MS = 500L
|
||||||
|
}
|
||||||
|
|
||||||
|
private val haptics = ButtonHaptics(context)
|
||||||
|
|
||||||
|
private val drawableIdle: Drawable?
|
||||||
|
private val drawablePressed: Drawable?
|
||||||
|
|
||||||
|
private val state: ControllerState = ControllerState()
|
||||||
|
|
||||||
|
inner class Touch(
|
||||||
|
val stateId: UByte,
|
||||||
|
private val startX: Float,
|
||||||
|
private val startY: Float)
|
||||||
|
{
|
||||||
|
var lifted = false // will be true but touch still in list when only relevant for short touch
|
||||||
|
private var maxDist: Float = 0.0f
|
||||||
|
val moveInsignificant: Boolean get() = maxDist < BUTTON_PRESS_MAX_MOVE_DIST_DP
|
||||||
|
|
||||||
|
fun onMove(x: Float, y: Float)
|
||||||
|
{
|
||||||
|
val d = (Vector(x, y) - Vector(startX, startY)).length / resources.displayMetrics.density
|
||||||
|
maxDist = max(d, maxDist)
|
||||||
|
}
|
||||||
|
|
||||||
|
val startButtonHoldRunnable = Runnable {
|
||||||
|
if(!moveInsignificant || buttonHeld)
|
||||||
|
return@Runnable
|
||||||
|
haptics.trigger(true)
|
||||||
|
state.buttons = state.buttons or ControllerState.BUTTON_TOUCHPAD
|
||||||
|
buttonHeld = true
|
||||||
|
triggerStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val pointerTouches = mutableMapOf<Int, Touch>()
|
||||||
|
|
||||||
|
private val stateSubject: Subject<ControllerState>
|
||||||
|
= BehaviorSubject.create<ControllerState>().also { it.onNext(state) }
|
||||||
|
val controllerState: Observable<ControllerState> get() = stateSubject
|
||||||
|
|
||||||
|
private var shortPressingTouches = listOf<Touch>()
|
||||||
|
private val shortButtonPressLiftRunnable = Runnable {
|
||||||
|
state.buttons = state.buttons and ControllerState.BUTTON_TOUCHPAD.inv()
|
||||||
|
shortPressingTouches.forEach {
|
||||||
|
state.stopTouch(it.stateId)
|
||||||
|
}
|
||||||
|
shortPressingTouches = listOf()
|
||||||
|
triggerStateChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var buttonHeld = false
|
||||||
|
|
||||||
|
init
|
||||||
|
{
|
||||||
|
context.theme.obtainStyledAttributes(attrs, R.styleable.TouchpadView, 0, 0).apply {
|
||||||
|
drawableIdle = getDrawable(R.styleable.TouchpadView_drawableIdle)
|
||||||
|
drawablePressed = getDrawable(R.styleable.TouchpadView_drawablePressed)
|
||||||
|
recycle()
|
||||||
|
}
|
||||||
|
isClickable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas)
|
||||||
|
if(pointerTouches.values.find { !it.lifted } == null)
|
||||||
|
return
|
||||||
|
val drawable = if(state.buttons and ControllerState.BUTTON_TOUCHPAD != 0U) drawablePressed else drawableIdle
|
||||||
|
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||||
|
drawable?.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun touchX(event: MotionEvent, index: Int): UShort =
|
||||||
|
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_WIDTH - 1u).toUShort(),
|
||||||
|
(ControllerState.TOUCHPAD_WIDTH.toFloat() * event.getX(index) / width.toFloat()).toUInt().toUShort()))
|
||||||
|
|
||||||
|
private fun touchY(event: MotionEvent, index: Int): UShort =
|
||||||
|
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_HEIGHT - 1u).toUShort(),
|
||||||
|
(ControllerState.TOUCHPAD_HEIGHT.toFloat() * event.getY(index) / height.toFloat()).toUInt().toUShort()))
|
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent): Boolean
|
||||||
|
{
|
||||||
|
when(event.actionMasked)
|
||||||
|
{
|
||||||
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
|
state.startTouch(touchX(event, event.actionIndex), touchY(event, event.actionIndex))?.let {
|
||||||
|
haptics.trigger()
|
||||||
|
val touch = Touch(it, event.getX(event.actionIndex), event.getY(event.actionIndex))
|
||||||
|
pointerTouches[event.getPointerId(event.actionIndex)] = touch
|
||||||
|
if(!buttonHeld)
|
||||||
|
postDelayed(touch.startButtonHoldRunnable, BUTTON_HOLD_DELAY_MS)
|
||||||
|
triggerStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||||
|
pointerTouches.remove(event.getPointerId(event.actionIndex))?.let {
|
||||||
|
removeCallbacks(it.startButtonHoldRunnable)
|
||||||
|
when
|
||||||
|
{
|
||||||
|
buttonHeld ->
|
||||||
|
{
|
||||||
|
buttonHeld = false
|
||||||
|
state.buttons = state.buttons and ControllerState.BUTTON_TOUCHPAD.inv()
|
||||||
|
state.stopTouch(it.stateId)
|
||||||
|
}
|
||||||
|
it.moveInsignificant -> triggerShortButtonPress(it)
|
||||||
|
else -> state.stopTouch(it.stateId)
|
||||||
|
}
|
||||||
|
triggerStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
val changed = pointerTouches.entries.fold(false) { acc, it ->
|
||||||
|
val index = event.findPointerIndex(it.key)
|
||||||
|
if(index < 0)
|
||||||
|
acc
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it.value.onMove(event.getX(event.actionIndex), event.getY(event.actionIndex))
|
||||||
|
acc || state.setTouchPos(it.value.stateId, touchX(event, index), touchY(event, index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(changed)
|
||||||
|
triggerStateChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun triggerShortButtonPress(touch: Touch)
|
||||||
|
{
|
||||||
|
shortPressingTouches = shortPressingTouches + listOf(touch)
|
||||||
|
removeCallbacks(shortButtonPressLiftRunnable)
|
||||||
|
state.buttons = state.buttons or ControllerState.BUTTON_TOUCHPAD
|
||||||
|
postDelayed(shortButtonPressLiftRunnable, SHORT_BUTTON_PRESS_DURATION_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun triggerStateChanged()
|
||||||
|
{
|
||||||
|
invalidate()
|
||||||
|
stateSubject.onNext(state)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,8 @@
|
||||||
/*
|
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||||
* This file is part of Chiaki.
|
|
||||||
*
|
|
||||||
* Chiaki is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Chiaki is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
data class Vector(val x: Float, val y: Float)
|
data class Vector(val x: Float, val y: Float)
|
||||||
|
@ -33,4 +19,10 @@ data class Vector(val x: Float, val y: Float)
|
||||||
val lengthSq get() = x*x + y*y
|
val lengthSq get() = x*x + y*y
|
||||||
val length get() = sqrt(lengthSq)
|
val length get() = sqrt(lengthSq)
|
||||||
val normalized get() = this / length
|
val normalized get() = this / length
|
||||||
|
}
|
||||||
|
|
||||||
|
val View.locationOnScreen: Vector get() {
|
||||||
|
val v = intArrayOf(0, 0)
|
||||||
|
this.getLocationOnScreen(v)
|
||||||
|
return Vector(v[0].toFloat(), v[1].toFloat())
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299ZM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474z"
|
android:pathData="M39.2255,0 L0,39.2255 20.3838,59.6093a27.7366,27.7366 88.4859,0 0,39.2255 0,27.7366 27.7366,88.4859 0,0 0,-39.2255zM25.4129,29.4287l2.7735,0l0,18.1643l9.9818,0l0,2.3342L25.4129,49.9272ZM46.3098,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8076,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_primary"
|
android:fillColor="@color/control_primary"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299ZM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474z"
|
android:pathData="M39.2255,0 L0,39.2255 20.3838,59.6093a27.7366,27.7366 88.4859,0 0,39.2255 0,27.7366 27.7366,88.4859 0,0 0,-39.2255zM25.4129,29.4287l2.7735,0l0,18.1643l9.9818,0l0,2.3342L25.4129,49.9272ZM46.3098,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8076,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_pressed"
|
android:fillColor="@color/control_pressed"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321 0.9062,-0.6682 2.1373,-1.002 3.6933,-1.002zM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299Z"
|
android:pathData="m27.6526,0a27.7366,27.7366 88.4859,0 0,-19.5285 8.1241,27.7366 27.7366,88.4859 0,0 0,39.2255L28.5078,67.7333 67.7333,28.5078 47.3496,8.1241A27.7366,27.7366 88.4859,0 0,27.6526 0ZM34.9017,15.1846c2.1235,0 3.8172,0.5309 5.0803,1.5927 1.2631,1.0618 1.8945,2.4804 1.8945,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9563,1.034 -2.1828,2.3063 -1.2265,1.2631 -2.9565,3.0342 -5.1899,5.3134l9.6795,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.3846,-1.9769 -1.1534,-2.6634 -0.7597,-0.6865 -1.7526,-1.0299 -2.9791,-1.0299 -0.8695,-0 -1.7898,0.1511 -2.76,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L29.1078,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM14.0053,15.5551l2.7735,0l0,18.1643l9.9813,0l0,2.3342L14.0053,36.0536Z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_primary"
|
android:fillColor="@color/control_primary"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321 0.9062,-0.6682 2.1373,-1.002 3.6933,-1.002zM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299Z"
|
android:pathData="m27.6526,0a27.7366,27.7366 88.4859,0 0,-19.5285 8.1241,27.7366 27.7366,88.4859 0,0 0,39.2255L28.5078,67.7333 67.7333,28.5078 47.3496,8.1241A27.7366,27.7366 88.4859,0 0,27.6526 0ZM34.9017,15.1846c2.1235,0 3.8172,0.5309 5.0803,1.5927 1.2631,1.0618 1.8945,2.4804 1.8945,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9563,1.034 -2.1828,2.3063 -1.2265,1.2631 -2.9565,3.0342 -5.1899,5.3134l9.6795,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.3846,-1.9769 -1.1534,-2.6634 -0.7597,-0.6865 -1.7526,-1.0299 -2.9791,-1.0299 -0.8695,-0 -1.7898,0.1511 -2.76,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L29.1078,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM14.0053,15.5551l2.7735,0l0,18.1643l9.9813,0l0,2.3342L14.0053,36.0536Z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_pressed"
|
android:fillColor="@color/control_pressed"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
12
android/app/src/main/res/drawable/control_button_l3.xml
Normal file
12
android/app/src/main/res/drawable/control_button_l3.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="256dp"
|
||||||
|
android:height="256dp"
|
||||||
|
android:viewportWidth="67.73333"
|
||||||
|
android:viewportHeight="67.73334">
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0 L-41.939,102.093 67.7333,67.7333C67.7333,30.3253 37.4081,0 0,0ZM33.6093,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8397,2.2423 1.8397,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5739,1.318 -2.773,1.6201 1.3272,0.2837 2.3613,0.8739 3.1027,1.771 0.7506,0.897 1.126,2.0047 1.126,3.3228 0,2.0228 -0.6959,3.5878 -2.0872,4.6953 -1.3913,1.1075 -3.368,1.6614 -5.9309,1.6614 -0.8604,0 -1.7486,-0.0871 -2.6639,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0621,-0.3434 3.9682,-1.0299 0.9153,-0.6865 1.3725,-1.6837 1.3725,-2.9926 0,-1.2082 -0.4252,-2.1514 -1.2764,-2.8288 -0.8421,-0.6865 -2.0187,-1.0294 -3.529,-1.0294l-2.3885,0l0,-2.2794l2.4986,0c1.3638,0 2.4075,-0.2697 3.1306,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.3755,-1.858 -1.126,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.037,-0.5124 2.9523,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM12.3832,31.6957l2.7735,0l0,18.1648l9.9813,0l0,2.3337L12.3832,52.1942Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="11.0688"
|
||||||
|
android:fillColor="@color/control_primary"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="256dp"
|
||||||
|
android:height="256dp"
|
||||||
|
android:viewportWidth="67.73333"
|
||||||
|
android:viewportHeight="67.73334">
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0 L-41.939,102.093 67.7333,67.7333C67.7333,30.3253 37.4081,0 0,0ZM33.6093,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8397,2.2423 1.8397,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5739,1.318 -2.773,1.6201 1.3272,0.2837 2.3613,0.8739 3.1027,1.771 0.7506,0.897 1.126,2.0047 1.126,3.3228 0,2.0228 -0.6959,3.5878 -2.0872,4.6953 -1.3913,1.1075 -3.368,1.6614 -5.9309,1.6614 -0.8604,0 -1.7486,-0.0871 -2.6639,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0621,-0.3434 3.9682,-1.0299 0.9153,-0.6865 1.3725,-1.6837 1.3725,-2.9926 0,-1.2082 -0.4252,-2.1514 -1.2764,-2.8288 -0.8421,-0.6865 -2.0187,-1.0294 -3.529,-1.0294l-2.3885,0l0,-2.2794l2.4986,0c1.3638,0 2.4075,-0.2697 3.1306,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.3755,-1.858 -1.126,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.037,-0.5124 2.9523,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM12.3832,31.6957l2.7735,0l0,18.1648l9.9813,0l0,2.3337L12.3832,52.1942Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="11.0688"
|
||||||
|
android:fillColor="@color/control_pressed"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
|
android:pathData="M28.5078,0 L8.1241,20.3838a27.7366,27.7366 88.4859,0 0,0 39.2255,27.7366 27.7366,88.4859 0,0 39.2255,0L67.7333,39.2255ZM12.4178,29.4287l6.2606,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4573 1.7301,4.4344 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.442 -2.6086,1.7715 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187L28.3853,49.9272L25.4062,49.9272l-2.6226,-5.2586C22.1063,43.2956 21.4472,42.385 20.8065,41.9365c-0.6316,-0.4485 -1.4963,-0.6728 -2.5947,-0.6728L15.1913,41.2636l0,8.6636L12.4178,49.9272ZM37.1864,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8075,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259zM15.1913,31.7076l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_primary"
|
android:fillColor="@color/control_primary"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
|
android:pathData="M28.5078,0 L8.1241,20.3838a27.7366,27.7366 88.4859,0 0,0 39.2255,27.7366 27.7366,88.4859 0,0 39.2255,0L67.7333,39.2255ZM12.4178,29.4287l6.2606,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4573 1.7301,4.4344 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.442 -2.6086,1.7715 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187L28.3853,49.9272L25.4062,49.9272l-2.6226,-5.2586C22.1063,43.2956 21.4472,42.385 20.8065,41.9365c-0.6316,-0.4485 -1.4963,-0.6728 -2.5947,-0.6728L15.1913,41.2636l0,8.6636L12.4178,49.9272ZM37.1864,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8075,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259zM15.1913,31.7076l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_pressed"
|
android:fillColor="@color/control_pressed"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321C37.8899,8.3705 39.1211,8.0367 40.6771,8.0367ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
|
android:pathData="M20.3838,8.1241 L0,28.5078 39.2255,67.7333 59.6093,47.3496a27.7366,27.7366 88.4859,0 0,0 -39.2255,27.7366 27.7366,88.4859 0,0 -39.2255,0zM48.9278,15.1846c2.1235,0 3.8167,0.5309 5.0798,1.5927 1.2631,1.0618 1.895,2.4804 1.895,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9568,1.034 -2.1833,2.3063 -1.2265,1.2631 -2.956,3.0342 -5.1893,5.3134l9.679,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.384,-1.9769 -1.1529,-2.6634 -0.7597,-0.6865 -1.7531,-1.0299 -2.9797,-1.0299 -0.8695,-0 -1.7893,0.1511 -2.7595,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L43.1338,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM24.1593,15.5551l6.2611,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7296,2.4578 1.7296,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187l2.8148,5.6017l-2.9797,0l-2.6221,-5.2586c-0.6773,-1.373 -1.3364,-2.2836 -1.9771,-2.7321 -0.6316,-0.4485 -1.4968,-0.6728 -2.5952,-0.6728l-3.0205,0l0,8.6636L24.1593,36.0536ZM26.9327,17.8346l0,7.2766l3.4876,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0294,-1.5331 1.0294,-2.7321 0,-1.1991 -0.3429,-2.1006 -1.0294,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_primary"
|
android:fillColor="@color/control_primary"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="256dp"
|
android:width="256dp"
|
||||||
android:height="128dp"
|
android:height="256dp"
|
||||||
android:viewportWidth="67.73333"
|
android:viewportWidth="67.73333"
|
||||||
android:viewportHeight="33.86667">
|
android:viewportHeight="67.73334">
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321C37.8899,8.3705 39.1211,8.0367 40.6771,8.0367ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
|
android:pathData="M20.3838,8.1241 L0,28.5078 39.2255,67.7333 59.6093,47.3496a27.7366,27.7366 88.4859,0 0,0 -39.2255,27.7366 27.7366,88.4859 0,0 -39.2255,0zM48.9278,15.1846c2.1235,0 3.8167,0.5309 5.0798,1.5927 1.2631,1.0618 1.895,2.4804 1.895,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9568,1.034 -2.1833,2.3063 -1.2265,1.2631 -2.956,3.0342 -5.1893,5.3134l9.679,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.384,-1.9769 -1.1529,-2.6634 -0.7597,-0.6865 -1.7531,-1.0299 -2.9797,-1.0299 -0.8695,-0 -1.7893,0.1511 -2.7595,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L43.1338,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM24.1593,15.5551l6.2611,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7296,2.4578 1.7296,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187l2.8148,5.6017l-2.9797,0l-2.6221,-5.2586c-0.6773,-1.373 -1.3364,-2.2836 -1.9771,-2.7321 -0.6316,-0.4485 -1.4968,-0.6728 -2.5952,-0.6728l-3.0205,0l0,8.6636L24.1593,36.0536ZM26.9327,17.8346l0,7.2766l3.4876,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0294,-1.5331 1.0294,-2.7321 0,-1.1991 -0.3429,-2.1006 -1.0294,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
android:strokeLineJoin="round"
|
android:strokeLineJoin="round"
|
||||||
android:strokeWidth="23.7399"
|
android:strokeWidth="5.54109"
|
||||||
android:fillColor="@color/control_pressed"
|
android:fillColor="@color/control_pressed"
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeLineCap="round"/>
|
android:strokeLineCap="round"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
12
android/app/src/main/res/drawable/control_button_r3.xml
Normal file
12
android/app/src/main/res/drawable/control_button_r3.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="256dp"
|
||||||
|
android:height="256dp"
|
||||||
|
android:viewportWidth="67.73333"
|
||||||
|
android:viewportHeight="67.73334">
|
||||||
|
<path
|
||||||
|
android:pathData="M67.7333,0C30.3252,0 0,30.3252 0,67.7333l109.6724,34.3597zM50.2982,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8402,2.2423 1.8402,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5744,1.318 -2.7735,1.6201 1.3272,0.2837 2.3618,0.8739 3.1032,1.771 0.7506,0.897 1.1255,2.0047 1.1255,3.3228 0,2.0228 -0.6954,3.5878 -2.0867,4.6953 -1.3913,1.1075 -3.3685,1.6614 -5.9314,1.6614 -0.8604,0 -1.7481,-0.0871 -2.6634,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0616,-0.3434 3.9677,-1.0299 0.9153,-0.6865 1.373,-1.6837 1.373,-2.9926 0,-1.2082 -0.4257,-2.1514 -1.2769,-2.8288 -0.8421,-0.6865 -2.0182,-1.0294 -3.5285,-1.0294l-2.389,0l0,-2.2794l2.4991,0c1.3638,0 2.4069,-0.2697 3.13,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.375,-1.858 -1.1255,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.0364,-0.5124 2.9518,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM25.2005,31.6957L31.4611,31.6957c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4578 1.7301,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6319 1.7296,1.2909 0.5675,0.659 1.1352,1.5649 1.7027,2.7182l2.8143,5.6017l-2.9791,0l-2.6226,-5.2581c-0.6773,-1.373 -1.3364,-2.2841 -1.9771,-2.7326 -0.6316,-0.4485 -1.4963,-0.6723 -2.5947,-0.6723l-3.0205,0l0,8.663L25.2005,52.1942ZM27.974,33.9752l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="11.0688"
|
||||||
|
android:fillColor="@color/control_primary"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="256dp"
|
||||||
|
android:height="256dp"
|
||||||
|
android:viewportWidth="67.73333"
|
||||||
|
android:viewportHeight="67.73334">
|
||||||
|
<path
|
||||||
|
android:pathData="M67.7333,0C30.3252,0 0,30.3252 0,67.7333l109.6724,34.3597zM50.2982,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8402,2.2423 1.8402,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5744,1.318 -2.7735,1.6201 1.3272,0.2837 2.3618,0.8739 3.1032,1.771 0.7506,0.897 1.1255,2.0047 1.1255,3.3228 0,2.0228 -0.6954,3.5878 -2.0867,4.6953 -1.3913,1.1075 -3.3685,1.6614 -5.9314,1.6614 -0.8604,0 -1.7481,-0.0871 -2.6634,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0616,-0.3434 3.9677,-1.0299 0.9153,-0.6865 1.373,-1.6837 1.373,-2.9926 0,-1.2082 -0.4257,-2.1514 -1.2769,-2.8288 -0.8421,-0.6865 -2.0182,-1.0294 -3.5285,-1.0294l-2.389,0l0,-2.2794l2.4991,0c1.3638,0 2.4069,-0.2697 3.13,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.375,-1.858 -1.1255,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.0364,-0.5124 2.9518,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM25.2005,31.6957L31.4611,31.6957c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4578 1.7301,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6319 1.7296,1.2909 0.5675,0.659 1.1352,1.5649 1.7027,2.7182l2.8143,5.6017l-2.9791,0l-2.6226,-5.2581c-0.6773,-1.373 -1.3364,-2.2841 -1.9771,-2.7326 -0.6316,-0.4485 -1.4963,-0.6723 -2.5947,-0.6723l-3.0205,0l0,8.663L25.2005,52.1942ZM27.974,33.9752l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="11.0688"
|
||||||
|
android:fillColor="@color/control_pressed"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -1,12 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="256dp"
|
|
||||||
android:height="256dp"
|
|
||||||
android:viewportWidth="67.73333"
|
|
||||||
android:viewportHeight="67.73334">
|
|
||||||
<path
|
|
||||||
android:pathData="M16.9333,4.2333C7.5523,4.2333 0,10.8416 0,19.05l0,29.6333c0,8.2084 7.5523,14.8167 16.9333,14.8167l33.8667,0c9.3811,0 16.9333,-6.6082 16.9333,-14.8167L67.7333,19.05C67.7333,10.8416 60.1811,4.2333 50.8,4.2333ZM11.8701,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042A3.7042,3.7042 0,0 1,22.9826 19.05,3.7042 3.7042,0 0,1 26.6867,15.3458ZM41.5034,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042z"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="22.6959"
|
|
||||||
android:fillColor="@color/control_primary"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
</vector>
|
|
|
@ -1,12 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="256dp"
|
|
||||||
android:height="256dp"
|
|
||||||
android:viewportWidth="67.73333"
|
|
||||||
android:viewportHeight="67.73334">
|
|
||||||
<path
|
|
||||||
android:pathData="M16.9333,4.2333C7.5523,4.2333 0,10.8416 0,19.05l0,29.6333c0,8.2084 7.5523,14.8167 16.9333,14.8167l33.8667,0c9.3811,0 16.9333,-6.6082 16.9333,-14.8167L67.7333,19.05C67.7333,10.8416 60.1811,4.2333 50.8,4.2333ZM11.8701,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042A3.7042,3.7042 0,0 1,22.9826 19.05,3.7042 3.7042,0 0,1 26.6867,15.3458ZM41.5034,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042z"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="22.6959"
|
|
||||||
android:fillColor="@color/control_pressed"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
</vector>
|
|
12
android/app/src/main/res/drawable/control_touchpad.xml
Normal file
12
android/app/src/main/res/drawable/control_touchpad.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="1920dp"
|
||||||
|
android:height="942dp"
|
||||||
|
android:viewportWidth="508"
|
||||||
|
android:viewportHeight="249.2375">
|
||||||
|
<path
|
||||||
|
android:pathData="M32,0L476,0A32,32 0,0 1,508 32L508,217.238A32,32 0,0 1,476 249.238L32,249.238A32,32 0,0 1,-0 217.238L-0,32A32,32 0,0 1,32 0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.59637"
|
||||||
|
android:fillColor="@color/control_primary"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="1920dp"
|
||||||
|
android:height="942dp"
|
||||||
|
android:viewportWidth="508"
|
||||||
|
android:viewportHeight="249.2375">
|
||||||
|
<path
|
||||||
|
android:pathData="M32,0L476,0A32,32 0,0 1,508 32L508,217.238A32,32 0,0 1,476 249.238L32,249.238A32,32 0,0 1,-0 217.238L-0,32A32,32 0,0 1,32 0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.59637"
|
||||||
|
android:fillColor="@color/control_pressed"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
9
android/app/src/main/res/drawable/ic_button_haptic.xml
Normal file
9
android/app/src/main/res/drawable/ic_button_haptic.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z"/>
|
||||||
|
</vector>
|
15
android/app/src/main/res/drawable/ic_codec.xml
Normal file
15
android/app/src/main/res/drawable/ic_codec.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M3,6h18v5h2V6c0,-1.1 -0.9,-2 -2,-2H3C1.9,4 1,4.9 1,6v12c0,1.1 0.9,2 2,2h9v-2H3V6z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M15,12l-6,-4l0,8z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"
|
||||||
|
android:pathData="M22.71,18.43c0.03,-0.29 0.04,-0.58 0.01,-0.86l1.07,-0.85c0.1,-0.08 0.12,-0.21 0.06,-0.32l-1.03,-1.79c-0.06,-0.11 -0.19,-0.15 -0.31,-0.11L21.23,15c-0.23,-0.17 -0.48,-0.31 -0.75,-0.42l-0.2,-1.36C20.26,13.09 20.16,13 20.03,13h-2.07c-0.12,0 -0.23,0.09 -0.25,0.21l-0.2,1.36c-0.26,0.11 -0.51,0.26 -0.74,0.42l-1.28,-0.5c-0.12,-0.05 -0.25,0 -0.31,0.11l-1.03,1.79c-0.06,0.11 -0.04,0.24 0.06,0.32l1.07,0.86c-0.03,0.29 -0.04,0.58 -0.01,0.86l-1.07,0.85c-0.1,0.08 -0.12,0.21 -0.06,0.32l1.03,1.79c0.06,0.11 0.19,0.15 0.31,0.11l1.27,-0.5c0.23,0.17 0.48,0.31 0.75,0.42l0.2,1.36c0.02,0.12 0.12,0.21 0.25,0.21h2.07c0.12,0 0.23,-0.09 0.25,-0.21l0.2,-1.36c0.26,-0.11 0.51,-0.26 0.74,-0.42l1.28,0.5c0.12,0.05 0.25,0 0.31,-0.11l1.03,-1.79c0.06,-0.11 0.04,-0.24 -0.06,-0.32L22.71,18.43zM19,19.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5S19.83,19.5 19,19.5z"/>
|
||||||
|
</vector>
|
9
android/app/src/main/res/drawable/ic_console_ps5.xml
Normal file
9
android/app/src/main/res/drawable/ic_console_ps5.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="512dp"
|
||||||
|
android:height="136dp"
|
||||||
|
android:viewportWidth="135.46666"
|
||||||
|
android:viewportHeight="35.983334">
|
||||||
|
<path
|
||||||
|
android:pathData="M135.059,0.004C105.38,2.679 69.761,6.719 7.637,2.238 2.368,1.858 -2.01,10.53 0.965,10.491 65.38,9.662 117.287,1.775 124.96,2.935c1.336,0.202 1.574,0.634 2.277,3.271 0.014,0.056 0.023,0.078 0.037,0.13C127.222,6.335 127.18,6.334 127.127,6.333 111.134,6.269 42.596,12.28 1.908,11.764V19.744C42.727,19.226 111.141,25.276 127.21,25.172c-0.021,0.076 -0.034,0.111 -0.056,0.195 -0.703,2.637 -0.941,3.069 -2.278,3.271C117.203,29.799 65.81,22.017 1.395,21.187 0.89,21.181 0.027,21.658 0.07,22.395 0.3,26.365 0.437,35.819 1.119,35.819c9.352,0.013 30.52,0.601 40.773,-0.532 18.706,-2.068 32.929,-4.728 44.502,-5.39 21.532,-1.232 33.907,0.053 48.583,1.672 0.933,0.103 0.031,-1.246 -0.448,-1.241 -1.987,0.023 -4.222,-2.731 -6.798,-4.992 -0.019,-0.016 -0.028,-0.082 -0.032,-0.174 1.321,-0.022 2.268,-0.087 2.695,-0.217V16.046,15.462 6.562c-0.424,-0.129 -1.327,-0.197 -2.607,-0.219 0.006,-0.053 0.013,-0.096 0.027,-0.107 2.576,-2.261 4.811,-5.015 6.797,-4.992 0.479,0.005 1.384,-1.325 0.448,-1.241z"
|
||||||
|
android:fillColor="?android:attr/textColorPrimary"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue