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:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- /^v\d.*$/
|
||||
- /^deploy-test(-.*)?$/
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
for:
|
||||
- matrix:
|
||||
only:
|
||||
- image: 'Visual Studio 2019'
|
||||
|
||||
build_script:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
- C:\msys64\usr\bin\bash -lc "cd \"%APPVEYOR_BUILD_FOLDER%\" && scripts/appveyor.sh"
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
artifacts:
|
||||
- path: Chiaki
|
||||
name: Chiaki
|
||||
- path: Chiaki-PDB
|
||||
name: Chiaki-PDB
|
||||
build_script:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
- C:\msys64\usr\bin\bash -lc "cd \"%APPVEYOR_BUILD_FOLDER%\" && scripts/appveyor-win.sh"
|
||||
|
||||
deploy:
|
||||
description: 'Chiaki Binaries'
|
||||
provider: GitHub
|
||||
draft: true
|
||||
auth_token:
|
||||
secure: Amvzm3PMM5nv+iFsqaU7TZ9fgyYt/YIrOLV0QMiCyOoUlLRIaiYxWiJ7maTpxhZ9
|
||||
artifact: "Chiaki"
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
artifacts:
|
||||
- path: Chiaki
|
||||
name: Chiaki
|
||||
- path: Chiaki-PDB
|
||||
name: Chiaki-PDB
|
||||
|
||||
- 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:
|
||||
- https://github.com/thestr4ng3r/chiaki
|
||||
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||
|
||||
packages:
|
||||
- cmake
|
||||
- protobuf
|
||||
- py37-protobuf
|
||||
- py311-setuptools # should not be needed with nanopb >= 0.4.9
|
||||
- py311-protobuf
|
||||
- opus
|
||||
- qt5-core
|
||||
- qt5-qmake
|
||||
- qt5-buildtools
|
||||
- qt5-gui
|
||||
- qt5-widgets
|
||||
- qt5-concurrent
|
||||
- qt5-svg
|
||||
- qt5-opengl
|
||||
- qt5-multimedia
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
image: openbsd/6.7
|
||||
image: openbsd/latest
|
||||
|
||||
sources:
|
||||
- https://github.com/thestr4ng3r/chiaki
|
||||
- https://git.sr.ht/~thestr4ng3r/chiaki
|
||||
|
||||
packages:
|
||||
- cmake
|
||||
- protobuf
|
||||
- py3-setuptools # should not be needed with nanopb >= 0.4.9
|
||||
- py3-protobuf
|
||||
- opus
|
||||
- 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
|
||||
compile_commands.json
|
||||
.ccls-cache
|
||||
.cache/
|
||||
.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
|
||||
[submodule "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"]
|
||||
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"]
|
||||
path = android/app/src/main/cpp/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_GUI "Enable Qt GUI" ON)
|
||||
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)
|
||||
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_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_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON)
|
||||
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_MINOR 3)
|
||||
set(CHIAKI_VERSION_MAJOR 2)
|
||||
set(CHIAKI_VERSION_MINOR 2)
|
||||
set(CHIAKI_VERSION_PATCH 0)
|
||||
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")
|
||||
include(CPack)
|
||||
|
||||
set(CHIAKI_IS_SWITCH ${NSWITCH})
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/setsu/cmake")
|
||||
|
||||
# configure nintendo switch toolchain
|
||||
if(CHIAKI_ENABLE_SWITCH AND CHIAKI_ENABLE_SWITCH_LINUX)
|
||||
# CHIAKI_ENABLE_SWITCH_LINUX is a special testing version
|
||||
# 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
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if(CHIAKI_IS_SWITCH)
|
||||
# force mbedtls as crypto lib
|
||||
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()
|
||||
|
||||
add_subdirectory(third-party)
|
||||
|
@ -62,6 +90,58 @@ endif()
|
|||
|
||||
if(CHIAKI_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()
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
@ -70,21 +150,30 @@ if(CHIAKI_ENABLE_CLI)
|
|||
add_subdirectory(cli)
|
||||
endif()
|
||||
|
||||
if(CHIAKI_ENABLE_GUI AND CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
|
||||
find_package(SDL2 MODULE REQUIRED)
|
||||
endif()
|
||||
|
||||
if(CHIAKI_ENABLE_SETSU)
|
||||
find_package(Udev QUIET)
|
||||
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()
|
||||
if(CHIAKI_ENABLE_SETSU STREQUAL AUTO AND SDL2_FOUND AND (SDL2_VERSION_MINOR GREATER 0 OR SDL2_VERSION_PATCH GREATER_EQUAL 14))
|
||||
message(STATUS "SDL version ${SDL2_VERSION} is >= 2.0.14, disabling Setsu")
|
||||
set(CHIAKI_ENABLE_SETSU OFF)
|
||||
endif()
|
||||
if(CHIAKI_ENABLE_SETSU)
|
||||
add_subdirectory(setsu)
|
||||
else()
|
||||
find_package(Udev QUIET)
|
||||
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()
|
||||
|
||||
|
@ -95,7 +184,6 @@ else()
|
|||
endif()
|
||||
|
||||
if(CHIAKI_ENABLE_GUI)
|
||||
#add_subdirectory(setsu)
|
||||
add_subdirectory(gui)
|
||||
endif()
|
||||
|
||||
|
@ -108,7 +196,6 @@ if(CHIAKI_ENABLE_ANDROID)
|
|||
add_subdirectory(android/app)
|
||||
endif()
|
||||
|
||||
if(CHIAKI_ENABLE_SWITCH)
|
||||
#TODO
|
||||
#add_subdirectory(switch)
|
||||
if(CHIAKI_ENABLE_BOREALIS)
|
||||
add_subdirectory(switch)
|
||||
endif()
|
||||
|
|
150
COPYING
150
COPYING
|
@ -1,5 +1,5 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
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
|
||||
|
@ -7,17 +7,15 @@
|
|||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
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,
|
||||
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
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
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.
|
||||
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
|
||||
|
@ -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
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
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.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
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.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
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.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
@ -72,7 +60,7 @@ modification follow.
|
|||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
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 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
|
||||
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 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
|
||||
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 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.
|
||||
|
||||
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
|
||||
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>
|
||||
|
||||
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
|
||||
(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 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/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
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".
|
||||
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 GPL, see
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
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.
|
||||
|
|
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.
|
||||
|
||||
[](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
|
||||
for Linux, FreeBSD, OpenBSD, Android, macOS, Windows and potentially even more platforms.
|
||||
Chiaki is a Free and Open Source Software Client for PlayStation 4 and PlayStation 5 Remote Play
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
* **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.
|
||||
* **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
|
||||
|
||||
|
@ -49,15 +49,15 @@ cmake ..
|
|||
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
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
|
@ -68,18 +68,18 @@ Simply run it in a terminal and follow the instructions. Once you know your ID,
|
|||
|
||||
### 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:
|
||||
Settings -> Remote Play (ensure this is ticked) -> Add Device
|
||||
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 -> 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
|
||||
|
||||
This project has only been made possible because of the following Open Source projects:
|
||||
[radare2](https://github.com/radare/radare2),
|
||||
[Cutter](https://cutter.re/),
|
||||
[Frida](https://www.frida.re/) and
|
||||
[x64dbg](https://x64dbg.com/).
|
||||
[Rizin](https://rizin.re),
|
||||
[Cutter](https://cutter.re),
|
||||
[Frida](https://www.frida.re) and
|
||||
[x64dbg](https://x64dbg.com).
|
||||
|
||||
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,
|
||||
|
@ -88,17 +88,27 @@ extremely helpful information about FEC and error correction.
|
|||
|
||||
## About
|
||||
|
||||
Created by Florian Märkl.
|
||||
Created by Florian Märkl
|
||||
|
||||
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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
it under the terms of the GNU Affero General Public License version 3
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
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 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/>.
|
||||
|
||||
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: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
def rootCMakeLists = "../../CMakeLists.txt"
|
||||
|
@ -18,13 +18,12 @@ def chiakiVersion = "$chiakiVersionMajor.$chiakiVersionMinor.$chiakiVersionPatch
|
|||
println("Determined Chiaki Version: $chiakiVersion")
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
compileSdkVersion 33
|
||||
defaultConfig {
|
||||
applicationId "com.metallic.chiaki"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 7
|
||||
targetSdkVersion 33
|
||||
versionCode 12
|
||||
versionName chiakiVersion
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
@ -33,11 +32,16 @@ android {
|
|||
"-DCHIAKI_ENABLE_GUI=OFF",
|
||||
"-DCHIAKI_ENABLE_SETSU=OFF",
|
||||
"-DCHIAKI_ENABLE_ANDROID=ON",
|
||||
"-DCHIAKI_ENABLE_FFMPEG_DECODER=OFF",
|
||||
"-DCHIAKI_ENABLE_PI_DECODER=OFF",
|
||||
"-DCHIAKI_LIB_ENABLE_OPUS=OFF",
|
||||
"-DCHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT=ON"
|
||||
}
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -47,7 +51,7 @@ android {
|
|||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version "3.10.2+"
|
||||
version "3.22.1"
|
||||
path rootCMakeLists
|
||||
}
|
||||
}
|
||||
|
@ -61,21 +65,14 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Properties properties = new Properties()
|
||||
def propertiesFile = file("../local.properties")
|
||||
if (propertiesFile.exists()) {
|
||||
properties.load(propertiesFile.newDataInputStream())
|
||||
}
|
||||
|
||||
def keystoreFile = file("../../keystore.jks")
|
||||
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")) {
|
||||
if(properties.containsKey("chiakiKeystore")) {
|
||||
println("Enabling Local Signing")
|
||||
buildTypes.release.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 {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-beta02'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.2.0'
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.12"
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.5.1'
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.20"
|
||||
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
|
||||
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"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
implementation "androidx.room:room-rxjava2:$room_version"
|
||||
implementation "com.squareup.moshi:moshi:1.9.2"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
|
||||
implementation "com.squareup.moshi:moshi:1.14.0"
|
||||
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.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -27,7 +29,8 @@
|
|||
</provider>
|
||||
|
||||
<activity android:name=".main.MainActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#include "audio-decoder.h"
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_AUDIO_DECODER_H
|
||||
#define CHIAKI_JNI_AUDIO_DECODER_H
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#include "audio-output.h"
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_AUDIO_OUTPUT_H
|
||||
#define CHIAKI_JNI_AUDIO_OUTPUT_H
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
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 };
|
||||
chiaki_connect_video_profile_preset(&profile, (ChiakiVideoResolutionPreset)resolution_preset, (ChiakiVideoFPSPreset)fps_preset);
|
||||
jclass profile_class = E->FindClass(env, BASE_PACKAGE"/ConnectVideoProfile");
|
||||
jmethodID profile_ctor = E->GetMethodID(env, profile_class, "<init>", "(IIII)V");
|
||||
return E->NewObject(env, profile_class, profile_ctor, profile.width, profile.height, profile.max_fps, profile.bitrate);
|
||||
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, codec);
|
||||
}
|
||||
|
||||
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_login_pin_request_meth;
|
||||
jmethodID java_session_event_quit_meth;
|
||||
jmethodID java_session_event_rumble_meth;
|
||||
jfieldID java_controller_state_buttons;
|
||||
jfieldID java_controller_state_l2_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_right_x;
|
||||
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;
|
||||
AndroidChiakiAudioDecoder audio_decoder;
|
||||
|
@ -193,6 +193,14 @@ static void android_chiaki_event_cb(ChiakiEvent *event, void *user)
|
|||
free(reason_str);
|
||||
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);
|
||||
|
@ -213,13 +221,16 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
|||
jclass result_class = E->GetObjectClass(env, result);
|
||||
|
||||
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;"));
|
||||
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"));
|
||||
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);
|
||||
|
||||
ChiakiConnectInfo connect_info;
|
||||
ChiakiConnectInfo connect_info = { 0 };
|
||||
connect_info.ps5 = ps5;
|
||||
|
||||
const char *str_borrow = E->GetStringUTFChars(env, host_string, NULL);
|
||||
connect_info.host = host_str = strdup(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.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);
|
||||
if(!session)
|
||||
{
|
||||
|
@ -262,7 +280,8 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
|
|||
}
|
||||
memset(session, 0, sizeof(AndroidChiakiSession));
|
||||
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)
|
||||
{
|
||||
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_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_rumble_meth = E->GetMethodID(env, session->java_session_class, "eventRumble", "(II)V");
|
||||
|
||||
jclass controller_state_class = E->FindClass(env, BASE_PACKAGE"/ControllerState");
|
||||
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_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_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_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)
|
||||
{
|
||||
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.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);
|
||||
|
@ -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.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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -589,11 +654,11 @@ JNIEXPORT void JNICALL JNI_FCN(discoveryServiceFree)(JNIEnv *env, jobject obj, j
|
|||
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;
|
||||
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);
|
||||
return r;
|
||||
}
|
||||
|
@ -607,6 +672,8 @@ typedef struct android_chiaki_regist_t
|
|||
jobject java_regist;
|
||||
jmethodID java_regist_event_meth;
|
||||
|
||||
jclass java_target_class;
|
||||
|
||||
jobject java_regist_event_canceled;
|
||||
jobject java_regist_event_failed;
|
||||
jclass java_regist_event_success_class;
|
||||
|
@ -616,6 +683,12 @@ typedef struct android_chiaki_regist_t
|
|||
jmethodID java_regist_host_ctor;
|
||||
} 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)
|
||||
{
|
||||
AndroidChiakiRegist *regist = user;
|
||||
|
@ -637,12 +710,13 @@ static void android_chiaki_regist_cb(ChiakiRegistEvent *event, void *user)
|
|||
{
|
||||
ChiakiRegisteredHost *host = event->registered_host;
|
||||
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_bssid),
|
||||
jnistr_from_ascii(env, host->ap_key),
|
||||
jnistr_from_ascii(env, host->ap_name),
|
||||
jnibytearray_create(env, host->ps4_mac, sizeof(host->ps4_mac)),
|
||||
jnistr_from_ascii(env, host->ps4_nickname),
|
||||
jnibytearray_create(env, host->server_mac, sizeof(host->server_mac)),
|
||||
jnistr_from_ascii(env, host->server_nickname),
|
||||
jnibytearray_create(env, (const uint8_t *)host->rp_regist_key, sizeof(host->rp_regist_key)),
|
||||
(jint)host->rp_key_type,
|
||||
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);
|
||||
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_failed);
|
||||
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_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_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"));
|
||||
|
@ -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_ctor = E->GetMethodID(env, regist->java_regist_host_class, "<init>", "("
|
||||
"L"BASE_PACKAGE"/Target;" // target: Target
|
||||
"Ljava/lang/String;" // apSsid: String
|
||||
"Ljava/lang/String;" // apBssid: String
|
||||
"Ljava/lang/String;" // apKey: String
|
||||
"Ljava/lang/String;" // apName: String
|
||||
"[B" // ps4Mac: ByteArray
|
||||
"Ljava/lang/String;" // ps4Nickname: String
|
||||
"[B" // serverMac: ByteArray
|
||||
"Ljava/lang/String;" // serverNickname: String
|
||||
"[B" // rpRegistKey: ByteArray
|
||||
"I" // rpKeyType: UInt
|
||||
"[B" // rpKey: ByteArray
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_H
|
||||
#define CHIAKI_JNI_H
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_CIRCULARBUF_HPP
|
||||
#define CHIAKI_JNI_CIRCULARBUF_HPP
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#include "log.h"
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_LOG_H
|
||||
#define CHIAKI_JNI_LOG_H
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0ab5b12a5bc3630a3d6c83b20eed2a669ebf7a24
|
||||
Subproject commit 8740d0fc321a55489dbbf6067298201b7d2e106d
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#include "video-decoder.h"
|
||||
|
||||
|
@ -29,36 +14,46 @@
|
|||
|
||||
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->codec = NULL;
|
||||
decoder->timestamp_cur = 0;
|
||||
decoder->target_width = target_width;
|
||||
decoder->target_height = target_height;
|
||||
decoder->target_codec = codec;
|
||||
decoder->shutdown_output = 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)
|
||||
{
|
||||
if(decoder->codec)
|
||||
{
|
||||
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);
|
||||
}
|
||||
kill_decoder(decoder);
|
||||
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);
|
||||
|
||||
if(!surface)
|
||||
{
|
||||
if(decoder->codec)
|
||||
{
|
||||
kill_decoder(decoder);
|
||||
CHIAKI_LOGI(decoder->log, "Decoder shut down after surface was removed");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(decoder->codec)
|
||||
{
|
||||
#if __ANDROID_API__ >= 23
|
||||
|
@ -82,7 +87,8 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
|
|||
|
||||
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);
|
||||
if(!decoder->codec)
|
||||
|
@ -196,6 +202,17 @@ static void *android_chiaki_video_decoder_output_thread_func(void *user)
|
|||
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");
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
#ifndef CHIAKI_JNI_VIDEO_DECODER_H
|
||||
#define CHIAKI_JNI_VIDEO_DECODER_H
|
||||
|
@ -34,11 +19,13 @@ typedef struct android_chiaki_video_decoder_t
|
|||
ANativeWindow *window;
|
||||
uint64_t timestamp_cur;
|
||||
ChiakiThread output_thread;
|
||||
bool shutdown_output;
|
||||
int32_t target_width;
|
||||
int32_t target_height;
|
||||
ChiakiCodec target_codec;
|
||||
} 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_set_surface(AndroidChiakiVideoDecoder *decoder, JNIEnv *env, jobject surface);
|
||||
bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, void *user);
|
||||
|
|
|
@ -1,27 +1,15 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.*
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.metallic.chiaki.lib.Target
|
||||
|
||||
@Database(
|
||||
version = 1,
|
||||
version = 2,
|
||||
entities = [RegisteredHost::class, ManualHost::class])
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase: RoomDatabase()
|
||||
|
@ -31,6 +19,18 @@ abstract class AppDatabase: RoomDatabase()
|
|||
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
|
||||
fun getDatabase(context: Context): AppDatabase
|
||||
{
|
||||
|
@ -40,7 +40,9 @@ fun getDatabase(context: Context): AppDatabase
|
|||
val db = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"chiaki").build()
|
||||
"chiaki")
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.build()
|
||||
database = db
|
||||
return db
|
||||
}
|
||||
|
@ -52,4 +54,10 @@ private class Converters
|
|||
|
||||
@TypeConverter
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
|
@ -25,6 +10,7 @@ sealed class DisplayHost
|
|||
abstract val host: String
|
||||
abstract val name: String?
|
||||
abstract val id: String?
|
||||
abstract val isPS5: Boolean
|
||||
|
||||
val isRegistered get() = registeredHost != null
|
||||
}
|
||||
|
@ -35,8 +21,9 @@ class DiscoveredDisplayHost(
|
|||
): DisplayHost()
|
||||
{
|
||||
override val host get() = discoveredHost.hostAddr ?: ""
|
||||
override val name get() = discoveredHost.hostName ?: registeredHost?.ps4Nickname
|
||||
override val id get() = discoveredHost.hostId ?: registeredHost?.ps4Mac?.toString()
|
||||
override val name get() = discoveredHost.hostName ?: registeredHost?.serverNickname
|
||||
override val id get() = discoveredHost.hostId ?: registeredHost?.serverMac?.toString()
|
||||
override val isPS5 get() = discoveredHost.isPS5
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
if(other !is DiscoveredDisplayHost)
|
||||
|
@ -55,8 +42,9 @@ class ManualDisplayHost(
|
|||
): DisplayHost()
|
||||
{
|
||||
override val host get() = manualHost.host
|
||||
override val name get() = registeredHost?.ps4Nickname
|
||||
override val id get() = registeredHost?.ps4Mac?.toString()
|
||||
override val name get() = registeredHost?.serverNickname
|
||||
override val id get() = registeredHost?.serverMac?.toString()
|
||||
override val isPS5: Boolean get() = registeredHost?.target?.isPS5 ?: false
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
if(other !is ManualDisplayHost)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
|
|
|
@ -1,24 +1,9 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.ForeignKey.SET_NULL
|
||||
import androidx.room.ForeignKey.Companion.SET_NULL
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Single
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
|
@ -22,6 +7,7 @@ import android.content.SharedPreferences
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.lib.Codec
|
||||
import com.metallic.chiaki.lib.ConnectVideoProfile
|
||||
import com.metallic.chiaki.lib.VideoFPSPreset
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
val resolutionDefault = Resolution.RES_720P
|
||||
val resolutionAll = Resolution.values()
|
||||
val fpsDefault = FPS.FPS_60
|
||||
val fpsAll = FPS.values()
|
||||
val codecDefault = Codec.CODEC_H265
|
||||
val codecAll = Codec.values()
|
||||
}
|
||||
|
||||
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -74,11 +68,26 @@ class Preferences(context: Context)
|
|||
get() = sharedPreferences.getBoolean(onScreenControlsEnabledKey, true)
|
||||
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
|
||||
get() = sharedPreferences.getBoolean(touchpadOnlyEnabledKey, false)
|
||||
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)
|
||||
var logVerbose
|
||||
get() = sharedPreferences.getBoolean(logVerboseKey, false)
|
||||
|
@ -112,12 +121,19 @@ class Preferences(context: Context)
|
|||
private val bitrateAutoSubject by lazy { BehaviorSubject.createDefault(bitrateAuto) }
|
||||
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 bitrate = bitrate
|
||||
if(bitrate == null)
|
||||
it
|
||||
else
|
||||
ConnectVideoProfile(it.width, it.height, it.maxFPS, bitrate)
|
||||
it.copy(bitrate = bitrate)
|
||||
}
|
||||
}
|
|
@ -1,25 +1,11 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
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.Target
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Maybe
|
||||
|
@ -28,24 +14,26 @@ import io.reactivex.Single
|
|||
@Entity(tableName = "registered_host")
|
||||
data class RegisteredHost(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo(name = "target") val target: Target,
|
||||
@ColumnInfo(name = "ap_ssid") val apSsid: String?,
|
||||
@ColumnInfo(name = "ap_bssid") val apBssid: String?,
|
||||
@ColumnInfo(name = "ap_key") val apKey: String?,
|
||||
@ColumnInfo(name = "ap_name") val apName: String?,
|
||||
@ColumnInfo(name = "ps4_mac") val ps4Mac: MacAddress,
|
||||
@ColumnInfo(name = "ps4_nickname") val ps4Nickname: String?,
|
||||
@ColumnInfo(name = "server_mac") val serverMac: MacAddress,
|
||||
@ColumnInfo(name = "server_nickname") val serverNickname: String?,
|
||||
@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", typeAffinity = BLOB) val rpKey: ByteArray // 0x10
|
||||
)
|
||||
{
|
||||
constructor(registHost: RegistHost) : this(
|
||||
target = registHost.target,
|
||||
apSsid = registHost.apSsid,
|
||||
apBssid = registHost.apBssid,
|
||||
apKey = registHost.apKey,
|
||||
apName = registHost.apName,
|
||||
ps4Mac = MacAddress(registHost.ps4Mac),
|
||||
ps4Nickname = registHost.ps4Nickname,
|
||||
serverMac = MacAddress(registHost.serverMac),
|
||||
serverNickname = registHost.serverNickname,
|
||||
rpRegistKey = registHost.rpRegistKey,
|
||||
rpKeyType = registHost.rpKeyType.toInt(),
|
||||
rpKey = registHost.rpKey
|
||||
|
@ -59,12 +47,13 @@ data class RegisteredHost(
|
|||
other as RegisteredHost
|
||||
|
||||
if(id != other.id) return false
|
||||
if(target != other.target) return false
|
||||
if(apSsid != other.apSsid) return false
|
||||
if(apBssid != other.apBssid) return false
|
||||
if(apKey != other.apKey) return false
|
||||
if(apName != other.apName) return false
|
||||
if(ps4Mac != other.ps4Mac) return false
|
||||
if(ps4Nickname != other.ps4Nickname) return false
|
||||
if(serverMac != other.serverMac) return false
|
||||
if(serverNickname != other.serverNickname) return false
|
||||
if(!rpRegistKey.contentEquals(other.rpRegistKey)) return false
|
||||
if(rpKeyType != other.rpKeyType) return false
|
||||
if(!rpKey.contentEquals(other.rpKey)) return false
|
||||
|
@ -75,12 +64,13 @@ data class RegisteredHost(
|
|||
override fun hashCode(): Int
|
||||
{
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + target.hashCode()
|
||||
result = 31 * result + (apSsid?.hashCode() ?: 0)
|
||||
result = 31 * result + (apBssid?.hashCode() ?: 0)
|
||||
result = 31 * result + (apKey?.hashCode() ?: 0)
|
||||
result = 31 * result + (apName?.hashCode() ?: 0)
|
||||
result = 31 * result + ps4Mac.hashCode()
|
||||
result = 31 * result + (ps4Nickname?.hashCode() ?: 0)
|
||||
result = 31 * result + serverMac.hashCode()
|
||||
result = 31 * result + (serverNickname?.hashCode() ?: 0)
|
||||
result = 31 * result + rpRegistKey.contentHashCode()
|
||||
result = 31 * result + rpKeyType
|
||||
result = 31 * result + rpKey.contentHashCode()
|
||||
|
@ -94,10 +84,10 @@ interface RegisteredHostDao
|
|||
@Query("SELECT * FROM registered_host")
|
||||
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>
|
||||
|
||||
@Query("DELETE FROM registered_host WHERE ps4_mac == :mac")
|
||||
@Query("DELETE FROM registered_host WHERE server_mac == :mac")
|
||||
fun deleteByMac(mac: MacAddress): Completable
|
||||
|
||||
@Delete
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common
|
||||
|
||||
|
@ -27,6 +12,7 @@ import androidx.core.content.FileProvider
|
|||
import androidx.room.*
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.lib.Target
|
||||
import com.squareup.moshi.*
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Flowable
|
||||
|
@ -39,28 +25,32 @@ import io.reactivex.rxkotlin.addTo
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import okio.Buffer
|
||||
import okio.Okio
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class SerializedRegisteredHost(
|
||||
@Json(name = "target") val target: Target,
|
||||
@Json(name = "ap_ssid") val apSsid: String?,
|
||||
@Json(name = "ap_bssid") val apBssid: String?,
|
||||
@Json(name = "ap_key") val apKey: String?,
|
||||
@Json(name = "ap_name") val apName: String?,
|
||||
@Json(name = "ps4_mac") val ps4Mac: MacAddress,
|
||||
@Json(name = "ps4_nickname") val ps4Nickname: String?,
|
||||
@Json(name = "server_mac") val serverMac: MacAddress,
|
||||
@Json(name = "server_nickname") val serverNickname: String?,
|
||||
@Json(name = "rp_regist_key") val rpRegistKey: ByteArray,
|
||||
@Json(name = "rp_key_type") val rpKeyType: Int,
|
||||
@Json(name = "rp_key") val rpKey: ByteArray
|
||||
){
|
||||
constructor(registeredHost: RegisteredHost) : this(
|
||||
registeredHost.target,
|
||||
registeredHost.apSsid,
|
||||
registeredHost.apBssid,
|
||||
registeredHost.apKey,
|
||||
registeredHost.apName,
|
||||
registeredHost.ps4Mac,
|
||||
registeredHost.ps4Nickname,
|
||||
registeredHost.serverMac,
|
||||
registeredHost.serverNickname,
|
||||
registeredHost.rpRegistKey,
|
||||
registeredHost.rpKeyType,
|
||||
registeredHost.rpKey
|
||||
|
@ -70,7 +60,7 @@ class SerializedRegisteredHost(
|
|||
@JsonClass(generateAdapter = true)
|
||||
class SerializedManualHost(
|
||||
@Json(name = "host") val host: String,
|
||||
@Json(name = "ps4_mac") val ps4Mac: MacAddress?
|
||||
@Json(name = "server_mac") val serverMac: MacAddress?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
@ -92,7 +82,7 @@ data class SerializedSettings(
|
|||
manualHost.host,
|
||||
manualHost.registeredHost?.let { 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 FORMAT = "chiaki-settings"
|
||||
private const val KEY_VERSION = "version"
|
||||
private const val VERSION = 1
|
||||
private const val VERSION = 2
|
||||
private const val KEY_SETTINGS = "settings"
|
||||
|
||||
fun exportAllSettings(db: AppDatabase) = SerializedSettings.fromDatabase(db)
|
||||
|
@ -176,7 +166,7 @@ fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDis
|
|||
try
|
||||
{
|
||||
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 adapter = moshi().serializedSettingsAdapter()
|
||||
|
||||
|
@ -212,13 +202,13 @@ fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDis
|
|||
if(it.isEmpty())
|
||||
"-"
|
||||
else
|
||||
it.joinToString(separator = "") { host -> "\n - ${host.ps4Nickname ?: "?"} / ${host.ps4Mac}" }
|
||||
it.joinToString(separator = "") { host -> "\n - ${host.serverNickname ?: "?"} / ${host.serverMac}" }
|
||||
},
|
||||
settings.manualHosts.let {
|
||||
if(it.isEmpty())
|
||||
"-"
|
||||
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)
|
||||
|
@ -257,7 +247,7 @@ abstract class ImportDao
|
|||
|
||||
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>
|
||||
|
||||
@Transaction
|
||||
|
@ -266,19 +256,20 @@ abstract class ImportDao
|
|||
insertRegisteredHosts(
|
||||
settings.registeredHosts.map {
|
||||
RegisteredHost(
|
||||
target = it.target,
|
||||
apSsid = it.apSsid,
|
||||
apBssid = it.apBssid,
|
||||
apKey = it.apKey,
|
||||
apName = it.apName,
|
||||
ps4Mac = it.ps4Mac,
|
||||
ps4Nickname = it.ps4Nickname,
|
||||
serverMac = it.serverMac,
|
||||
serverNickname = it.serverNickname,
|
||||
rpRegistKey = it.rpRegistKey,
|
||||
rpKeyType = it.rpKeyType,
|
||||
rpKey = it.rpKey
|
||||
)
|
||||
})
|
||||
|
||||
val macs = settings.manualHosts.mapNotNull { it.ps4Mac }
|
||||
val macs = settings.manualHosts.mapNotNull { it.serverMac }
|
||||
val idMacs =
|
||||
if(macs.isNotEmpty())
|
||||
registeredHostsByMac(macs)
|
||||
|
@ -289,7 +280,7 @@ abstract class ImportDao
|
|||
settings.manualHosts.map {
|
||||
ManualHost(
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common.ext
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common.ext
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common.ext
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.common.ext
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.discovery
|
||||
|
||||
|
@ -36,7 +21,7 @@ import java.nio.charset.Charset
|
|||
import java.nio.charset.StandardCharsets
|
||||
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)
|
||||
MacAddress(it)
|
||||
else
|
||||
|
@ -107,14 +92,14 @@ class DiscoveryManager
|
|||
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 credential = try { registKeyString.toULong(16) } catch(e: NumberFormatException) {
|
||||
Log.e("DiscoveryManager", "Failed to convert registKey to int", e)
|
||||
return
|
||||
}
|
||||
DiscoveryService.wakeup(discoveryService, host, credential)
|
||||
DiscoveryService.wakeup(discoveryService, host, credential, ps5)
|
||||
}
|
||||
|
||||
private fun updateService()
|
||||
|
|
|
@ -3,17 +3,27 @@ package com.metallic.chiaki.lib
|
|||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import android.view.Surface
|
||||
import kotlinx.android.parcel.IgnoredOnParcel
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.lang.Exception
|
||||
import java.net.InetSocketAddress
|
||||
import kotlin.math.abs
|
||||
|
||||
enum class Target(val value: Int)
|
||||
{
|
||||
PS4_UNKNOWN(0),
|
||||
PS4_8(800),
|
||||
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)
|
||||
|
@ -30,23 +40,32 @@ enum class VideoFPSPreset(val value: Int)
|
|||
FPS_60(60)
|
||||
}
|
||||
|
||||
enum class Codec(val value: Int)
|
||||
{
|
||||
CODEC_H264(0),
|
||||
CODEC_H265(1),
|
||||
CODEC_H265_HDR(2)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class ConnectVideoProfile(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val maxFPS: Int,
|
||||
val bitrate: Int
|
||||
val bitrate: Int,
|
||||
val codec: Codec
|
||||
): Parcelable
|
||||
{
|
||||
companion object
|
||||
{
|
||||
fun preset(resolutionPreset: VideoResolutionPreset, fpsPreset: VideoFPSPreset)
|
||||
= ChiakiNative.videoProfilePreset(resolutionPreset.value, fpsPreset.value)
|
||||
fun preset(resolutionPreset: VideoResolutionPreset, fpsPreset: VideoFPSPreset, codec: Codec)
|
||||
= ChiakiNative.videoProfilePreset(resolutionPreset.value, fpsPreset.value, codec)
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class ConnectInfo(
|
||||
val ps5: Boolean,
|
||||
val host: String,
|
||||
val registKey: ByteArray,
|
||||
val morning: ByteArray,
|
||||
|
@ -64,19 +83,19 @@ private class ChiakiNative
|
|||
}
|
||||
@JvmStatic external fun errorCodeToString(value: Int): String
|
||||
@JvmStatic external fun quitReasonToString(value: Int): String
|
||||
@JvmStatic external fun quitReasonIsStopped(value: Int): Boolean
|
||||
@JvmStatic external fun videoProfilePreset(resolutionPreset: Int, fpsPreset: Int): ConnectVideoProfile
|
||||
@JvmStatic external fun quitReasonIsError(value: Int): Boolean
|
||||
@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 sessionFree(ptr: Long)
|
||||
@JvmStatic external fun sessionStart(ptr: Long): Int
|
||||
@JvmStatic external fun sessionStop(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 sessionSetLoginPin(ptr: Long, pin: String)
|
||||
@JvmStatic external fun discoveryServiceCreate(result: CreateResult, options: DiscoveryServiceOptions, javaService: DiscoveryService)
|
||||
@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 registStop(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 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(
|
||||
var buttons: UInt = 0U,
|
||||
var l2State: UByte = 0U,
|
||||
|
@ -137,26 +164,40 @@ data class ControllerState constructor(
|
|||
var leftX: Short = 0,
|
||||
var leftY: 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
|
||||
{
|
||||
val BUTTON_CROSS = (1 shl 0).toUInt()
|
||||
val BUTTON_MOON = (1 shl 1).toUInt()
|
||||
val BUTTON_BOX = (1 shl 2).toUInt()
|
||||
val BUTTON_PYRAMID = (1 shl 3).toUInt()
|
||||
val BUTTON_BOX = (1 shl 2).toUInt()
|
||||
val BUTTON_PYRAMID = (1 shl 3).toUInt()
|
||||
val BUTTON_DPAD_LEFT = (1 shl 4).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_L1 = (1 shl 8).toUInt()
|
||||
val BUTTON_R1 = (1 shl 9).toUInt()
|
||||
val BUTTON_L1 = (1 shl 8).toUInt()
|
||||
val BUTTON_R1 = (1 shl 9).toUInt()
|
||||
val BUTTON_L3 = (1 shl 10).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_TOUCHPAD = (1 shl 14).toUInt()
|
||||
val BUTTON_TOUCHPAD = (1 shl 14).toUInt()
|
||||
val BUTTON_PS = (1 shl 15).toUInt()
|
||||
val TOUCHPAD_WIDTH: UShort = 1920U
|
||||
val TOUCHPAD_HEIGHT: UShort = 942U
|
||||
}
|
||||
|
||||
infix fun or(o: ControllerState) = ControllerState(
|
||||
|
@ -166,24 +207,116 @@ data class ControllerState constructor(
|
|||
leftX = maxAbs(leftX, o.leftX),
|
||||
leftY = maxAbs(leftY, o.leftY),
|
||||
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)
|
||||
{
|
||||
override fun toString() = ChiakiNative.quitReasonToString(value)
|
||||
|
||||
/**
|
||||
* whether the reason is CHIAKI_QUIT_REASON_STOPPED
|
||||
*/
|
||||
val isStopped = ChiakiNative.quitReasonIsStopped(value)
|
||||
val isError = ChiakiNative.quitReasonIsError(value)
|
||||
}
|
||||
|
||||
sealed class Event
|
||||
object ConnectedEvent: Event()
|
||||
data class LoginPinRequestEvent(val pinIncorrect: Boolean): 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")
|
||||
|
||||
|
@ -239,7 +372,12 @@ class Session(connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean)
|
|||
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)
|
||||
}
|
||||
|
@ -273,6 +411,8 @@ data class DiscoveryHost(
|
|||
READY,
|
||||
STANDBY
|
||||
}
|
||||
|
||||
val isPS5 get() = deviceDiscoveryProtocolVersion == "00030010"
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,8 +429,8 @@ class DiscoveryService(
|
|||
{
|
||||
companion object
|
||||
{
|
||||
fun wakeup(service: DiscoveryService?, host: String, userCredential: ULong) =
|
||||
ChiakiNative.discoveryServiceWakeup(service?.nativePtr ?: 0, host, userCredential.toLong())
|
||||
fun wakeup(service: DiscoveryService?, host: String, userCredential: ULong, ps5: Boolean) =
|
||||
ChiakiNative.discoveryServiceWakeup(service?.nativePtr ?: 0, host, userCredential.toLong(), ps5)
|
||||
}
|
||||
|
||||
private var nativePtr: Long
|
||||
|
@ -339,12 +479,13 @@ data class RegistInfo(
|
|||
}
|
||||
|
||||
data class RegistHost(
|
||||
val target: Target,
|
||||
val apSsid: String,
|
||||
val apBssid: String,
|
||||
val apKey: String,
|
||||
val apName: String,
|
||||
val ps4Mac: ByteArray,
|
||||
val ps4Nickname: String,
|
||||
val serverMac: ByteArray,
|
||||
val serverNickname: String,
|
||||
val rpRegistKey: ByteArray,
|
||||
val rpKeyType: UInt,
|
||||
val rpKey: ByteArray
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.main
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.ManualDisplayHost
|
||||
import com.metallic.chiaki.common.ext.inflate
|
||||
import com.metallic.chiaki.databinding.ItemDisplayHostBinding
|
||||
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()
|
||||
{
|
||||
|
@ -57,10 +43,10 @@ class DisplayHostRecyclerViewAdapter(
|
|||
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)
|
||||
= ViewHolder(parent.inflate(R.layout.item_display_host))
|
||||
= ViewHolder(ItemDisplayHostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
|
||||
override fun getItemCount() = hosts.count()
|
||||
|
||||
|
@ -68,7 +54,7 @@ class DisplayHostRecyclerViewAdapter(
|
|||
{
|
||||
val context = holder.itemView.context
|
||||
val host = hosts[position]
|
||||
holder.itemView.also {
|
||||
holder.binding.also {
|
||||
it.nameTextView.text = host.name
|
||||
it.hostTextView.text = context.getString(R.string.display_host_host, host.host)
|
||||
val id = host.id
|
||||
|
@ -90,16 +76,19 @@ class DisplayHostRecyclerViewAdapter(
|
|||
} ?: ""
|
||||
it.discoveredIndicatorLayout.visibility = if(host is DiscoveredDisplayHost) View.VISIBLE else View.GONE
|
||||
it.stateIndicatorImageView.setImageResource(
|
||||
if(host is DiscoveredDisplayHost)
|
||||
when(host.discoveredHost.state)
|
||||
when
|
||||
{
|
||||
host is DiscoveredDisplayHost -> when(host.discoveredHost.state)
|
||||
{
|
||||
DiscoveryHost.State.STANDBY -> R.drawable.ic_console_standby
|
||||
DiscoveryHost.State.READY -> R.drawable.ic_console_ready
|
||||
else -> R.drawable.ic_console
|
||||
DiscoveryHost.State.STANDBY -> if(host.isPS5) R.drawable.ic_console_ps5_standby else R.drawable.ic_console_standby
|
||||
DiscoveryHost.State.READY -> if(host.isPS5) R.drawable.ic_console_ps5_ready else R.drawable.ic_console_ready
|
||||
else -> if(host.isPS5) R.drawable.ic_console_ps5 else R.drawable.ic_console
|
||||
}
|
||||
else
|
||||
R.drawable.ic_console)
|
||||
it.setOnClickListener { clickCallback(host) }
|
||||
host.isPS5 -> R.drawable.ic_console_ps5
|
||||
else -> R.drawable.ic_console
|
||||
}
|
||||
)
|
||||
it.root.setOnClickListener { clickCallback(host) }
|
||||
|
||||
val canWakeup = host.registeredHost != null
|
||||
val canEditDelete = host is ManualDisplayHost
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.main
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.main
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.main
|
||||
|
||||
|
@ -32,52 +17,54 @@ import com.metallic.chiaki.R
|
|||
import com.metallic.chiaki.common.*
|
||||
import com.metallic.chiaki.common.ext.putRevealExtra
|
||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||
import com.metallic.chiaki.databinding.ActivityMainBinding
|
||||
import com.metallic.chiaki.lib.ConnectInfo
|
||||
import com.metallic.chiaki.lib.DiscoveryHost
|
||||
import com.metallic.chiaki.manualconsole.EditManualConsoleActivity
|
||||
import com.metallic.chiaki.regist.RegistActivity
|
||||
import com.metallic.chiaki.settings.SettingsActivity
|
||||
import com.metallic.chiaki.stream.StreamActivity
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
|
||||
class MainActivity : AppCompatActivity()
|
||||
{
|
||||
private lateinit var viewModel: MainViewModel
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var discoveryMenuItem: MenuItem? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
title = ""
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
floatingActionButton.setOnClickListener {
|
||||
expandFloatingActionButton(!floatingActionButton.isExpanded)
|
||||
binding.floatingActionButton.setOnClickListener {
|
||||
expandFloatingActionButton(!binding.floatingActionButton.isExpanded)
|
||||
}
|
||||
floatingActionButtonDialBackground.setOnClickListener {
|
||||
binding.floatingActionButtonDialBackground.setOnClickListener {
|
||||
expandFloatingActionButton(false)
|
||||
}
|
||||
|
||||
addManualButton.setOnClickListener { addManualConsole() }
|
||||
addManualLabelButton.setOnClickListener { addManualConsole() }
|
||||
binding.addManualButton.setOnClickListener { addManualConsole() }
|
||||
binding.addManualLabelButton.setOnClickListener { addManualConsole() }
|
||||
|
||||
registerButton.setOnClickListener { showRegistration() }
|
||||
registerLabelButton.setOnClickListener { showRegistration() }
|
||||
binding.registerButton.setOnClickListener { showRegistration() }
|
||||
binding.registerLabelButton.setOnClickListener { showRegistration() }
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory { MainViewModel(getDatabase(this), Preferences(this)) })
|
||||
.get(MainViewModel::class.java)
|
||||
|
||||
val recyclerViewAdapter = DisplayHostRecyclerViewAdapter(this::hostTriggered, this::wakeupHost, this::editHost, this::deleteHost)
|
||||
hostsRecyclerView.adapter = recyclerViewAdapter
|
||||
hostsRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||
binding.hostsRecyclerView.adapter = recyclerViewAdapter
|
||||
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||
viewModel.displayHosts.observe(this, Observer {
|
||||
val top = hostsRecyclerView.computeVerticalScrollOffset() == 0
|
||||
val top = binding.hostsRecyclerView.computeVerticalScrollOffset() == 0
|
||||
recyclerViewAdapter.hosts = it
|
||||
if(top)
|
||||
hostsRecyclerView.scrollToPosition(0)
|
||||
binding.hostsRecyclerView.scrollToPosition(0)
|
||||
updateEmptyInfo()
|
||||
})
|
||||
|
||||
|
@ -91,19 +78,19 @@ class MainActivity : AppCompatActivity()
|
|||
{
|
||||
if(viewModel.displayHosts.value?.isEmpty() ?: true)
|
||||
{
|
||||
emptyInfoLayout.visibility = View.VISIBLE
|
||||
binding.emptyInfoLayout.visibility = View.VISIBLE
|
||||
val discoveryActive = viewModel.discoveryActive.value ?: false
|
||||
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.emptyInfoImageView.setImageResource(if(discoveryActive) R.drawable.ic_discover_on else R.drawable.ic_discover_off)
|
||||
binding.emptyInfoTextView.setText(if(discoveryActive) R.string.display_hosts_empty_discovery_on_info else R.string.display_hosts_empty_discovery_off_info)
|
||||
}
|
||||
else
|
||||
emptyInfoLayout.visibility = View.GONE
|
||||
binding.emptyInfoLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun expandFloatingActionButton(expand: Boolean)
|
||||
{
|
||||
floatingActionButton.isExpanded = expand
|
||||
floatingActionButton.isActivated = floatingActionButton.isExpanded
|
||||
binding.floatingActionButton.isExpanded = expand
|
||||
binding.floatingActionButton.isActivated = binding.floatingActionButton.isExpanded
|
||||
}
|
||||
|
||||
override fun onStart()
|
||||
|
@ -120,7 +107,7 @@ class MainActivity : AppCompatActivity()
|
|||
|
||||
override fun onBackPressed()
|
||||
{
|
||||
if(floatingActionButton.isExpanded)
|
||||
if(binding.floatingActionButton.isExpanded)
|
||||
{
|
||||
expandFloatingActionButton(false)
|
||||
return
|
||||
|
@ -166,7 +153,7 @@ class MainActivity : AppCompatActivity()
|
|||
private fun addManualConsole()
|
||||
{
|
||||
Intent(this, EditManualConsoleActivity::class.java).also {
|
||||
it.putRevealExtra(addManualButton, rootLayout)
|
||||
it.putRevealExtra(binding.addManualButton, binding.rootLayout)
|
||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +161,7 @@ class MainActivity : AppCompatActivity()
|
|||
private fun showRegistration()
|
||||
{
|
||||
Intent(this, RegistActivity::class.java).also {
|
||||
it.putRevealExtra(registerButton, rootLayout)
|
||||
it.putRevealExtra(binding.registerButton, binding.rootLayout)
|
||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +172,7 @@ class MainActivity : AppCompatActivity()
|
|||
if(registeredHost != null)
|
||||
{
|
||||
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 {
|
||||
it.putExtra(StreamActivity.EXTRA_CONNECT_INFO, connectInfo)
|
||||
startActivity(it)
|
||||
|
@ -224,7 +211,7 @@ class MainActivity : AppCompatActivity()
|
|||
private fun wakeupHost(host: DisplayHost)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.main
|
||||
|
||||
|
@ -21,7 +6,7 @@ import androidx.lifecycle.ViewModel
|
|||
import com.metallic.chiaki.common.*
|
||||
import com.metallic.chiaki.common.ext.toLiveData
|
||||
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.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.Observables
|
||||
|
@ -46,10 +31,10 @@ class MainViewModel(val database: AppDatabase, val preferences: Preferences): Vi
|
|||
database.registeredHostDao().getAll().toObservable(),
|
||||
discoveryManager.discoveredHosts)
|
||||
{ manualHosts, registeredHosts, discoveredHosts ->
|
||||
val macRegisteredHosts = registeredHosts.associateBy { it.ps4Mac }
|
||||
val macRegisteredHosts = registeredHosts.associateBy { it.serverMac }
|
||||
val idRegisteredHosts = registeredHosts.associateBy { it.id }
|
||||
discoveredHosts.map {
|
||||
DiscoveredDisplayHost(it.ps4Mac?.let { mac -> macRegisteredHosts[mac] }, it)
|
||||
DiscoveredDisplayHost(it.serverMac?.let { mac -> macRegisteredHosts[mac] }, it)
|
||||
} +
|
||||
manualHosts.map {
|
||||
ManualDisplayHost(it.registeredHost?.let { id -> idRegisteredHosts[id] }, it)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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.viewModelFactory
|
||||
import com.metallic.chiaki.common.getDatabase
|
||||
import com.metallic.chiaki.databinding.ActivityEditManualBinding
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.addTo
|
||||
import kotlinx.android.synthetic.main.activity_edit_manual.*
|
||||
|
||||
class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
||||
{
|
||||
|
@ -43,18 +28,20 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
|||
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 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()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_edit_manual)
|
||||
binding = ActivityEditManualBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
handleReveal()
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory {
|
||||
|
@ -67,17 +54,17 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
|||
.get(EditManualConsoleViewModel::class.java)
|
||||
|
||||
viewModel.existingHost?.observe(this, Observer {
|
||||
hostEditText.setText(it.host)
|
||||
binding.hostEditText.setText(it.host)
|
||||
})
|
||||
|
||||
viewModel.selectedRegisteredHost.observe(this, Observer {
|
||||
registeredHostTextView.setText(titleForRegisteredHost(it))
|
||||
binding.registeredHostTextView.setText(titleForRegisteredHost(it))
|
||||
})
|
||||
|
||||
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) }))
|
||||
registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
|
||||
binding.registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
|
||||
{
|
||||
if(position >= hosts.size)
|
||||
|
@ -88,26 +75,25 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
saveButton.setOnClickListener { saveHost() }
|
||||
binding.saveButton.setOnClickListener { saveHost() }
|
||||
}
|
||||
|
||||
private fun titleForRegisteredHost(registeredHost: RegisteredHost?) =
|
||||
if(registeredHost == null)
|
||||
getString(R.string.add_manual_regist_on_connect)
|
||||
else
|
||||
"${registeredHost.ps4Nickname ?: ""} (${registeredHost.ps4Mac})"
|
||||
"${registeredHost.serverNickname ?: ""} (${registeredHost.serverMac})"
|
||||
|
||||
private fun saveHost()
|
||||
{
|
||||
val host = hostEditText.text.toString().trim()
|
||||
val host = binding.hostEditText.text.toString().trim()
|
||||
if(host.isEmpty())
|
||||
{
|
||||
hostEditText.error = getString(R.string.entered_host_invalid)
|
||||
binding.hostEditText.error = getString(R.string.entered_host_invalid)
|
||||
return
|
||||
}
|
||||
|
||||
saveButton.isEnabled = false
|
||||
binding.saveButton.isEnabled = false
|
||||
viewModel.saveHost(host)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.manualconsole
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.regist
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.regist
|
||||
|
||||
|
@ -27,9 +12,9 @@ import androidx.lifecycle.Observer
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.common.ext.RevealActivity
|
||||
import com.metallic.chiaki.databinding.ActivityRegistBinding
|
||||
import com.metallic.chiaki.lib.RegistInfo
|
||||
import com.metallic.chiaki.lib.Target
|
||||
import kotlinx.android.synthetic.main.activity_regist.*
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
class RegistActivity: AppCompatActivity(), RevealActivity
|
||||
|
@ -45,96 +30,103 @@ class RegistActivity: AppCompatActivity(), RevealActivity
|
|||
private const val REQUEST_REGIST = 1
|
||||
}
|
||||
|
||||
private lateinit var viewModel: RegistViewModel
|
||||
private lateinit var binding: ActivityRegistBinding
|
||||
|
||||
override val revealWindow: Window get() = window
|
||||
override val revealIntent: Intent get() = intent
|
||||
override val revealRootLayout: View get() = rootLayout
|
||||
|
||||
private lateinit var viewModel: RegistViewModel
|
||||
override val revealRootLayout: View get() = binding.rootLayout
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_regist)
|
||||
binding = ActivityRegistBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
handleReveal()
|
||||
|
||||
viewModel = ViewModelProvider(this).get(RegistViewModel::class.java)
|
||||
|
||||
hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
|
||||
broadcastCheckBox.isChecked = intent.getBooleanExtra(EXTRA_BROADCAST, true)
|
||||
binding.hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
|
||||
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) {
|
||||
RegistViewModel.PS4Version.GE_8 -> R.id.ps4VersionGE8RadioButton
|
||||
RegistViewModel.PS4Version.GE_7 -> R.id.ps4VersionGE7RadioButton
|
||||
RegistViewModel.PS4Version.LT_7 -> R.id.ps4VersionLT7RadioButton
|
||||
binding.ps4VersionRadioGroup.check(when(viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5) {
|
||||
RegistViewModel.ConsoleVersion.PS5 -> R.id.ps5RadioButton
|
||||
RegistViewModel.ConsoleVersion.PS4_GE_8 -> R.id.ps4VersionGE8RadioButton
|
||||
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)
|
||||
{
|
||||
R.id.ps4VersionGE8RadioButton -> RegistViewModel.PS4Version.GE_8
|
||||
R.id.ps4VersionGE7RadioButton -> RegistViewModel.PS4Version.GE_7
|
||||
R.id.ps4VersionLT7RadioButton -> RegistViewModel.PS4Version.LT_7
|
||||
else -> RegistViewModel.PS4Version.GE_7
|
||||
R.id.ps5RadioButton -> RegistViewModel.ConsoleVersion.PS5
|
||||
R.id.ps4VersionGE8RadioButton -> RegistViewModel.ConsoleVersion.PS4_GE_8
|
||||
R.id.ps4VersionGE7RadioButton -> RegistViewModel.ConsoleVersion.PS4_GE_7
|
||||
R.id.ps4VersionLT7RadioButton -> RegistViewModel.ConsoleVersion.PS4_LT_7
|
||||
else -> RegistViewModel.ConsoleVersion.PS5
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.ps4Version.observe(this, Observer {
|
||||
psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.PS4Version.LT_7) View.GONE else View.VISIBLE
|
||||
psnIdTextInputLayout.hint = getString(when(it!!)
|
||||
binding.psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.ConsoleVersion.PS4_LT_7) View.GONE else View.VISIBLE
|
||||
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
|
||||
})
|
||||
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()
|
||||
{
|
||||
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 broadcast = broadcastCheckBox.isChecked
|
||||
val broadcast = binding.broadcastCheckBox.isChecked
|
||||
|
||||
val psnId = psnIdEditText.text.toString().trim()
|
||||
val psnOnlineId: String? = if(ps4Version == RegistViewModel.PS4Version.LT_7) psnId else null
|
||||
val psnId = binding.psnIdEditText.text.toString().trim()
|
||||
val psnOnlineId: String? = if(ps4Version == RegistViewModel.ConsoleVersion.PS4_LT_7) psnId else null
|
||||
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 }
|
||||
else
|
||||
null
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
val pin = pinEditText.text.toString()
|
||||
val pin = binding.pinEditText.text.toString()
|
||||
val pinValid = pin.length == PIN_LENGTH
|
||||
|
||||
hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
|
||||
psnIdEditText.error =
|
||||
binding.hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
|
||||
binding.psnIdEditText.error =
|
||||
if(!psnIdValid)
|
||||
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
|
||||
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)
|
||||
return
|
||||
|
||||
val target = when(ps4Version)
|
||||
{
|
||||
RegistViewModel.PS4Version.GE_8 -> Target.PS4_10
|
||||
RegistViewModel.PS4Version.GE_7 -> Target.PS4_9
|
||||
RegistViewModel.PS4Version.LT_7 -> Target.PS4_8
|
||||
RegistViewModel.ConsoleVersion.PS5 -> Target.PS5_1
|
||||
RegistViewModel.ConsoleVersion.PS4_GE_8 -> Target.PS4_10
|
||||
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())
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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.ext.viewModelFactory
|
||||
import com.metallic.chiaki.common.getDatabase
|
||||
import com.metallic.chiaki.databinding.ActivityRegistExecuteBinding
|
||||
import com.metallic.chiaki.lib.RegistInfo
|
||||
import kotlinx.android.synthetic.main.activity_regist_execute.*
|
||||
import kotlin.math.max
|
||||
|
||||
class RegistExecuteActivity: AppCompatActivity()
|
||||
|
@ -46,55 +31,57 @@ class RegistExecuteActivity: AppCompatActivity()
|
|||
}
|
||||
|
||||
private lateinit var viewModel: RegistExecuteViewModel
|
||||
private lateinit var binding: ActivityRegistExecuteBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_regist_execute)
|
||||
binding = ActivityRegistExecuteBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory { RegistExecuteViewModel(getDatabase(this)) })
|
||||
.get(RegistExecuteViewModel::class.java)
|
||||
|
||||
logTextView.setHorizontallyScrolling(true)
|
||||
logTextView.movementMethod = ScrollingMovementMethod()
|
||||
binding.logTextView.setHorizontallyScrolling(true)
|
||||
binding.logTextView.movementMethod = ScrollingMovementMethod()
|
||||
viewModel.logText.observe(this, Observer {
|
||||
val textLayout = logTextView.layout ?: return@Observer
|
||||
val textLayout = binding.logTextView.layout ?: return@Observer
|
||||
val lineCount = textLayout.lineCount
|
||||
if(lineCount < 1)
|
||||
return@Observer
|
||||
logTextView.text = it
|
||||
val scrollY = textLayout.getLineBottom(lineCount - 1) - logTextView.height + logTextView.paddingTop + logTextView.paddingBottom
|
||||
logTextView.scrollTo(0, max(scrollY, 0))
|
||||
binding.logTextView.text = it
|
||||
val scrollY = textLayout.getLineBottom(lineCount - 1) - binding.logTextView.height + binding.logTextView.paddingTop + binding.logTextView.paddingBottom
|
||||
binding.logTextView.scrollTo(0, max(scrollY, 0))
|
||||
})
|
||||
|
||||
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)
|
||||
{
|
||||
RegistExecuteViewModel.State.FAILED ->
|
||||
{
|
||||
infoTextView.visibility = View.VISIBLE
|
||||
infoTextView.setText(R.string.regist_info_failed)
|
||||
binding.infoTextView.visibility = View.VISIBLE
|
||||
binding.infoTextView.setText(R.string.regist_info_failed)
|
||||
setResult(RESULT_FAILED)
|
||||
}
|
||||
RegistExecuteViewModel.State.SUCCESSFUL, RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE ->
|
||||
{
|
||||
infoTextView.visibility = View.VISIBLE
|
||||
infoTextView.setText(R.string.regist_info_success)
|
||||
binding.infoTextView.visibility = View.VISIBLE
|
||||
binding.infoTextView.setText(R.string.regist_info_success)
|
||||
setResult(RESULT_OK)
|
||||
if(it == RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE)
|
||||
showDuplicateDialog()
|
||||
}
|
||||
RegistExecuteViewModel.State.STOPPED ->
|
||||
{
|
||||
infoTextView.visibility = View.GONE
|
||||
binding.infoTextView.visibility = View.GONE
|
||||
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 ?: ""
|
||||
Intent(Intent.ACTION_SEND).also {
|
||||
it.type = "text/plain"
|
||||
|
@ -129,7 +116,7 @@ class RegistExecuteActivity: AppCompatActivity()
|
|||
if(dialog != null)
|
||||
return
|
||||
|
||||
val macStr = viewModel.host?.ps4Mac?.let { MacAddress(it).toString() } ?: ""
|
||||
val macStr = viewModel.host?.serverMac?.let { MacAddress(it).toString() } ?: ""
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(this)
|
||||
.setMessage(getString(R.string.alert_regist_duplicate, macStr))
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.regist
|
||||
|
||||
|
@ -93,7 +78,7 @@ class RegistExecuteViewModel(val database: AppDatabase): ViewModel()
|
|||
private fun registSuccess(host: RegistHost)
|
||||
{
|
||||
this.host = host
|
||||
database.registeredHostDao().getByMac(MacAddress(host.ps4Mac))
|
||||
database.registeredHostDao().getByMac(MacAddress(host.serverMac))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess {
|
||||
|
@ -113,7 +98,7 @@ class RegistExecuteViewModel(val database: AppDatabase): ViewModel()
|
|||
val dao = database.registeredHostDao()
|
||||
val manualHostDao = database.manualHostDao()
|
||||
val registeredHost = RegisteredHost(host)
|
||||
dao.deleteByMac(registeredHost.ps4Mac)
|
||||
dao.deleteByMac(registeredHost.serverMac)
|
||||
.andThen(dao.insert(registeredHost))
|
||||
.let {
|
||||
if(assignManualHostId != null)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.regist
|
||||
|
||||
|
@ -22,11 +7,14 @@ import androidx.lifecycle.ViewModel
|
|||
|
||||
class RegistViewModel: ViewModel()
|
||||
{
|
||||
enum class PS4Version {
|
||||
GE_8,
|
||||
GE_7,
|
||||
LT_7
|
||||
enum class ConsoleVersion {
|
||||
PS5,
|
||||
PS4_GE_8,
|
||||
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
|
||||
|
||||
import android.util.Log
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.content.Context
|
||||
import android.hardware.*
|
||||
import android.view.*
|
||||
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.lib.ControllerState
|
||||
|
||||
class StreamInput(val preferences: Preferences)
|
||||
class StreamInput(val context: Context, val preferences: Preferences)
|
||||
{
|
||||
var controllerStateChangedCallback: ((ControllerState) -> Unit)? = null
|
||||
|
||||
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
|
||||
// (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
|
||||
}
|
||||
|
||||
private val sensorControllerState = ControllerState() // from Motion Sensors
|
||||
private val keyControllerState = ControllerState() // from KeyEvents
|
||||
private val motionControllerState = ControllerState() // from MotionEvents
|
||||
var touchControllerState = ControllerState()
|
||||
|
@ -36,6 +55,66 @@ class StreamInput(val preferences: Preferences)
|
|||
|
||||
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()
|
||||
{
|
||||
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)
|
||||
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()
|
||||
motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
|
||||
motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.session
|
||||
|
||||
|
@ -40,8 +25,11 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
|||
|
||||
private val _state = MutableLiveData<StreamState>(StreamStateIdle)
|
||||
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
|
||||
{
|
||||
|
@ -74,9 +62,9 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
|||
_state.value = StreamStateConnecting
|
||||
session.eventCallback = this::eventCallback
|
||||
session.start()
|
||||
val surfaceTexture = surfaceTexture
|
||||
if(surfaceTexture != null)
|
||||
session.setSurface(Surface(surfaceTexture))
|
||||
val surface = surface
|
||||
if(surface != null)
|
||||
session.setSurface(surface)
|
||||
this.session = session
|
||||
}
|
||||
catch(e: CreateError)
|
||||
|
@ -101,9 +89,30 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
|||
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)
|
||||
{
|
||||
textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
|
||||
|
@ -112,6 +121,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
|||
if(surfaceTexture != null)
|
||||
return
|
||||
surfaceTexture = surface
|
||||
this@StreamSession.surface = Surface(surfaceTexture)
|
||||
session?.setSurface(Surface(surface))
|
||||
}
|
||||
|
||||
|
@ -127,7 +137,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
|
|||
|
||||
val surfaceTexture = surfaceTexture
|
||||
if(surfaceTexture != null)
|
||||
textureView.surfaceTexture = surfaceTexture
|
||||
textureView.setSurfaceTexture(surfaceTexture)
|
||||
}
|
||||
|
||||
fun setLoginPin(pin: String)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
|
@ -24,7 +9,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.metallic.chiaki.R
|
||||
import kotlinx.android.synthetic.main.activity_settings.*
|
||||
import com.metallic.chiaki.databinding.ActivitySettingsBinding
|
||||
|
||||
interface TitleFragment
|
||||
{
|
||||
|
@ -33,20 +18,23 @@ interface TitleFragment
|
|||
|
||||
class SettingsActivity: AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
|
||||
{
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
title = ""
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
val rootFragment = SettingsFragment()
|
||||
replaceFragment(rootFragment, false)
|
||||
supportFragmentManager.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)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
|
@ -40,6 +25,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
|||
{
|
||||
preferences.logVerboseKey -> preferences.logVerbose
|
||||
preferences.swapCrossMoonKey -> preferences.swapCrossMoon
|
||||
preferences.rumbleEnabledKey -> preferences.rumbleEnabled
|
||||
preferences.motionEnabledKey -> preferences.motionEnabled
|
||||
preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled
|
||||
else -> defValue
|
||||
}
|
||||
|
||||
|
@ -49,6 +37,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
|||
{
|
||||
preferences.logVerboseKey -> preferences.logVerbose = 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.fpsKey -> preferences.fps.value
|
||||
preferences.bitrateKey -> preferences.bitrate?.toString() ?: ""
|
||||
preferences.codecKey -> preferences.codec.value
|
||||
else -> defValue
|
||||
}
|
||||
|
||||
|
@ -75,6 +67,11 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
|
|||
preferences.fps = fps
|
||||
}
|
||||
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
|
||||
})
|
||||
|
||||
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")
|
||||
viewModel.registeredHostsCount.observe(this, Observer {
|
||||
registeredHostsPreference?.summary = getString(R.string.preferences_registered_hosts_summary, it)
|
||||
|
|
|
@ -1,29 +1,14 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
import android.view.View
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.common.LogFile
|
||||
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.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -35,7 +20,7 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
|
|||
private val dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
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()
|
||||
set(value)
|
||||
|
@ -44,16 +29,16 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
|
|||
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 onBindViewHolder(holder: ViewHolder, position: Int)
|
||||
{
|
||||
val view = holder.itemView
|
||||
val logFile = logFiles[position]
|
||||
view.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
|
||||
view.summaryTextView.text = logFile.filename
|
||||
view.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
|
||||
holder.binding.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
|
||||
holder.binding.summaryTextView.text = logFile.filename
|
||||
holder.binding.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
|
||||
}
|
||||
}
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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.ext.viewModelFactory
|
||||
import com.metallic.chiaki.common.fileProviderAuthority
|
||||
import kotlinx.android.synthetic.main.fragment_settings_logs.*
|
||||
import com.metallic.chiaki.databinding.FragmentSettingsLogsBinding
|
||||
|
||||
class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
|
||||
{
|
||||
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 =
|
||||
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?)
|
||||
{
|
||||
val context = context!!
|
||||
val context = requireContext()
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory { SettingsLogsViewModel(LogManager(context)) })
|
||||
.get(SettingsLogsViewModel::class.java)
|
||||
|
||||
val adapter = SettingsLogsAdapter()
|
||||
logsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
logsRecyclerView.adapter = adapter
|
||||
binding.logsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.logsRecyclerView.adapter = adapter
|
||||
adapter.shareCallback = this::shareLogFile
|
||||
viewModel.sessionLogs.observe(viewLifecycleOwner, Observer {
|
||||
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)
|
||||
|
@ -70,7 +61,7 @@ class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
|
|||
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)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
import android.view.View
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.common.RegisteredHost
|
||||
import com.metallic.chiaki.common.ext.inflate
|
||||
import kotlinx.android.synthetic.main.item_registered_host.view.*
|
||||
import com.metallic.chiaki.databinding.ItemRegisteredHostBinding
|
||||
|
||||
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()
|
||||
set(value)
|
||||
|
@ -36,15 +19,15 @@ class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHos
|
|||
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 onBindViewHolder(holder: ViewHolder, position: Int)
|
||||
{
|
||||
val view = holder.itemView
|
||||
val host = hosts[position]
|
||||
view.nameTextView.text = host.ps4Nickname
|
||||
view.summaryTextView.text = host.ps4Mac.toString()
|
||||
holder.binding.nameTextView.text = "${host.serverNickname} (${if(host.target.isPS5) "PS5" else "PS4"})"
|
||||
holder.binding.summaryTextView.text = host.serverMac.toString()
|
||||
}
|
||||
}
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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.viewModelFactory
|
||||
import com.metallic.chiaki.common.getDatabase
|
||||
import com.metallic.chiaki.databinding.FragmentSettingsRegisteredHostsBinding
|
||||
import com.metallic.chiaki.regist.RegistActivity
|
||||
import kotlinx.android.synthetic.main.fragment_settings_registered_hosts.*
|
||||
|
||||
class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
|
||||
{
|
||||
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 =
|
||||
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?)
|
||||
{
|
||||
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context!!)) })
|
||||
val context = requireContext()
|
||||
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context)) })
|
||||
.get(SettingsRegisteredHostsViewModel::class.java)
|
||||
|
||||
val adapter = SettingsRegisteredHostsAdapter()
|
||||
hostsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
hostsRecyclerView.adapter = adapter
|
||||
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context!!)
|
||||
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.hostsRecyclerView.adapter = adapter
|
||||
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
|
||||
{
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int)
|
||||
{
|
||||
val pos = viewHolder.adapterPosition
|
||||
val host = viewModel.registeredHosts.value?.getOrNull(pos) ?: return
|
||||
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) { _, _ ->
|
||||
viewModel.deleteHost(host)
|
||||
}
|
||||
|
@ -71,15 +63,15 @@ class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
|
|||
.show()
|
||||
}
|
||||
}
|
||||
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(hostsRecyclerView)
|
||||
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(binding.hostsRecyclerView)
|
||||
viewModel.registeredHosts.observe(this, Observer {
|
||||
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 {
|
||||
it.putRevealExtra(floatingActionButton, rootLayout)
|
||||
it.putRevealExtra(binding.floatingActionButton, binding.rootLayout)
|
||||
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.settings
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.stream
|
||||
|
||||
|
@ -21,12 +6,8 @@ import android.animation.Animator
|
|||
import android.animation.AnimatorListenerAdapter
|
||||
import android.app.AlertDialog
|
||||
import android.graphics.Matrix
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.TextureView
|
||||
import android.view.View
|
||||
import android.os.*
|
||||
import android.view.*
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isGone
|
||||
|
@ -35,15 +16,18 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.lifecycle.*
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.common.LogManager
|
||||
import com.metallic.chiaki.common.Preferences
|
||||
import com.metallic.chiaki.common.ext.viewModelFactory
|
||||
import com.metallic.chiaki.databinding.ActivityStreamBinding
|
||||
import com.metallic.chiaki.lib.ConnectInfo
|
||||
import com.metallic.chiaki.lib.ConnectVideoProfile
|
||||
import com.metallic.chiaki.session.*
|
||||
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
|
||||
import com.metallic.chiaki.touchcontrols.DefaultTouchControlsFragment
|
||||
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 object StreamQuitDialog: DialogContents()
|
||||
|
@ -59,6 +43,8 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
}
|
||||
|
||||
private lateinit var viewModel: StreamViewModel
|
||||
private lateinit var binding: ActivityStreamBinding
|
||||
|
||||
private val uiVisibilityHandler = Handler()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
|
@ -73,64 +59,76 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
}
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory {
|
||||
StreamViewModel(Preferences(this), LogManager(this), connectInfo)
|
||||
StreamViewModel(application, connectInfo)
|
||||
})[StreamViewModel::class.java]
|
||||
|
||||
setContentView(R.layout.activity_stream)
|
||||
viewModel.input.observe(this)
|
||||
|
||||
binding = ActivityStreamBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window.decorView.setOnSystemUiVisibilityChangeListener(this)
|
||||
|
||||
viewModel.onScreenControlsEnabled.observe(this, Observer {
|
||||
if(onScreenControlsSwitch.isChecked != it)
|
||||
onScreenControlsSwitch.isChecked = it
|
||||
if(onScreenControlsSwitch.isChecked)
|
||||
touchpadOnlySwitch.isChecked = false
|
||||
if(binding.onScreenControlsSwitch.isChecked != it)
|
||||
binding.onScreenControlsSwitch.isChecked = it
|
||||
if(binding.onScreenControlsSwitch.isChecked)
|
||||
binding.touchpadOnlySwitch.isChecked = false
|
||||
})
|
||||
onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
viewModel.setOnScreenControlsEnabled(isChecked)
|
||||
showOverlay()
|
||||
}
|
||||
|
||||
viewModel.touchpadOnlyEnabled.observe(this, Observer {
|
||||
if(touchpadOnlySwitch.isChecked != it)
|
||||
touchpadOnlySwitch.isChecked = it
|
||||
if(touchpadOnlySwitch.isChecked)
|
||||
onScreenControlsSwitch.isChecked = false
|
||||
if(binding.touchpadOnlySwitch.isChecked != it)
|
||||
binding.touchpadOnlySwitch.isChecked = it
|
||||
if(binding.touchpadOnlySwitch.isChecked)
|
||||
binding.onScreenControlsSwitch.isChecked = false
|
||||
})
|
||||
touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
viewModel.setTouchpadOnlyEnabled(isChecked)
|
||||
showOverlay()
|
||||
}
|
||||
|
||||
displayModeToggle.addOnButtonCheckedListener { _, checkedId, _ ->
|
||||
// following 'if' is a workaround until selectionRequired for MaterialButtonToggleGroup
|
||||
// comes out of alpha.
|
||||
// See https://stackoverflow.com/questions/56164004/required-single-selection-on-materialbuttontogglegroup
|
||||
if (displayModeToggle.checkedButtonId == -1)
|
||||
displayModeToggle.check(checkedId)
|
||||
|
||||
adjustTextureViewAspect()
|
||||
binding.displayModeToggle.addOnButtonCheckedListener { _, _, _ ->
|
||||
adjustStreamViewAspect()
|
||||
showOverlay()
|
||||
}
|
||||
|
||||
viewModel.session.attachToTextureView(textureView)
|
||||
//viewModel.session.attachToTextureView(textureView)
|
||||
viewModel.session.attachToSurfaceView(binding.surfaceView)
|
||||
viewModel.session.state.observe(this, Observer { this.stateChanged(it) })
|
||||
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||
adjustTextureViewAspect()
|
||||
adjustStreamViewAspect()
|
||||
|
||||
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)
|
||||
{
|
||||
super.onAttachFragment(fragment)
|
||||
if(fragment is TouchControlsFragment)
|
||||
{
|
||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
||||
fragment.controllerState
|
||||
.subscribe { viewModel.input.touchControllerState = it }
|
||||
.addTo(controlsDisposable)
|
||||
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
|
||||
}
|
||||
if(fragment is TouchpadOnlyFragment)
|
||||
{
|
||||
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
|
||||
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
||||
if(fragment is TouchpadOnlyFragment)
|
||||
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +145,12 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
viewModel.session.pause()
|
||||
}
|
||||
|
||||
override fun onDestroy()
|
||||
{
|
||||
super.onDestroy()
|
||||
controlsDisposable.dispose()
|
||||
}
|
||||
|
||||
private fun reconnect()
|
||||
{
|
||||
viewModel.session.shutdown()
|
||||
|
@ -165,14 +169,14 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
|
||||
private fun showOverlay()
|
||||
{
|
||||
overlay.isVisible = true
|
||||
overlay.animate()
|
||||
binding.overlay.isVisible = true
|
||||
binding.overlay.animate()
|
||||
.alpha(1.0f)
|
||||
.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)
|
||||
|
@ -181,13 +185,13 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
|
||||
private fun hideOverlay()
|
||||
{
|
||||
overlay.animate()
|
||||
binding.overlay.animate()
|
||||
.alpha(0.0f)
|
||||
.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)
|
||||
{
|
||||
progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
|
||||
binding.progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
|
||||
|
||||
when(state)
|
||||
{
|
||||
is StreamStateQuit ->
|
||||
{
|
||||
if(!state.reason.isStopped && dialogContents != StreamQuitDialog)
|
||||
if(dialogContents != StreamQuitDialog)
|
||||
{
|
||||
dialog?.dismiss()
|
||||
val reasonStr = state.reasonString
|
||||
val dialog = MaterialAlertDialogBuilder(this)
|
||||
.setMessage(getString(R.string.alert_message_session_quit, state.reason.toString())
|
||||
+ (if(reasonStr != null) "\n$reasonStr" else ""))
|
||||
.setPositiveButton(R.string.action_reconnect) { _, _ ->
|
||||
dialog = null
|
||||
reconnect()
|
||||
}
|
||||
.setOnCancelListener {
|
||||
dialog = null
|
||||
finish()
|
||||
}
|
||||
.setNegativeButton(R.string.action_quit_session) { _, _ ->
|
||||
dialog = null
|
||||
finish()
|
||||
}
|
||||
.create()
|
||||
dialogContents = StreamQuitDialog
|
||||
dialog.show()
|
||||
if(state.reason.isError)
|
||||
{
|
||||
dialog?.dismiss()
|
||||
val reasonStr = state.reasonString
|
||||
val dialog = MaterialAlertDialogBuilder(this)
|
||||
.setMessage(getString(R.string.alert_message_session_quit, state.reason.toString())
|
||||
+ (if(reasonStr != null) "\n$reasonStr" else ""))
|
||||
.setPositiveButton(R.string.action_reconnect) { _, _ ->
|
||||
dialog = null
|
||||
reconnect()
|
||||
}
|
||||
.setOnCancelListener {
|
||||
dialog = null
|
||||
finish()
|
||||
}
|
||||
.setNegativeButton(R.string.action_quit_session) { _, _ ->
|
||||
dialog = null
|
||||
finish()
|
||||
}
|
||||
.create()
|
||||
dialogContents = StreamQuitDialog
|
||||
dialog.show()
|
||||
}
|
||||
else
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,77 +311,89 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
|
|||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustTextureViewAspect()
|
||||
private fun adjustTextureViewAspect(textureView: TextureView)
|
||||
{
|
||||
val displayInfo = DisplayInfo(viewModel.session.connectInfo.videoProfile, textureView)
|
||||
val resolution = displayInfo.computeResolutionFor(displayModeToggle.checkedButtonId)
|
||||
|
||||
val trans = TextureViewTransform(viewModel.session.connectInfo.videoProfile, textureView)
|
||||
val resolution = trans.resolutionFor(TransformMode.fromButton(binding.displayModeToggle.checkedButtonId))
|
||||
Matrix().also {
|
||||
textureView.getTransform(it)
|
||||
it.setScale(resolution.width / displayInfo.viewWidth, resolution.height / displayInfo.viewHeight)
|
||||
it.postTranslate((displayInfo.viewWidth - resolution.width) * 0.5f, (displayInfo.viewHeight - resolution.height) * 0.5f)
|
||||
it.setScale(resolution.width / trans.viewWidth, resolution.height / trans.viewHeight)
|
||||
it.postTranslate((trans.viewWidth - resolution.width) * 0.5f, (trans.viewHeight - resolution.height) * 0.5f)
|
||||
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 onGenericMotionEvent(event: MotionEvent) = viewModel.input.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
|
||||
|
||||
}
|
||||
|
||||
|
||||
class DisplayInfo constructor(val videoProfile: ConnectVideoProfile, val textureView: TextureView)
|
||||
enum class TransformMode
|
||||
{
|
||||
val contentWidth : Float get() = videoProfile.width.toFloat()
|
||||
val contentHeight : Float get() = videoProfile.height.toFloat()
|
||||
FIT,
|
||||
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 viewHeight : Float get() = textureView.height.toFloat()
|
||||
val contentAspect : Float get() = contentHeight / contentWidth
|
||||
private val contentAspect : Float get() = contentHeight / contentWidth
|
||||
|
||||
fun computeResolutionFor(displayModeButtonId: Int) : Resolution
|
||||
{
|
||||
when (displayModeButtonId)
|
||||
fun resolutionFor(mode: TransformMode): Resolution
|
||||
= when(mode)
|
||||
{
|
||||
R.id.display_mode_stretch_button -> return computeStrechedResolution()
|
||||
R.id.display_mode_zoom_button -> return computeZoomedResolution()
|
||||
else -> return computeNormalResolution()
|
||||
TransformMode.STRETCH -> strechedResolution
|
||||
TransformMode.ZOOM -> zoomedResolution
|
||||
TransformMode.FIT -> normalResolution
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeStrechedResolution(): Resolution
|
||||
{
|
||||
return Resolution(viewWidth, viewHeight)
|
||||
}
|
||||
private val strechedResolution get() = Resolution(viewWidth, viewHeight)
|
||||
|
||||
private fun computeZoomedResolution(): Resolution
|
||||
{
|
||||
if (viewHeight > viewWidth * contentAspect)
|
||||
private val zoomedResolution get() =
|
||||
if(viewHeight > viewWidth * contentAspect)
|
||||
{
|
||||
val zoomFactor = viewHeight / contentHeight
|
||||
return Resolution(contentWidth * zoomFactor, viewHeight)
|
||||
Resolution(contentWidth * zoomFactor, viewHeight)
|
||||
}
|
||||
else
|
||||
{
|
||||
val zoomFactor = viewWidth / contentWidth
|
||||
return Resolution(viewWidth, contentHeight * zoomFactor)
|
||||
Resolution(viewWidth, contentHeight * zoomFactor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeNormalResolution(): Resolution
|
||||
{
|
||||
if (viewHeight > viewWidth * contentAspect)
|
||||
{
|
||||
return Resolution(viewWidth, viewWidth * contentAspect)
|
||||
}
|
||||
private val normalResolution get() =
|
||||
if(viewHeight > viewWidth * contentAspect)
|
||||
Resolution(viewWidth, viewWidth * contentAspect)
|
||||
else
|
||||
{
|
||||
return Resolution(viewHeight / contentAspect, viewHeight)
|
||||
}
|
||||
}
|
||||
|
||||
Resolution(viewHeight / contentAspect, viewHeight)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,35 +1,23 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.stream
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.*
|
||||
import com.metallic.chiaki.common.LogManager
|
||||
import com.metallic.chiaki.session.StreamSession
|
||||
import com.metallic.chiaki.common.Preferences
|
||||
import com.metallic.chiaki.lib.*
|
||||
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
|
||||
val input = StreamInput(preferences)
|
||||
val input = StreamInput(application, preferences)
|
||||
val session = StreamSession(connectInfo, logManager, preferences.logVerbose, input)
|
||||
|
||||
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
|
@ -21,14 +6,19 @@ import android.content.Context
|
|||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import com.metallic.chiaki.R
|
||||
|
||||
class ButtonView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr)
|
||||
{
|
||||
private val haptics = ButtonHaptics(context)
|
||||
|
||||
var buttonPressed = false
|
||||
private set(value)
|
||||
{
|
||||
|
@ -36,6 +26,8 @@ class ButtonView @JvmOverloads constructor(
|
|||
field = value
|
||||
if(diff)
|
||||
{
|
||||
if(value)
|
||||
haptics.trigger()
|
||||
invalidate()
|
||||
buttonPressedCallback?.let { it(field) }
|
||||
}
|
||||
|
@ -61,16 +53,39 @@ class ButtonView @JvmOverloads constructor(
|
|||
{
|
||||
super.onDraw(canvas)
|
||||
val drawable = if(buttonPressed) drawablePressed else drawableIdle
|
||||
drawable?.setBounds(0, 0, width, height)
|
||||
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||
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
|
||||
{
|
||||
when(event.action)
|
||||
when(event.actionMasked)
|
||||
{
|
||||
MotionEvent.ACTION_DOWN -> buttonPressed = true
|
||||
MotionEvent.ACTION_UP -> buttonPressed = false
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
|
@ -31,6 +16,8 @@ class DPadView @JvmOverloads constructor(
|
|||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr)
|
||||
{
|
||||
private val haptics = ButtonHaptics(context)
|
||||
|
||||
enum class Direction {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
|
@ -86,7 +73,7 @@ class DPadView @JvmOverloads constructor(
|
|||
else
|
||||
drawable = dpadIdleDrawable
|
||||
|
||||
drawable?.setBounds(0, 0, width, height)
|
||||
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
|
||||
//drawable?.alpha = 127
|
||||
drawable?.draw(canvas)
|
||||
}
|
||||
|
@ -128,6 +115,8 @@ class DPadView @JvmOverloads constructor(
|
|||
|
||||
if(state != newState)
|
||||
{
|
||||
if(newState != null)
|
||||
haptics.trigger()
|
||||
state = newState
|
||||
invalidate()
|
||||
stateChangeCallback?.let { it(state) }
|
||||
|
|
|
@ -1,91 +1,99 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.databinding.FragmentControlsBinding
|
||||
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()
|
||||
private set(value)
|
||||
protected var ownControllerState = ControllerState()
|
||||
set(value)
|
||||
{
|
||||
val diff = field != value
|
||||
field = value
|
||||
if(diff)
|
||||
controllerStateCallback?.let { it(value) }
|
||||
ownControllerStateSubject.onNext(ownControllerState)
|
||||
}
|
||||
|
||||
var controllerStateCallback: ((ControllerState) -> Unit)? = null
|
||||
var onScreenControlsEnabled: LiveData<Boolean>? = null
|
||||
protected val ownControllerStateSubject: Subject<ControllerState>
|
||||
= BehaviorSubject.create<ControllerState>().also { it.onNext(ownControllerState) }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
= inflater.inflate(R.layout.fragment_controls, container, false)
|
||||
// to delay attaching to the touchpadView until it's available
|
||||
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?)
|
||||
{
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
dpadView.stateChangeCallback = this::dpadStateChanged
|
||||
crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
|
||||
moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
|
||||
pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
|
||||
boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
|
||||
l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
|
||||
r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
|
||||
optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
|
||||
shareButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_SHARE)
|
||||
psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
|
||||
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
||||
binding.dpadView.stateChangeCallback = this::dpadStateChanged
|
||||
binding.crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
|
||||
binding.moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
|
||||
binding.pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
|
||||
binding.boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
|
||||
binding.l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
|
||||
binding.r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
|
||||
binding.l3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L3)
|
||||
binding.r3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R3)
|
||||
binding.optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
|
||||
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 } }
|
||||
r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
||||
binding.l2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
||||
binding.r2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
||||
|
||||
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)
|
||||
leftY = quantizeStick(it.y)
|
||||
}}
|
||||
|
||||
rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
||||
binding.rightAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
|
||||
rightX = quantizeStick(it.x)
|
||||
rightY = quantizeStick(it.y)
|
||||
}}
|
||||
|
||||
onScreenControlsEnabled?.observe(this, Observer {
|
||||
onScreenControlsEnabled?.observe(viewLifecycleOwner, Observer {
|
||||
view.visibility = if(it) View.VISIBLE else View.GONE
|
||||
})
|
||||
}
|
||||
|
||||
private fun dpadStateChanged(direction: DPadView.Direction?)
|
||||
{
|
||||
controllerState = controllerState.copy().apply {
|
||||
ownControllerState = ownControllerState.copy().apply {
|
||||
buttons = ((buttons
|
||||
and ControllerState.BUTTON_DPAD_LEFT.inv()
|
||||
and ControllerState.BUTTON_DPAD_RIGHT.inv()
|
||||
|
@ -107,7 +115,7 @@ class TouchControlsFragment : Fragment()
|
|||
}
|
||||
|
||||
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
||||
controllerState = controllerState.copy().apply {
|
||||
ownControllerState = ownControllerState.copy().apply {
|
||||
buttons =
|
||||
if(pressed)
|
||||
buttons or buttonMask
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
|
@ -41,7 +26,7 @@ class TouchTracker
|
|||
if(pointerId == null)
|
||||
{
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
|
@ -21,49 +6,42 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.metallic.chiaki.R
|
||||
import com.metallic.chiaki.lib.ControllerState
|
||||
import kotlinx.android.synthetic.main.fragment_controls.*
|
||||
import com.metallic.chiaki.databinding.FragmentTouchpadOnlyBinding
|
||||
import io.reactivex.rxkotlin.Observables.combineLatest
|
||||
|
||||
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
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
= inflater.inflate(R.layout.fragment_touchpad_only, container, false)
|
||||
private var _binding: FragmentTouchpadOnlyBinding? = null
|
||||
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?)
|
||||
{
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
|
||||
|
||||
touchpadOnlyEnabled?.observe(this, Observer {
|
||||
touchpadOnlyEnabled?.observe(viewLifecycleOwner, Observer {
|
||||
view.visibility = if(it) View.VISIBLE else View.GONE
|
||||
})
|
||||
}
|
||||
|
||||
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
|
||||
controllerState = controllerState.copy().apply {
|
||||
ownControllerState = ownControllerState.copy().apply {
|
||||
buttons =
|
||||
if(pressed)
|
||||
buttons or buttonMask
|
||||
else
|
||||
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 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
|
||||
|
||||
package com.metallic.chiaki.touchcontrols
|
||||
|
||||
import android.view.View
|
||||
import kotlin.math.sqrt
|
||||
|
||||
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 length get() = sqrt(lengthSq)
|
||||
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"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_primary"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_pressed"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_primary"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_pressed"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</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"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_primary"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_pressed"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_primary"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="128dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="67.73333"
|
||||
android:viewportHeight="33.86667">
|
||||
android:viewportHeight="67.73334">
|
||||
<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:strokeWidth="23.7399"
|
||||
android:strokeWidth="5.54109"
|
||||
android:fillColor="@color/control_pressed"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</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