Compare commits
366 Commits
v0.2.2
...
05074ed4f0
| Author | SHA1 | Date | |
|---|---|---|---|
| 05074ed4f0 | |||
| 4e4ed58605 | |||
| 6a109fe03d | |||
| a783aab229 | |||
| 43dc2285b3 | |||
| eac8e3fb44 | |||
| 035d29fabc | |||
| ad2b10972c | |||
| e65e1f3ec4 | |||
| 10b86812cd | |||
| fe4c794f68 | |||
| 6115d748e3 | |||
| b0f266bb0a | |||
| 646c99feb5 | |||
| e8461d7059 | |||
| b2efd9f22f | |||
| 8e426b7fd6 | |||
| c13a65b204 | |||
| 503a24e003 | |||
| dfddd3d3d0 | |||
| 26e01bb7f8 | |||
| 5f88626ddf | |||
| e04bb29bb2 | |||
| c9690e028b | |||
| 8709f0bd8e | |||
| 13d7f33c37 | |||
| eac8592536 | |||
| 3e0feaa3e8 | |||
| 85023299d2 | |||
| 98cfb79961 | |||
| f628ec908c | |||
| 6a166a6ef7 | |||
| b10f7dd888 | |||
| 0e768e7768 | |||
| d59a37f185 | |||
| f0c6713d76 | |||
| 0eeac949ea | |||
| 04c0c1c0e2 | |||
| 03011d107f | |||
| c88003bea1 | |||
| 6c83373d72 | |||
| 888a1cf296 | |||
| ed6c01243d | |||
| 5945f78e97 | |||
| c58fca395b | |||
| 9203905ed8 | |||
| 259e0dacd0 | |||
| 0acd13f0f0 | |||
| cfdb948372 | |||
| 9b3130f363 | |||
| d10efc274c | |||
| 2e1b7fab53 | |||
| 91b92b2cc4 | |||
| 6e3ab111f3 | |||
| 7de0b1c00e | |||
| 8107743af2 | |||
| aeff82f625 | |||
| 935cb1c38b | |||
| acd5b7706b | |||
| 0b111d1012 | |||
| 211d8f37d8 | |||
| e2517a7786 | |||
| aaf5b4fecc | |||
| 173e251e9f | |||
| 3c0891e069 | |||
| 2dc3de43d1 | |||
| 1b332b5b3b | |||
| 6ef34afc6d | |||
| 1cd6b63f1e | |||
| 6fd17ee70e | |||
| 050d151c67 | |||
| 25ec569cd8 | |||
| 2dd9847566 | |||
| ef108f2e4a | |||
| 4894c2d627 | |||
| f0861a9a62 | |||
| 221cf89d10 | |||
| c54ef9b84a | |||
| 7aad272316 | |||
| 1f43353360 | |||
| 5b54566c89 | |||
| a9e3d331fc | |||
| 192086546a | |||
| a30b8f888d | |||
| 1e3fc9be3d | |||
| 2ec08c7f68 | |||
| 3adf9b0d00 | |||
| 6fba0f28db | |||
| 9bdc2c09e5 | |||
| 1eb95cde92 | |||
| 719e793860 | |||
| f0e68f7b48 | |||
| 72501bd0b3 | |||
| bd6aaa07c8 | |||
| 77a9f81a1d | |||
| 3eb88f66cf | |||
| 3fb76d59b2 | |||
| 72db2863d0 | |||
| 81ad0cf4db | |||
| b6f2a89e04 | |||
| e2c735b804 | |||
| 3f576ce3e5 | |||
| 1e51e8bb8b | |||
| ad48191b53 | |||
| d851f302cc | |||
| 09ba2122e7 | |||
| 9b16bf6e6f | |||
| ab6b5eefc0 | |||
| 993cd5ed1c | |||
| b733bb4154 | |||
| 2edbbc3ede | |||
| af3013ad67 | |||
| d02fe73952 | |||
| 32444d5a7e | |||
| b003b5e04b | |||
| 5d217a264a | |||
| 8d8c4d2da3 | |||
|
|
943a03bd2c | ||
|
|
680303cfa2 | ||
|
|
e16bbf8acf | ||
| 42ebbdba6d | |||
| a2b477a3dc | |||
| 3c7d5ad5ad | |||
| 4261e26f19 | |||
| a0ee4e1312 | |||
| 93630650fc | |||
| 36b20fa2dd | |||
| ae1c4dd3e6 | |||
| 69438f44b3 | |||
| aceaa01cdb | |||
| 7fe220a630 | |||
| f07599adf2 | |||
| 1b89b16705 | |||
| 0fb1148508 | |||
| 208145d288 | |||
| a6bd60077d | |||
| 387c20a708 | |||
| 1a9d34d347 | |||
| ed4ee53fdb | |||
| 4848a13fa0 | |||
| b9ebd506c8 | |||
| 249f49f7b3 | |||
| db3f5eb066 | |||
| c2f43e0096 | |||
| d81586d026 | |||
| 18a9419cef | |||
| 8a69083c19 | |||
| ba1b79f657 | |||
| 1a2415925f | |||
| 16a597183a | |||
| 2461430869 | |||
| 605201dbc8 | |||
| 214d3250fe | |||
| ea9c634a25 | |||
| 62095cb170 | |||
| aba85c70c1 | |||
| 98569cff69 | |||
| 952dfdc521 | |||
| 93724802d8 | |||
| e5f19e3b8b | |||
| 4479506ee0 | |||
| 359d4508b1 | |||
| ef9ba68790 | |||
| 53ce0d9e54 | |||
| 505921045e | |||
| 2c71e01e5a | |||
| c3cf84ee7d | |||
| 5787a8943d | |||
| 313e276ad6 | |||
| 310891bf16 | |||
| 8852356966 | |||
| 6243766ecc | |||
| 2d5e987fcc | |||
| bf851c2bd6 | |||
| 0327f254a2 | |||
| a0c7078593 | |||
| 0cbea9607e | |||
| e799d516ea | |||
| c630d8f091 | |||
| 2494fbb837 | |||
| cb2560d46f | |||
| 240ed5f859 | |||
| 0365730e0e | |||
| b9d5eab3ea | |||
| 16c84d59dc | |||
| 621c396407 | |||
| 3f2cc3d97a | |||
| ce927308c4 | |||
| df99eb0aab | |||
| e8473d4f5b | |||
| 1175d77c55 | |||
| 570f4ca7d9 | |||
| e4b9c8f1bc | |||
| ea6e7c5d8c | |||
| a462945c98 | |||
| 003d4d65e5 | |||
| 38fa5ab991 | |||
| e22e7b9c90 | |||
| fc6a8eae9d | |||
| 4dab811388 | |||
| 012dc5ec69 | |||
| f72f67342d | |||
| 283ac315d8 | |||
| 1a5b0f372d | |||
| de40e859d7 | |||
| 6140de8eea | |||
| a9fcbd7909 | |||
| a963153c2a | |||
| 8efb743b84 | |||
| f251d6b97b | |||
| 8a5a96d02c | |||
| fc0dd14b4d | |||
| 4d67c157f0 | |||
| a678ef70e7 | |||
| 87d320b6da | |||
| 6b7d3c4b7c | |||
| 8a99f8f6b1 | |||
| 4aa24cc0a1 | |||
| e309f3bbd0 | |||
| a757c45b84 | |||
| ed397e352f | |||
| cf6cec4d32 | |||
| c4422355e3 | |||
| dd947ecd39 | |||
| 968b59aaee | |||
| b9cb023306 | |||
| 031ef140f3 | |||
| 7376607475 | |||
| 1aea6ee588 | |||
| 12717ba25e | |||
| a1fa666cd3 | |||
| 30cfd67e28 | |||
| 068d156da3 | |||
| ce4ed9b0a9 | |||
| b8acbe7359 | |||
| c61485638b | |||
| fd20d5177d | |||
| 2a603e1e41 | |||
| e4d71c5a39 | |||
| 842a6ebe16 | |||
| 14ecc63944 | |||
| 18fb728973 | |||
| a11b75f1cb | |||
| 86abadd6bb | |||
| ab47b06fd6 | |||
| 640ffcb77e | |||
| 26b6abe66b | |||
| c00df84f2a | |||
| 2b7b7a10bc | |||
| 5b18b3d50d | |||
| be24afc8bf | |||
| 5332572b2e | |||
| 6551fda493 | |||
| e3d33f201c | |||
| ea3d550f64 | |||
| 21c1632233 | |||
| 1a66cadb53 | |||
| c8b1330244 | |||
| 4c5204598e | |||
| b8aedc842e | |||
| c1579cb106 | |||
| c1ff949346 | |||
| 3eea6c2ff9 | |||
| d1f826bdb5 | |||
| 5586fcff7a | |||
| f98b18affc | |||
| 28135244c3 | |||
| 47533c7512 | |||
| f79f35e2be | |||
| c43c4a9b24 | |||
| 81a47a12ec | |||
| 9e3a0a0f1d | |||
| 2d0426c0a3 | |||
| 7f366d3f3c | |||
| 3dd1e0461c | |||
| 8036a3a5be | |||
| 29a692de5f | |||
| 4f515d4733 | |||
| 2c28e95bd9 | |||
| e7d354d4c7 | |||
| 5b64506612 | |||
| f2135081ef | |||
| ae9b1a8215 | |||
| 0f5b3d62b1 | |||
| cf63408023 | |||
| 2bfcd52c8e | |||
| 95075c1664 | |||
| 55e639a61c | |||
| fb414c4764 | |||
| 2a53238cad | |||
| 59dfb606ad | |||
| e1271419eb | |||
| a6faa103b6 | |||
| 72de6a4fb5 | |||
| 5c7de2406a | |||
| a6ccc2cc24 | |||
| 7a132d96ca | |||
| 1e2fcf98c7 | |||
| 8fc3df0672 | |||
| 930370c1b5 | |||
| 25677c028e | |||
| 6444e9f1d5 | |||
| 94f9474e2a | |||
| 392606e165 | |||
| f8950d9fb3 | |||
| 6ab4b4062c | |||
| 31464e5025 | |||
| 1f166cbc44 | |||
| 0d8bf8dd12 | |||
| fdbf2534b7 | |||
| 8fd69c4d5a | |||
| 94c8224cf8 | |||
| 7bcccd5558 | |||
| bb987ed257 | |||
| 80f4104969 | |||
| 78ecd57aee | |||
| da66c3fc17 | |||
| 94b75f0eee | |||
| a798b72515 | |||
| 8bd7ef6cfe | |||
| d66a6790f9 | |||
| 50f488ae21 | |||
| 2a18715f77 | |||
| dd39fea3f8 | |||
| 650fcc9215 | |||
| c52bdbecf8 | |||
| 9171f42d09 | |||
| 82885b2bde | |||
| 805a701daa | |||
| 691a1efe16 | |||
| 4f39c995e0 | |||
| 7c5a9bccec | |||
| b8ccbd9722 | |||
| 3c8d942c67 | |||
| 9ef0b851a9 | |||
| 630da4a9e9 | |||
| df4bf316fb | |||
| dc1a17677e | |||
| 5ce8f74aa7 | |||
| 07954bf51b | |||
| b518ddbfb8 | |||
| e5aa20a446 | |||
| dc08515e1d | |||
| a49bce8292 | |||
| 0021d0d84d | |||
| b952b73738 | |||
| ec3ed96771 | |||
| d02a2e74b0 | |||
| 40cb344e0d | |||
| 8c32e42fe9 | |||
| cbceab7cff | |||
| a0864933de | |||
| df7bc4aede | |||
| 009ce28c04 | |||
| 5f087121a3 | |||
| 074381ff67 | |||
| 757e0be5ef | |||
| bb7adceeb7 | |||
| 15a401e681 | |||
| 976478d5bb | |||
| b3fcbfd4fe | |||
| 8ae4821bc8 | |||
| da767a8981 | |||
| ae793f480c | |||
| a039954f9e | |||
| 03a775ef1c |
4
.gitignore
vendored
@@ -52,7 +52,11 @@ app.*.map.json
|
|||||||
**/*.g.dart
|
**/*.g.dart
|
||||||
**/*.freezed.dart
|
**/*.freezed.dart
|
||||||
**/*.moxxy.dart
|
**/*.moxxy.dart
|
||||||
|
lib/i18n/*.dart
|
||||||
|
|
||||||
# Direnv
|
# Direnv
|
||||||
.envrc
|
.envrc
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
||||||
|
# Android artifacts
|
||||||
|
.android
|
||||||
|
|||||||
2
.gitlint
@@ -7,7 +7,7 @@ line-length=72
|
|||||||
[title-trailing-punctuation]
|
[title-trailing-punctuation]
|
||||||
[title-hard-tab]
|
[title-hard-tab]
|
||||||
[title-match-regex]
|
[title-match-regex]
|
||||||
regex=^(ui,service|service,xmpp|feat|test|refactor|xmpp|service|redux|ui|lint|style|docs|build|misc|flake|shared|meta|android|ios|release):.*$
|
regex=^(feat|fix|chore|refactor|docs|release|test)\((xmpp|service|ui|shared|meta|tests|i18n)+(,(xmpp|service|ui|shared|meta|tests|i18n))*\): .*$
|
||||||
|
|
||||||
|
|
||||||
[body-trailing-whitespace]
|
[body-trailing-whitespace]
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ An experimental XMPP client that tries to be as easy, modern and beautiful as po
|
|||||||
|
|
||||||
The code is also available on [codeberg](https://codeberg.org/moxxy/moxxyv2).
|
The code is also available on [codeberg](https://codeberg.org/moxxy/moxxyv2).
|
||||||
|
|
||||||
|
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="80" />](https://apt.izzysoft.de/fdroid/index/apk/org.moxxy.moxxyv2)
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||

|
[<img src="https://codeberg.org/moxxy/moxxyv2/raw/branch/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png" width="20%"></img>](./fastlane/metadata/android/en-US/images/phoneScreenshots/1.png)
|
||||||
|
[<img src="https://codeberg.org/moxxy/moxxyv2/raw/branch/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" width="20%"></img>](./fastlane/metadata/android/en-US/images/phoneScreenshots/2.png)
|
||||||
|
|
||||||
## Developing and Building
|
## Developing and Building
|
||||||
|
|
||||||
@@ -14,7 +17,8 @@ Clone using `git clone --recursive https://github.com/Polynomdivision/moxxyv2.gi
|
|||||||
|
|
||||||
In order to build Moxxy, you need to have [Flutter](https://docs.flutter.dev/get-started/install) set
|
In order to build Moxxy, you need to have [Flutter](https://docs.flutter.dev/get-started/install) set
|
||||||
up. If you are running NixOS or using Nix, you can also use the Flake at the root of the repository
|
up. If you are running NixOS or using Nix, you can also use the Flake at the root of the repository
|
||||||
by running `nix develop` to get a development shell including everything that is needed.
|
by running `nix develop` to get a development shell including everything that is needed. Note
|
||||||
|
that if you decide to use the Flake, `ANDROID_HOME` and `ANDROID_AVD_HOME` must be set to the respective directories.
|
||||||
|
|
||||||
Before building Moxxy, you need to generate all needed data classes. To do this, run
|
Before building Moxxy, you need to generate all needed data classes. To do this, run
|
||||||
`flutter pub get` to install all dependencies. Then run `flutter pub run build_runner build` to generate
|
`flutter pub get` to install all dependencies. Then run `flutter pub run build_runner build` to generate
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ analyzer:
|
|||||||
- "**/*.freezed.dart"
|
- "**/*.freezed.dart"
|
||||||
- "**/*.moxxy.dart"
|
- "**/*.moxxy.dart"
|
||||||
- "test/"
|
- "test/"
|
||||||
|
- "integration_test/"
|
||||||
|
- "lib/service/database/migrations/*.dart"
|
||||||
|
- "lib/i18n/*.dart"
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "me.polynom.moxxyv2"
|
applicationId "org.moxxy.moxxyv2"
|
||||||
|
|
||||||
// TODO: Remove once https://github.com/fluttercommunity/flutter_launcher_icons/pull/313 is merged
|
// TODO: Remove once https://github.com/fluttercommunity/flutter_launcher_icons/pull/313 is merged
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
|
|||||||
1
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-keep class net.sqlcipher.** { *; }
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.moxxyv2">
|
package="org.moxxy.moxxyv2">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.moxxyv2">
|
package="org.moxxy.moxxyv2">
|
||||||
<application
|
<application
|
||||||
android:label="Moxxy"
|
android:label="Moxxy"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
@@ -18,16 +18,26 @@
|
|||||||
to determine the Window background behind the Flutter UI. -->
|
to determine the Window background behind the Flutter UI. -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme" />
|
||||||
/>
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2"
|
android:value="2" />
|
||||||
/>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Allow receiving share intents for all kinds of things -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
@@ -36,6 +46,8 @@
|
|||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.moxxyv2
|
package org.moxxy.moxxyv2
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.moxxyv2">
|
package="org.moxxy.moxxyv2">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
|||||||
BIN
assets/fonts/RobotoMono-Regular.ttf
Normal file
256
assets/i18n/strings.i18n.json
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
{
|
||||||
|
"@@name": "English",
|
||||||
|
"global": {
|
||||||
|
"title": "Moxxy",
|
||||||
|
"moxxySubtitle": "An experiment into building a modern, easy and beautiful XMPP client.",
|
||||||
|
"dialogAccept": "Okay",
|
||||||
|
"dialogCancel": "Cancel",
|
||||||
|
"yes": "Yes",
|
||||||
|
"no": "No"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"permanent": {
|
||||||
|
"idle": "Idle",
|
||||||
|
"ready": "Ready to receive messages",
|
||||||
|
"connecting": "Connecting...",
|
||||||
|
"disconnect": "Disconnected",
|
||||||
|
"error": "Error"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"reply": "Reply",
|
||||||
|
"markAsRead": "Mark as read"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"messagesChannelName": "Messages",
|
||||||
|
"messagesChannelDescription": "The notification channel for received messages",
|
||||||
|
"warningChannelName": "Warnings",
|
||||||
|
"warningChannelDescription": "Warnings related to Moxxy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"image": "Image",
|
||||||
|
"video": "Video",
|
||||||
|
"audio": "Audio",
|
||||||
|
"file": "File",
|
||||||
|
"retracted": "The message has been retracted",
|
||||||
|
"retractedFallback": "A previous message has been retracted but your client does not support it"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"omemo": {
|
||||||
|
"couldNotPublish": "Could not publish the cryptographic identity to the server. This means that end-to-end encryption may not work.",
|
||||||
|
"notEncryptedForDevice": "This message was not encrypted for this device",
|
||||||
|
"invalidHmac": "Could not decrypt message",
|
||||||
|
"noDecryptionKey": "No decryption key available",
|
||||||
|
"messageInvalidAfixElement": "Invalid encrypted message"
|
||||||
|
},
|
||||||
|
"connection": {
|
||||||
|
"connectionTimeout": "Could not connect to server"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"saslFailed": "Invalid login credentials",
|
||||||
|
"startTlsFailed": "Failed to establish a secure connection",
|
||||||
|
"noConnection": "Failed to establish a connection",
|
||||||
|
"unspecified": "Unspecified error"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"unspecified": "Unknown error",
|
||||||
|
"fileUploadFailed": "The file upload failed",
|
||||||
|
"contactDoesntSupportOmemo": "The contact does not support encryption using OMEMO:2",
|
||||||
|
"fileDownloadFailed": "The file download failed",
|
||||||
|
"serviceUnavailable": "The message could not be delivered to the contact",
|
||||||
|
"remoteServerTimeout": "The message could not be delivered to the contact's server",
|
||||||
|
"remoteServerNotFound": "The message could not be delivered to the contact's server as it cannot be found",
|
||||||
|
"failedToEncrypt": "The message could not be encrypted",
|
||||||
|
"failedToEncryptFile": "The file could not be encrypted",
|
||||||
|
"failedToDecryptFile": "The file could not be decrypted",
|
||||||
|
"fileNotEncrypted": "The chat is encrypted but the file is not encrypted"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warnings": {
|
||||||
|
"message": {
|
||||||
|
"integrityCheckFailed": "Could not verify file integrity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pages": {
|
||||||
|
"intro": {
|
||||||
|
"noAccount": "Have no XMPP account? No worries, creating one is really easy.",
|
||||||
|
"loginButton": "Login",
|
||||||
|
"registerButton": "Register"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "Login",
|
||||||
|
"xmppAddress": "XMPP-Address",
|
||||||
|
"password": "Password",
|
||||||
|
"advancedOptions": "Advanced options",
|
||||||
|
"createAccount": "Create account on server"
|
||||||
|
},
|
||||||
|
"conversations": {
|
||||||
|
"speeddialNewChat": "New chat",
|
||||||
|
"speeddialJoinGroupchat": "Join groupchat",
|
||||||
|
"overlaySettings": "Settings",
|
||||||
|
"noOpenChats": "You have no open chats",
|
||||||
|
"startChat": "Start a chat",
|
||||||
|
"closeChat": "Close chat",
|
||||||
|
"closeChatBody": "Are you sure you want to close the chat with ${conversationTitle}?",
|
||||||
|
"markAsRead": "Mark as read"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"unencrypted": "Unencrypted",
|
||||||
|
"encrypted": "Encrypted",
|
||||||
|
"closeChat": "Close chat",
|
||||||
|
"closeChatConfirmTitle": "Close chat",
|
||||||
|
"closeChatConfirmSubtext": "Are you sure you want to close this chat?",
|
||||||
|
"blockUser": "Block user",
|
||||||
|
"online": "Online",
|
||||||
|
"retract": "Retract message",
|
||||||
|
"retractBody": "Are you sure you want to retract the message? Keep in mind that this is only a request that the client does not have to honour.",
|
||||||
|
"forward": "Forward",
|
||||||
|
"edit": "Edit",
|
||||||
|
"quote": "Quote",
|
||||||
|
"copy": "Copy content"
|
||||||
|
},
|
||||||
|
"addcontact": {
|
||||||
|
"title": "Add new contact",
|
||||||
|
"xmppAddress": "XMPP-Address",
|
||||||
|
"subtitle": "You can add a contact either by typing in their XMPP address or by scanning their QR code",
|
||||||
|
"buttonAddToContact": "Add to contacts"
|
||||||
|
},
|
||||||
|
"newconversation": {
|
||||||
|
"title": "Start new chat",
|
||||||
|
"addContact": "Add contact",
|
||||||
|
"createGroupchat": "Create groupchat"
|
||||||
|
},
|
||||||
|
"crop": {
|
||||||
|
"setProfilePicture": "Set as profile picture"
|
||||||
|
},
|
||||||
|
"shareselection": {
|
||||||
|
"shareWith": "Share with...",
|
||||||
|
"confirmTitle": "Send file",
|
||||||
|
"confirmBody": "One or more chats are unencrypted. This means that the file will be leaked to the server. Do you still want to continue?"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"self": {
|
||||||
|
"devices": "Devices"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"muteChatTooltip": "Mute chat",
|
||||||
|
"unmuteChatTooltip": "Unmute chat",
|
||||||
|
"muteChat": "Mute",
|
||||||
|
"unmuteChat": "Unmute",
|
||||||
|
"devices": "Devices"
|
||||||
|
},
|
||||||
|
"owndevices": {
|
||||||
|
"title": "Own Devices",
|
||||||
|
"thisDevice": "This device",
|
||||||
|
"otherDevices": "Other devices",
|
||||||
|
"deleteDeviceConfirmTitle": "Delete device",
|
||||||
|
"deleteDeviceConfirmBody": "This means that contacts will not be able to encrypt for that device. Continue?",
|
||||||
|
"recreateOwnSessions": "Rebuild sessions",
|
||||||
|
"recreateOwnSessionsConfirmTitle": "Recreate own sessions?",
|
||||||
|
"recreateOwnSessionsConfirmBody": "This will recreate the cryptographic sessions with your own devices. Use only if your own devices throw decryption errors.",
|
||||||
|
"recreateOwnDevice": "Recreate device",
|
||||||
|
"recreateOwnDeviceConfirmTitle": "Recreate own device?",
|
||||||
|
"recreateOwnDeviceConfirmBody": "This will recreate this device's cryptographic identity. It will take some time. If contacts verified your device, they will have to do it again. Continue?"
|
||||||
|
},
|
||||||
|
"devices": {
|
||||||
|
"title": "Devices",
|
||||||
|
"recreateSessions": "Rebuild sessions",
|
||||||
|
"recreateSessionsConfirmTitle": "Rebuild sessions?",
|
||||||
|
"recreateSessionsConfirmBody": "This will recreate the cryptographic sessions with your own devices. Use only if your own devices throw decryption errors."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blocklist": {
|
||||||
|
"title": "Blocklist",
|
||||||
|
"noUsersBlocked": "You have no users blocked",
|
||||||
|
"unblockAll": "Unblock all",
|
||||||
|
"unblockAllConfirmTitle": "Are you sure?",
|
||||||
|
"unblockAllConfirmBody": "Are you sure you want to unblock all users?",
|
||||||
|
"unblockJidConfirmTitle": "Unblock ${jid}?",
|
||||||
|
"unblockJidConfirmBody": "Are you sure you want to unblock ${jid}? You will receive messages from this user again."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Settings",
|
||||||
|
"conversationsSection": "Conversations",
|
||||||
|
"accountSection": "Account",
|
||||||
|
"signOut": "Sign out",
|
||||||
|
"signOutConfirmTitle": "Sign Out",
|
||||||
|
"signOutConfirmBody": "You are about to sign out. Proceed?",
|
||||||
|
"miscellaneousSection": "Miscellaneous",
|
||||||
|
"debuggingSection": "Debugging"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "About",
|
||||||
|
"licensed": "Licensed under GPL3",
|
||||||
|
"viewSourceCode": "View source code"
|
||||||
|
},
|
||||||
|
"appearance": {
|
||||||
|
"title": "Appearance",
|
||||||
|
"languageSection": "Language",
|
||||||
|
"language": "App language",
|
||||||
|
"languageSubtext": "Currently selected: $selectedLanguage",
|
||||||
|
"systemLanguage": "Default language"
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"title": "Open-Source Licenses",
|
||||||
|
"licensedUnder": "Licensed under $license"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"title": "Chat",
|
||||||
|
"appearance": "Appearance",
|
||||||
|
"selectBackgroundImage": "Select background image",
|
||||||
|
"selectBackgroundImageDescription": "This image will be the background of all your chats",
|
||||||
|
"removeBackgroundImage": "Remove background image",
|
||||||
|
"removeBackgroundImageConfirmTitle": "Remove background image",
|
||||||
|
"removeBackgroundImageConfirmBody": "Are you sure you want to remove your conversation background image?",
|
||||||
|
"newChatsSection": "New Conversations",
|
||||||
|
"newChatsMuteByDefault": "Mute new chats by default",
|
||||||
|
"newChatsE2EE": "Enable end-to-end encryption by default. WARNING: Experimental"
|
||||||
|
},
|
||||||
|
"debugging": {
|
||||||
|
"title": "Debugging options",
|
||||||
|
"generalSection": "General",
|
||||||
|
"generalEnableDebugging": "Enable debugging",
|
||||||
|
"generalEncryptionPassword": "Encryption password",
|
||||||
|
"generalEncryptionPasswordSubtext": "The logs may contain sensitive information so pick a strong passphrase",
|
||||||
|
"generalLoggingIp": "Logging IP",
|
||||||
|
"generalLoggingIpSubtext": "The IP the logs should be sent to",
|
||||||
|
"generalLoggingPort": "Logging Port",
|
||||||
|
"generalLoggingPortSubtext": "The IP the logs should be sent to"
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"title": "Network",
|
||||||
|
"automaticDownloadsSection": "Automatic Downloads",
|
||||||
|
"automaticDownloadsText": "Moxxy will automatically download files on...",
|
||||||
|
"automaticDownloadsMaximumSize": "Maximum Download Size",
|
||||||
|
"automaticDownloadsMaximumSizeSubtext": "The maximum file size for a file to be automatically downloaded",
|
||||||
|
"wifi": "Wifi",
|
||||||
|
"mobileData": "Mobile data"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Privacy",
|
||||||
|
"generalSection": "General",
|
||||||
|
"showContactRequests": "Show contact requests",
|
||||||
|
"showContactRequestsSubtext": "This will show people who added you to their contact list but sent no message yet",
|
||||||
|
"profilePictureVisibility": "Make profile picture public",
|
||||||
|
"profilePictureVisibilitSubtext": "If enabled, everyone can see your profile picture. If disabled, only users on your contact list can see your profile picture.",
|
||||||
|
"autoAcceptSubscriptionRequests": "Auto-accept subscription requests",
|
||||||
|
"autoAcceptSubscriptionRequestsSubtext": "If enabled, subscription requests will be automatically accepted if the user is in the contact list.",
|
||||||
|
"conversationsSection": "Conversation",
|
||||||
|
"sendChatMarkers": "Send chat markers",
|
||||||
|
"sendChatMarkersSubtext": "This will tell your conversation partner if you received or read a message",
|
||||||
|
"sendChatStates": "Send chat states",
|
||||||
|
"sendChatStatesSubtext": "This will show your conversation partner if you are typing or looking at the chat",
|
||||||
|
"redirectsSection": "Redirects",
|
||||||
|
"redirectText": "This will redirect $serviceName links that you tap to a proxy service, e.g. $exampleProxy",
|
||||||
|
"currentlySelected": "Currently selected: $proxy",
|
||||||
|
"redirectsTitle": "$serviceName Redirect",
|
||||||
|
"cannotEnableRedirect": "Cannot enable $serviceName redirects",
|
||||||
|
"cannotEnableRedirectSubtext": "You must first set a proxy service to redirect to. To do so, tap the field next to the switch.",
|
||||||
|
"urlEmpty": "URL cannot be empty",
|
||||||
|
"urlInvalid": "Invalid URL",
|
||||||
|
"redirectDialogTitle": "$serviceName Redirect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
256
assets/i18n/strings_de.i18n.json
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
{
|
||||||
|
"@@name": "Deutsch",
|
||||||
|
"global": {
|
||||||
|
"title": "Moxxy",
|
||||||
|
"moxxySubtitle": "Ein Experiment im Entwickeln eines modernen, einfachen und schönen XMPP-Clients.",
|
||||||
|
"dialogAccept": "Okay",
|
||||||
|
"dialogCancel": "Abbrechen",
|
||||||
|
"yes": "Ja",
|
||||||
|
"no": "Nein"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"permanent": {
|
||||||
|
"idle": "Bereit",
|
||||||
|
"ready": "Bereit zum Nachrichtenempfang",
|
||||||
|
"connecting": "Verbinde...",
|
||||||
|
"disconnect": "Keine Verbindung",
|
||||||
|
"error": "Fehler"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"reply": "Antworten",
|
||||||
|
"markAsRead": "Als gelesen markieren"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"messagesChannelName": "Nachrichten",
|
||||||
|
"messagesChannelDescription": "Empfangene Nachrichten",
|
||||||
|
"warningChannelName": "Warnungen",
|
||||||
|
"warningChannelDescription": "Warnungen im Bezug auf Moxxy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"image": "Bild",
|
||||||
|
"video": "Video",
|
||||||
|
"audio": "Audio",
|
||||||
|
"file": "Datei",
|
||||||
|
"retracted": "Die Nachricht wurde zurückgezogen",
|
||||||
|
"retractedFallback": "Eine vorherige Nachricht wurde zurückgezogen. Dein Client unterstüzt dies jedoch nicht"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"omemo": {
|
||||||
|
"couldNotPublish": "Konnte die kryptographische Identität nicht auf dem Server veröffentlichen. Ende-zu-Ende-Verschlüsselung funktioniert eventuell nicht.",
|
||||||
|
"notEncryptedForDevice": "Die Nachricht wurde nicht für dieses Gerät verschlüsselt",
|
||||||
|
"invalidHmac": "Die Nachricht konnte nicht entschlüsselt werden",
|
||||||
|
"noDecryptionKey": "Kein Schlüssel zum Entschlüsseln vorhanden",
|
||||||
|
"messageInvalidAfixElement": "Ungültige verschlüsselte Nachricht"
|
||||||
|
},
|
||||||
|
"connection": {
|
||||||
|
"connectionTimeout": "Verbindung zum Server nicht möglich"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"saslFailed": "Ungültige Logindaten",
|
||||||
|
"startTlsFailed": "Konnte keine sichere Verbindung zum Server aufbauen",
|
||||||
|
"noConnection": "Konnte keine Verbindung zum Server aufbauen",
|
||||||
|
"unspecified": "Unbestimmter Fehler"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"unspecified": "Unbekannter Fehler",
|
||||||
|
"fileUploadFailed": "Das Hochladen der Datei ist fehlgeschlagen",
|
||||||
|
"contactDoesntSupportOmemo": "Der Kontakt unterstützt Verschlüsselung mit OMEMO:2 nicht",
|
||||||
|
"fileDownloadFailed": "Das Herunterladen der Datei ist fehlgeschlagen",
|
||||||
|
"serviceUnavailable": "Die Nachricht konnte nicht gesendet werden",
|
||||||
|
"remoteServerTimeout": "Die Nachricht konnte nicht zugestellt werden",
|
||||||
|
"remoteServerNotFound": "Die Nachricht konnte nicht gesendet werden, da der Empfängerserver unbekannt ist",
|
||||||
|
"failedToEncrypt": "Die Nachricht konnte nicht verschlüsselt werden",
|
||||||
|
"failedToEncryptFile": "Die Datei konnte nicht verschlüsselt werden",
|
||||||
|
"failedToDecryptFile": "Die Datei konnte nicht entschlüsselt werden",
|
||||||
|
"fileNotEncrypted": "Der Chat ist verschlüsselt, aber die Datei wurde unverschlüsselt übertragen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warnings": {
|
||||||
|
"message": {
|
||||||
|
"integrityCheckFailed": "Konnte Integrität der Datei nicht überprüfen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pages": {
|
||||||
|
"intro": {
|
||||||
|
"noAccount": "Kein XMPP-Account vorhanden? Einen zu erstellen ist sehr einfach.",
|
||||||
|
"loginButton": "Einloggen",
|
||||||
|
"registerButton": "Registrieren"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "Login",
|
||||||
|
"xmppAddress": "XMPP-Adresse",
|
||||||
|
"password": "Passwort",
|
||||||
|
"advancedOptions": "Fortgeschrittene Optionen",
|
||||||
|
"createAccount": "Account auf dem Server erstellen"
|
||||||
|
},
|
||||||
|
"conversations": {
|
||||||
|
"speeddialNewChat": "Neuer chat",
|
||||||
|
"speeddialJoinGroupchat": "Gruppenchat beitreten",
|
||||||
|
"overlaySettings": "Einstellungen",
|
||||||
|
"noOpenChats": "Du hast keine offenen chats",
|
||||||
|
"startChat": "Einen chat anfangen",
|
||||||
|
"closeChat": "Chat schließen",
|
||||||
|
"closeChatBody": "Bist du dir sicher, dass du den Chat mit ${conversationTitle} schließen möchtest?",
|
||||||
|
"markAsRead": "Als gelesen markieren"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"unencrypted": "Unverschlüsselt",
|
||||||
|
"encrypted": "Verschlüsselt",
|
||||||
|
"closeChat": "Chat schließen",
|
||||||
|
"closeChatConfirmTitle": "Chat schließen",
|
||||||
|
"closeChatConfirmSubtext": "Bist Du dir sicher, dass du den Chat schließen möchtest?",
|
||||||
|
"blockUser": "Nutzer blockieren",
|
||||||
|
"online": "Online",
|
||||||
|
"retract": "Nachricht löschen",
|
||||||
|
"retractBody": "Bist du dir sicher, dass du die Nachricht löschen willst? Bedenke, dass dies nur eine Bitte ist, die dein gegenüber nicht beachten muss.",
|
||||||
|
"forward": "Weiterleiten",
|
||||||
|
"edit": "Bearbeiten",
|
||||||
|
"quote": "Zitieren",
|
||||||
|
"copy": "Inhalt kopieren"
|
||||||
|
},
|
||||||
|
"addcontact": {
|
||||||
|
"title": "Neuen Kontakt hinzufügen",
|
||||||
|
"xmppAddress": "XMPP-Adresse",
|
||||||
|
"subtitle": "Du kannst einen Kontakt hinzufügen, indem Du entweder die XMPP-Adresse eingibst oder den QR-Code deines Kontaktes scannst",
|
||||||
|
"buttonAddToContact": "Kontakt hinzufügen"
|
||||||
|
},
|
||||||
|
"newconversation": {
|
||||||
|
"title": "Neuer chat",
|
||||||
|
"addContact": "Kontakt hinzufügen",
|
||||||
|
"createGroupchat": "Gruppenchat erstellen"
|
||||||
|
},
|
||||||
|
"crop": {
|
||||||
|
"setProfilePicture": "Als Profilbild festlegen"
|
||||||
|
},
|
||||||
|
"shareselection": {
|
||||||
|
"shareWith": "Teilen mit...",
|
||||||
|
"confirmTitle": "Dateien senden?",
|
||||||
|
"confirmBody": "Einer oder mehr Chats sind unverschlüsselt. Das bedeutet, dass die Dateien dem Server unverschlüsselt vorliegen. Dateien trotzdem senden?"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"self": {
|
||||||
|
"devices": "Geräte"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"muteChatTooltip": "Chat stummschalten",
|
||||||
|
"unmuteChatTooltip": "Chat lautstellen",
|
||||||
|
"muteChat": "Stummschalten",
|
||||||
|
"unmuteChat": "Lautstellen",
|
||||||
|
"devices": "Geräte"
|
||||||
|
},
|
||||||
|
"owndevices": {
|
||||||
|
"title": "Eigene Geräte",
|
||||||
|
"thisDevice": "Dieses Gerät",
|
||||||
|
"otherDevices": "Andere Geräte",
|
||||||
|
"deleteDeviceConfirmTitle": "Gerät löschen",
|
||||||
|
"deleteDeviceConfirmBody": "Das bedeutet, dass Kontakte für dieses Gerät nichtmehr verschlüsseln können. Fortfahren?",
|
||||||
|
"recreateOwnSessions": "Sessions neuerstellen",
|
||||||
|
"recreateOwnSessionsConfirmTitle": "Eigene Sessions neuerstellen?",
|
||||||
|
"recreateOwnSessionsConfirmBody": "Das wird alle kryptographischen Sessions mit den eigenen Geräten neuerstellen. Verwende dies nur, wenn deine eigenen Geräte Entschlüsselungsfehler erzeugen.",
|
||||||
|
"recreateOwnDevice": "Gerät neuerstellen",
|
||||||
|
"recreateOwnDeviceConfirmTitle": "Gerät neuerstellen?",
|
||||||
|
"recreateOwnDeviceConfirmBody": "Das wird die kryptographische Identität dieses Geräts neu erstellen. Wenn Kontakte die kryptographische Indentität verifiziert haben, dann müssen diese es erneut tun. Fortfahren?"
|
||||||
|
},
|
||||||
|
"devices": {
|
||||||
|
"title": "Devices",
|
||||||
|
"recreateSessions": "Rebuild sessions",
|
||||||
|
"recreateSessionsConfirmTitle": "Rebuild sessions?",
|
||||||
|
"recreateSessionsConfirmBody": "This will recreate the cryptographic sessions with your own devices. Use only if your own devices throw decryption errors."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"blocklist": {
|
||||||
|
"title": "Blockliste",
|
||||||
|
"noUsersBlocked": "Du hast niemanden blockiert",
|
||||||
|
"unblockAll": "Alle entblocken",
|
||||||
|
"unblockAllConfirmTitle": "Alle entblocken",
|
||||||
|
"unblockAllConfirmBody": "Bist Du dir sicher, dass du alle geblockten Personen entblocken möchtest?",
|
||||||
|
"unblockJidConfirmTitle": "${jid} entblocken?",
|
||||||
|
"unblockJidConfirmBody": "Bist du dir sicher, dass du ${jid} entblocken möchtest? Du wirst wieder Nachrichten von dieser Person erhalten können."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Einstellungen",
|
||||||
|
"conversationsSection": "Unterhaltungen",
|
||||||
|
"accountSection": "Account",
|
||||||
|
"signOut": "Abmelden",
|
||||||
|
"signOutConfirmTitle": "Abmelden",
|
||||||
|
"signOutConfirmBody": "Du bist dabei dich abzumelden. Fortfahren?",
|
||||||
|
"miscellaneousSection": "Unterschiedlich",
|
||||||
|
"debuggingSection": "Debugging"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "Über",
|
||||||
|
"licensed": "Lizensiert unter GPL3",
|
||||||
|
"viewSourceCode": "Quellcode anschauen"
|
||||||
|
},
|
||||||
|
"appearance": {
|
||||||
|
"title": "Aussehen",
|
||||||
|
"languageSection": "Sprache",
|
||||||
|
"language": "Appsprache",
|
||||||
|
"languageSubtext": "Aktuell ausgewählt: $selectedLanguage",
|
||||||
|
"systemLanguage": "Systemsprache"
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"title": "Open-Source Lizenzen",
|
||||||
|
"licensedUnder": "Lizensiert unter $license"
|
||||||
|
},
|
||||||
|
"conversation": {
|
||||||
|
"title": "Chat",
|
||||||
|
"appearance": "Aussehen",
|
||||||
|
"selectBackgroundImage": "Hintergrundbild auswählen",
|
||||||
|
"selectBackgroundImageDescription": "Dieses Bild wird als Hintergrundbild in allen Chats verwendet",
|
||||||
|
"removeBackgroundImage": "Hintergrundbild entfernen",
|
||||||
|
"removeBackgroundImageConfirmTitle": "Hintergrundbild entfernen",
|
||||||
|
"removeBackgroundImageConfirmBody": "Bist Du dir sicher, dass Du das Hintergrundbild entfernen möchtest?",
|
||||||
|
"newChatsSection": "Neue Chats",
|
||||||
|
"newChatsMuteByDefault": "Neue Chats standardmäßig stummschalten",
|
||||||
|
"newChatsE2EE": "Ende-zu-Ende-Verschlüsselung standardmäßig aktivieren. WARNUNG: Experimentell"
|
||||||
|
},
|
||||||
|
"debugging": {
|
||||||
|
"title": "Debuggingoptionen",
|
||||||
|
"generalSection": "Generell",
|
||||||
|
"generalEnableDebugging": "Debugging einschalten",
|
||||||
|
"generalEncryptionPassword": "Verschlüsselungspasswort",
|
||||||
|
"generalEncryptionPasswordSubtext": "Die Logs enthalten eventuell sensible Daten. Wähle also daher eine starke Passphrase",
|
||||||
|
"generalLoggingIp": "Logging-IP",
|
||||||
|
"generalLoggingIpSubtext": "Die IP-Adresse an die die Logs gesendet werden",
|
||||||
|
"generalLoggingPort": "Logging-Port",
|
||||||
|
"generalLoggingPortSubtext": "Der Port an den die Logs gesendet werden"
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"title": "Netzwerk",
|
||||||
|
"automaticDownloadsSection": "Automatische Downloads",
|
||||||
|
"automaticDownloadsText": "Moxxy läd Dateien automatisch herunter, wenn verbunden mit...",
|
||||||
|
"automaticDownloadsMaximumSize": "Maximale Downloadgröße",
|
||||||
|
"automaticDownloadsMaximumSizeSubtext": "Die maximale Dateigröße, die automatisch heruntergeladen werden soll",
|
||||||
|
"wifi": "Wifi",
|
||||||
|
"mobileData": "Mobile Daten"
|
||||||
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Privatsphäre",
|
||||||
|
"generalSection": "Generell",
|
||||||
|
"showContactRequests": "Kontaktanfragen zeigen",
|
||||||
|
"showContactRequestsSubtext": "Dies zeigt Personen in der Chatübersicht an, die Dich zu ihrer Kontaktliste hinzugefügt haben, aber noch keine Nachricht gesendet haben",
|
||||||
|
"profilePictureVisibility": "Öffentliches Profilbild",
|
||||||
|
"profilePictureVisibilitSubtext": "Wenn aktiviert, dann kann jeder Dein Profilbild sehen. Wenn deaktiviert, dann können nur Personen aus deiner Kontaktliste kein Profilbild sehen",
|
||||||
|
"autoAcceptSubscriptionRequests": "Subscriptionanfragen automatisch annehmen",
|
||||||
|
"autoAcceptSubscriptionRequestsSubtext": "Wenn aktiviert, dann werden Subscriptionanfragen automatisch angenommen, wenn die Person in deiner Kontaktliste ist",
|
||||||
|
"conversationsSection": "Unterhaltungen",
|
||||||
|
"sendChatMarkers": "Chatmarker senden",
|
||||||
|
"sendChatMarkersSubtext": "Dies teilt Deinem Gesprächspartner mit, ob du Nachrichten empfangen oder gelesen hast",
|
||||||
|
"sendChatStates": "Chatstates senden",
|
||||||
|
"sendChatStatesSubtext": "Dies teilt Deinem Gesprächspartner mit, ob du gerade im Chat aktiv bist oder schreibst",
|
||||||
|
"redirectsSection": "Weiterleitungen",
|
||||||
|
"redirectText": "Dies leitet Links von $serviceName, die du öffnest, an einen Proxydienst weiter, wie zum Beispiel $exampleProxy",
|
||||||
|
"currentlySelected": "Aktuell ausgewählt: $proxy",
|
||||||
|
"redirectsTitle": "${serviceName}weiterleitung",
|
||||||
|
"cannotEnableRedirect": "Kann ${serviceName}weiterleitung nicht aktivieren",
|
||||||
|
"cannotEnableRedirectSubtext": "Du must zuerst einen Proxydienst auswählen. Dazu berühre das Feld neben dem Schalter.",
|
||||||
|
"urlEmpty": "URL kann nicht leer sein",
|
||||||
|
"urlInvalid": "Ungültige URL",
|
||||||
|
"redirectDialogTitle": "${serviceName}weiterleitung"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
build.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
slang_build_runner:
|
||||||
|
options:
|
||||||
|
input_directory: assets/i18n
|
||||||
|
output_directory: lib/i18n
|
||||||
12
flake.lock
generated
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1649676176,
|
"lastModified": 1667395993,
|
||||||
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -17,11 +17,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1660551188,
|
"lastModified": 1669165918,
|
||||||
"narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=",
|
"narHash": "sha256-hIVruk2+0wmw/Kfzy11rG3q7ev3VTi/IKVODeHcVjFo=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65",
|
"rev": "3b400a525d92e4085e46141ff48cbf89fd89739e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -44,9 +44,7 @@
|
|||||||
ripgrep # General utilities
|
ripgrep # General utilities
|
||||||
];
|
];
|
||||||
|
|
||||||
ANDROID_HOME = "${android.androidsdk}/libexec/android-sdk";
|
|
||||||
JAVA_HOME = pinnedJDK;
|
JAVA_HOME = pinnedJDK;
|
||||||
ANDROID_AVD_HOME = (toString ./.) + "/.android/avd";
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
67
integration_test/backoff_test.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||||
|
import 'package:moxxyv2/service/connectivity.dart';
|
||||||
|
import 'package:moxxyv2/service/moxxmpp/reconnect.dart';
|
||||||
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
class StubConnectivityService extends ConnectivityService {
|
||||||
|
StubConnectivityService() : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConnectivityResult get currentState => ConnectivityResult.wifi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Logger.root.level = Level.ALL;
|
||||||
|
Logger.root.onRecord.listen((record) {
|
||||||
|
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||||
|
});
|
||||||
|
final log = Logger('FailureReconnectionTest');
|
||||||
|
GetIt.I.registerSingleton<ConnectivityService>(StubConnectivityService());
|
||||||
|
|
||||||
|
test('Failing an awaited connection with MoxxyReconnectionPolicy', () async {
|
||||||
|
var errors = 0;
|
||||||
|
final connection = XmppConnection(
|
||||||
|
MoxxyReconnectionPolicy(maxBackoffTime: 1),
|
||||||
|
TCPSocketWrapper(false),
|
||||||
|
);
|
||||||
|
connection.registerFeatureNegotiators([
|
||||||
|
StartTlsNegotiator(),
|
||||||
|
]);
|
||||||
|
connection.registerManagers([
|
||||||
|
DiscoManager(),
|
||||||
|
RosterManager(),
|
||||||
|
PingManager(),
|
||||||
|
MessageManager(),
|
||||||
|
PresenceManager('http://moxxmpp.example'),
|
||||||
|
]);
|
||||||
|
connection.asBroadcastStream().listen((event) {
|
||||||
|
if (event is ConnectionStateChangedEvent) {
|
||||||
|
if (event.state == XmppConnectionState.error) {
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.setConnectionSettings(
|
||||||
|
ConnectionSettings(
|
||||||
|
jid: JID.fromString('testuser@no-sasl.badxmpp.eu'),
|
||||||
|
password: 'abc123',
|
||||||
|
useDirectTLS: true,
|
||||||
|
allowPlainAuth: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await connection.connectAwaitable();
|
||||||
|
log.info('Connection failed as expected');
|
||||||
|
expect(result.success, false);
|
||||||
|
expect(errors, 1);
|
||||||
|
|
||||||
|
log.info('Waiting 20 seconds for unexpected reconnections');
|
||||||
|
await Future.delayed(const Duration(seconds: 20));
|
||||||
|
expect(errors, 1);
|
||||||
|
}, timeout: Timeout.factor(2));
|
||||||
|
}
|
||||||
34
ios/.gitignore
vendored
@@ -1,34 +0,0 @@
|
|||||||
**/dgph
|
|
||||||
*.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
*.moved-aside
|
|
||||||
*.pbxuser
|
|
||||||
*.perspectivev3
|
|
||||||
**/*sync/
|
|
||||||
.sconsign.dblite
|
|
||||||
.tags*
|
|
||||||
**/.vagrant/
|
|
||||||
**/DerivedData/
|
|
||||||
Icon?
|
|
||||||
**/Pods/
|
|
||||||
**/.symlinks/
|
|
||||||
profile
|
|
||||||
xcuserdata
|
|
||||||
**/.generated/
|
|
||||||
Flutter/App.framework
|
|
||||||
Flutter/Flutter.framework
|
|
||||||
Flutter/Flutter.podspec
|
|
||||||
Flutter/Generated.xcconfig
|
|
||||||
Flutter/ephemeral/
|
|
||||||
Flutter/app.flx
|
|
||||||
Flutter/app.zip
|
|
||||||
Flutter/flutter_assets/
|
|
||||||
Flutter/flutter_export_environment.sh
|
|
||||||
ServiceDefinitions.json
|
|
||||||
Runner/GeneratedPluginRegistrant.*
|
|
||||||
|
|
||||||
# Exceptions to above rules.
|
|
||||||
!default.mode1v3
|
|
||||||
!default.mode2v3
|
|
||||||
!default.pbxuser
|
|
||||||
!default.perspectivev3
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>App</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>io.flutter.flutter.app</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>App</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>9.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "Generated.xcconfig"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include "Generated.xcconfig"
|
|
||||||
@@ -1,481 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 50;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = "";
|
|
||||||
dstSubfolderSpec = 10;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
name = "Embed Frameworks";
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
|
||||||
);
|
|
||||||
name = Flutter;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
97C146E51CF9000F007C117D = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9740EEB11CF90186004384FC /* Flutter */,
|
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
97C146EF1CF9000F007C117D /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
97C146F01CF9000F007C117D /* Runner */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
|
||||||
97C147021CF9000F007C117D /* Info.plist */,
|
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
|
||||||
);
|
|
||||||
path = Runner;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
|
||||||
buildPhases = (
|
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = Runner;
|
|
||||||
productName = Runner;
|
|
||||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 1300;
|
|
||||||
ORGANIZATIONNAME = "";
|
|
||||||
TargetAttributes = {
|
|
||||||
97C146ED1CF9000F007C117D = {
|
|
||||||
CreatedOnToolsVersion = 7.3.1;
|
|
||||||
LastSwiftMigration = 1100;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
|
||||||
compatibilityVersion = "Xcode 9.3";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = 97C146E51CF9000F007C117D;
|
|
||||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
97C146ED1CF9000F007C117D /* Runner */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "Thin Binary";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
|
||||||
};
|
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "Run Script";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
97C146FB1CF9000F007C117D /* Base */,
|
|
||||||
);
|
|
||||||
name = Main.storyboard;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
97C147001CF9000F007C117D /* Base */,
|
|
||||||
);
|
|
||||||
name = LaunchScreen.storyboard;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXVariantGroup section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
SDKROOT = iphoneos;
|
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
|
||||||
VALIDATE_PRODUCT = YES;
|
|
||||||
};
|
|
||||||
name = Profile;
|
|
||||||
};
|
|
||||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = assets/images/icon_ios.png;
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
|
||||||
ENABLE_BITCODE = NO;
|
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.moxxyv2;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
|
||||||
};
|
|
||||||
name = Profile;
|
|
||||||
};
|
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
SDKROOT = iphoneos;
|
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
97C147041CF9000F007C117D /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
SDKROOT = iphoneos;
|
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
|
||||||
VALIDATE_PRODUCT = YES;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
97C147061CF9000F007C117D /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = assets/images/icon_ios.png;
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
|
||||||
ENABLE_BITCODE = NO;
|
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.moxxyv2;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
97C147071CF9000F007C117D /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = assets/images/icon_ios.png;
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
|
||||||
ENABLE_BITCODE = NO;
|
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.moxxyv2;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
97C147031CF9000F007C117D /* Debug */,
|
|
||||||
97C147041CF9000F007C117D /* Release */,
|
|
||||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
97C147061CF9000F007C117D /* Debug */,
|
|
||||||
97C147071CF9000F007C117D /* Release */,
|
|
||||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>PreviewsEnabled</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1300"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Runner.app"
|
|
||||||
BlueprintName = "Runner"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Runner.app"
|
|
||||||
BlueprintName = "Runner"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Runner.app"
|
|
||||||
BlueprintName = "Runner"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Profile"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Runner.app"
|
|
||||||
BlueprintName = "Runner"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
7
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "group:Runner.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>PreviewsEnabled</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import UIKit
|
|
||||||
import Flutter
|
|
||||||
|
|
||||||
@UIApplicationMain
|
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
|
||||||
override func application(
|
|
||||||
_ application: UIApplication,
|
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
|
||||||
) -> Bool {
|
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"filename" : "Icon-App-1024x1024@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 980 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "LaunchImage@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
@@ -1,5 +0,0 @@
|
|||||||
# Launch Screen Assets
|
|
||||||
|
|
||||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
|
||||||
|
|
||||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"assets/images/icon_ios.png-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"assets/images/icon_ios.png-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/icon_ios.png-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/icon_ios.png-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/icon_ios.png-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"assets/images/icon_ios.png-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"assets/images/icon_ios.png-40x40@3x.png","scale":"3x"},{"size":"60x60","idiom":"iphone","filename":"assets/images/icon_ios.png-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"assets/images/icon_ios.png-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"assets/images/icon_ios.png-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"assets/images/icon_ios.png-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"assets/images/icon_ios.png-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"assets/images/icon_ios.png-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"assets/images/icon_ios.png-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"assets/images/icon_ios.png-40x40@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"assets/images/icon_ios.png-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"assets/images/icon_ios.png-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"assets/images/icon_ios.png-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"assets/images/icon_ios.png-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
|
||||||
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 759 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1 +0,0 @@
|
|||||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"assets/images/logo_ios.png-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"assets/images/logo_ios.png-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/logo_ios.png-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/logo_ios.png-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"assets/images/logo_ios.png-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"assets/images/logo_ios.png-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"assets/images/logo_ios.png-40x40@3x.png","scale":"3x"},{"size":"60x60","idiom":"iphone","filename":"assets/images/logo_ios.png-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"assets/images/logo_ios.png-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"assets/images/logo_ios.png-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"assets/images/logo_ios.png-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"assets/images/logo_ios.png-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"assets/images/logo_ios.png-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"assets/images/logo_ios.png-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"assets/images/logo_ios.png-40x40@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"assets/images/logo_ios.png-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"assets/images/logo_ios.png-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"assets/images/logo_ios.png-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"assets/images/logo_ios.png-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
|
||||||
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 328 B |
|
Before Width: | Height: | Size: 749 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 537 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 749 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<layoutGuides>
|
|
||||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
|
||||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
|
||||||
</layoutGuides>
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
|
||||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
<resources>
|
|
||||||
<image name="LaunchImage" width="168" height="185"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--Flutter View Controller-->
|
|
||||||
<scene sceneID="tne-QT-ifu">
|
|
||||||
<objects>
|
|
||||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
|
||||||
<layoutGuides>
|
|
||||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
|
||||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
|
||||||
</layoutGuides>
|
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Moxxy</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>moxxyv2</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>LaunchScreen</string>
|
|
||||||
<key>UIMainStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
|
||||||
<true/>
|
|
||||||
<key>LSApplicationQueriesScheme</key>
|
|
||||||
<array>
|
|
||||||
<string>https</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#import "GeneratedPluginRegistrant.h"
|
|
||||||
@@ -7,13 +7,15 @@ files:
|
|||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
attributes:
|
attributes:
|
||||||
jid: String
|
jid: String
|
||||||
displayName: String
|
preStart:
|
||||||
|
type: PreStartDoneEvent
|
||||||
|
deserialise: true
|
||||||
- name: LoginFailureEvent
|
- name: LoginFailureEvent
|
||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
attributes:
|
attributes:
|
||||||
reason: String
|
reason: String?
|
||||||
- name: PreStartDoneEvent
|
- name: PreStartDoneEvent
|
||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
implements:
|
implements:
|
||||||
@@ -69,8 +71,7 @@ files:
|
|||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
# Send by the service if a message has been received or returned by
|
# Send by the service if a message has been received or returned by # [SendMessageCommand].
|
||||||
# [SendMessageCommand].
|
|
||||||
- name: MessageAddedEvent
|
- name: MessageAddedEvent
|
||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
implements:
|
implements:
|
||||||
@@ -109,7 +110,7 @@ files:
|
|||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
attributes:
|
attributes:
|
||||||
id: int
|
id: int
|
||||||
progress: double
|
progress: double?
|
||||||
# Triggered by [RosterService] if we receive a roster push.
|
# Triggered by [RosterService] if we receive a roster push.
|
||||||
- name: RosterDiffEvent
|
- name: RosterDiffEvent
|
||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
@@ -172,6 +173,40 @@ files:
|
|||||||
extends: BackgroundEvent
|
extends: BackgroundEvent
|
||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
|
- name: GetConversationOmemoFingerprintsResult
|
||||||
|
extends: BackgroundEvent
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
fingerprints:
|
||||||
|
type: List<OmemoDevice>
|
||||||
|
deserialise: true
|
||||||
|
- name: GetOwnOmemoFingerprintsResult
|
||||||
|
extends: BackgroundEvent
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
ownDeviceFingerprint: String
|
||||||
|
ownDeviceId: int
|
||||||
|
fingerprints:
|
||||||
|
type: List<OmemoDevice>
|
||||||
|
deserialise: true
|
||||||
|
- name: RegenerateOwnDeviceResult
|
||||||
|
extends: BackgroundEvent
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
device:
|
||||||
|
type: OmemoDevice
|
||||||
|
deserialise: true
|
||||||
|
- name: MessageNotificationTappedEvent
|
||||||
|
extends: BackgroundEvent
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
conversationJid: String
|
||||||
|
title: String
|
||||||
|
avatarUrl: String
|
||||||
generate_builder: true
|
generate_builder: true
|
||||||
builder_name: "Event"
|
builder_name: "Event"
|
||||||
builder_baseclass: "BackgroundEvent"
|
builder_baseclass: "BackgroundEvent"
|
||||||
@@ -190,6 +225,8 @@ files:
|
|||||||
extends: BackgroundCommand
|
extends: BackgroundCommand
|
||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
systemLocaleCode: String
|
||||||
- name: AddConversationCommand
|
- name: AddConversationCommand
|
||||||
extends: BackgroundCommand
|
extends: BackgroundCommand
|
||||||
implements:
|
implements:
|
||||||
@@ -216,7 +253,7 @@ files:
|
|||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
attributes:
|
attributes:
|
||||||
jid: String
|
recipients: List<String>
|
||||||
body: String
|
body: String
|
||||||
chatState: String
|
chatState: String
|
||||||
quotedMessage:
|
quotedMessage:
|
||||||
@@ -227,7 +264,7 @@ files:
|
|||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
attributes:
|
attributes:
|
||||||
jid: String
|
recipients: List<String>
|
||||||
paths: List<String>
|
paths: List<String>
|
||||||
- name: BlockJidCommand
|
- name: BlockJidCommand
|
||||||
extends: BackgroundCommand
|
extends: BackgroundCommand
|
||||||
@@ -308,6 +345,77 @@ files:
|
|||||||
extends: BackgroundCommand
|
extends: BackgroundCommand
|
||||||
implements:
|
implements:
|
||||||
- JsonImplementation
|
- JsonImplementation
|
||||||
|
- name: SetConversationMuteStatusCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
jid: String
|
||||||
|
muted: bool
|
||||||
|
- name: GetConversationOmemoFingerprintsCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
jid: String
|
||||||
|
- name: SetOmemoDeviceEnabledCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
jid: String
|
||||||
|
deviceId: int
|
||||||
|
enabled: bool
|
||||||
|
- name: RecreateSessionsCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
jid: String
|
||||||
|
- name: SetOmemoEnabledCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
jid: String
|
||||||
|
enabled: bool
|
||||||
|
- name: GetOwnOmemoFingerprintsCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
- name: RemoveOwnDeviceCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
deviceId: int
|
||||||
|
- name: RegenerateOwnDeviceCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
- name: RetractMessageCommentCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
originId: String
|
||||||
|
conversationJid: String
|
||||||
|
- name: MarkConversationAsReadCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
conversationId: int
|
||||||
|
- name: MarkMessageAsReadCommand
|
||||||
|
extends: BackgroundCommand
|
||||||
|
implements:
|
||||||
|
- JsonImplementation
|
||||||
|
attributes:
|
||||||
|
conversationJid: String
|
||||||
|
sid: String
|
||||||
|
newUnreadCounter: int
|
||||||
generate_builder: true
|
generate_builder: true
|
||||||
# get${builder_Name}FromJson
|
# get${builder_Name}FromJson
|
||||||
builder_name: "Command"
|
builder_name: "Command"
|
||||||
|
|||||||
140
lib/main.dart
@@ -1,24 +1,31 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:moxplatform/moxplatform.dart';
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
|
import 'package:moxxyv2/i18n/strings.g.dart';
|
||||||
import 'package:moxxyv2/service/service.dart';
|
import 'package:moxxyv2/service/service.dart';
|
||||||
import 'package:moxxyv2/shared/commands.dart';
|
import 'package:moxxyv2/shared/commands.dart';
|
||||||
|
import 'package:moxxyv2/shared/synchronized_queue.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/addcontact_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/addcontact_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/blocklist_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/blocklist_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/crop_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/crop_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/cropbackground_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/cropbackground_bloc.dart';
|
||||||
|
import 'package:moxxyv2/ui/bloc/devices_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/login_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/login_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/navigation_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/navigation_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/newconversation_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/newconversation_bloc.dart';
|
||||||
|
import 'package:moxxyv2/ui/bloc/own_devices_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/profile_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/profile_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/sendfiles_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/sendfiles_bloc.dart';
|
||||||
|
import 'package:moxxyv2/ui/bloc/server_info_bloc.dart';
|
||||||
|
import 'package:moxxyv2/ui/bloc/share_selection_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/bloc/sharedmedia_bloc.dart';
|
import 'package:moxxyv2/ui/bloc/sharedmedia_bloc.dart';
|
||||||
import 'package:moxxyv2/ui/constants.dart';
|
import 'package:moxxyv2/ui/constants.dart';
|
||||||
import 'package:moxxyv2/ui/events.dart';
|
import 'package:moxxyv2/ui/events.dart';
|
||||||
@@ -34,25 +41,31 @@ import 'package:moxxyv2/ui/pages/crop.dart';
|
|||||||
import 'package:moxxyv2/ui/pages/intro.dart';
|
import 'package:moxxyv2/ui/pages/intro.dart';
|
||||||
import 'package:moxxyv2/ui/pages/login.dart';
|
import 'package:moxxyv2/ui/pages/login.dart';
|
||||||
import 'package:moxxyv2/ui/pages/newconversation.dart';
|
import 'package:moxxyv2/ui/pages/newconversation.dart';
|
||||||
|
import 'package:moxxyv2/ui/pages/profile/devices.dart';
|
||||||
|
import 'package:moxxyv2/ui/pages/profile/own_devices.dart';
|
||||||
import 'package:moxxyv2/ui/pages/profile/profile.dart';
|
import 'package:moxxyv2/ui/pages/profile/profile.dart';
|
||||||
import 'package:moxxyv2/ui/pages/sendfiles.dart';
|
import 'package:moxxyv2/ui/pages/sendfiles.dart';
|
||||||
|
import 'package:moxxyv2/ui/pages/server_info.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/about.dart';
|
import 'package:moxxyv2/ui/pages/settings/about.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/appearance/appearance.dart';
|
import 'package:moxxyv2/ui/pages/settings/appearance/appearance.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/appearance/cropbackground.dart';
|
import 'package:moxxyv2/ui/pages/settings/appearance/cropbackground.dart';
|
||||||
|
import 'package:moxxyv2/ui/pages/settings/conversation.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/debugging.dart';
|
import 'package:moxxyv2/ui/pages/settings/debugging.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/licenses.dart';
|
import 'package:moxxyv2/ui/pages/settings/licenses.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/network.dart';
|
import 'package:moxxyv2/ui/pages/settings/network.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/privacy.dart';
|
import 'package:moxxyv2/ui/pages/settings/privacy/privacy.dart';
|
||||||
import 'package:moxxyv2/ui/pages/settings/settings.dart';
|
import 'package:moxxyv2/ui/pages/settings/settings.dart';
|
||||||
|
import 'package:moxxyv2/ui/pages/share_selection.dart';
|
||||||
import 'package:moxxyv2/ui/pages/sharedmedia.dart';
|
import 'package:moxxyv2/ui/pages/sharedmedia.dart';
|
||||||
import 'package:moxxyv2/ui/pages/splashscreen/splashscreen.dart';
|
import 'package:moxxyv2/ui/pages/splashscreen/splashscreen.dart';
|
||||||
import 'package:moxxyv2/ui/service/data.dart';
|
import 'package:moxxyv2/ui/service/data.dart';
|
||||||
import 'package:moxxyv2/ui/service/progress.dart';
|
import 'package:moxxyv2/ui/service/progress.dart';
|
||||||
import 'package:moxxyv2/ui/service/thumbnail.dart';
|
import 'package:moxxyv2/ui/theme.dart';
|
||||||
import 'package:page_transition/page_transition.dart';
|
import 'package:page_transition/page_transition.dart';
|
||||||
|
import 'package:share_handler/share_handler.dart';
|
||||||
|
|
||||||
void setupLogging() {
|
void setupLogging() {
|
||||||
Logger.root.level = Level.ALL;
|
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
|
||||||
Logger.root.onRecord.listen((record) {
|
Logger.root.onRecord.listen((record) {
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
print('[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}');
|
print('[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}');
|
||||||
@@ -63,30 +76,30 @@ void setupLogging() {
|
|||||||
Future<void> setupUIServices() async {
|
Future<void> setupUIServices() async {
|
||||||
GetIt.I.registerSingleton<UIProgressService>(UIProgressService());
|
GetIt.I.registerSingleton<UIProgressService>(UIProgressService());
|
||||||
GetIt.I.registerSingleton<UIDataService>(UIDataService());
|
GetIt.I.registerSingleton<UIDataService>(UIDataService());
|
||||||
GetIt.I.registerSingleton<ThumbnailCacheService>(ThumbnailCacheService());
|
}
|
||||||
await GetIt.I.get<UIDataService>().init();}
|
|
||||||
|
|
||||||
void setupBlocs(GlobalKey<NavigatorState> navKey) {
|
void setupBlocs(GlobalKey<NavigatorState> navKey) {
|
||||||
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navigationKey: navKey));
|
GetIt.I.registerSingleton<NavigationBloc>(NavigationBloc(navigationKey: navKey));
|
||||||
GetIt.I.registerSingleton<ConversationsBloc>(ConversationsBloc());
|
GetIt.I.registerSingleton<ConversationsBloc>(ConversationsBloc());
|
||||||
GetIt.I.registerSingleton<NewConversationBloc>(NewConversationBloc());
|
GetIt.I.registerSingleton<NewConversationBloc>(NewConversationBloc());
|
||||||
GetIt.I.registerSingleton<ConversationBloc>(ConversationBloc());
|
GetIt.I.registerSingleton<ConversationBloc>(ConversationBloc());
|
||||||
GetIt.I.registerSingleton<BlocklistBloc>(BlocklistBloc());
|
GetIt.I.registerSingleton<BlocklistBloc>(BlocklistBloc()); GetIt.I.registerSingleton<ProfileBloc>(ProfileBloc());
|
||||||
GetIt.I.registerSingleton<ProfileBloc>(ProfileBloc());
|
|
||||||
GetIt.I.registerSingleton<PreferencesBloc>(PreferencesBloc());
|
GetIt.I.registerSingleton<PreferencesBloc>(PreferencesBloc());
|
||||||
GetIt.I.registerSingleton<AddContactBloc>(AddContactBloc());
|
GetIt.I.registerSingleton<AddContactBloc>(AddContactBloc());
|
||||||
GetIt.I.registerSingleton<SharedMediaBloc>(SharedMediaBloc());
|
GetIt.I.registerSingleton<SharedMediaBloc>(SharedMediaBloc());
|
||||||
GetIt.I.registerSingleton<CropBloc>(CropBloc());
|
GetIt.I.registerSingleton<CropBloc>(CropBloc());
|
||||||
GetIt.I.registerSingleton<SendFilesBloc>(SendFilesBloc());
|
GetIt.I.registerSingleton<SendFilesBloc>(SendFilesBloc());
|
||||||
GetIt.I.registerSingleton<CropBackgroundBloc>(CropBackgroundBloc());
|
GetIt.I.registerSingleton<CropBackgroundBloc>(CropBackgroundBloc());
|
||||||
|
GetIt.I.registerSingleton<ShareSelectionBloc>(ShareSelectionBloc());
|
||||||
|
GetIt.I.registerSingleton<ServerInfoBloc>(ServerInfoBloc());
|
||||||
|
GetIt.I.registerSingleton<DevicesBloc>(DevicesBloc());
|
||||||
|
GetIt.I.registerSingleton<OwnDevicesBloc>(OwnDevicesBloc());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Unknown): Replace all Column(children: [ Padding(), Padding, ...]) with a
|
// TODO(Unknown): Replace all Column(children: [ Padding(), Padding, ...]) with a
|
||||||
// Padding(padding: ..., child: Column(children: [ ... ]))
|
// Padding(padding: ..., child: Column(children: [ ... ]))
|
||||||
// TODO(Unknown): Theme the switches
|
// TODO(Unknown): Theme the switches
|
||||||
void main() async {
|
void main() async {
|
||||||
GetIt.I.registerSingleton<Completer<void>>(Completer());
|
|
||||||
|
|
||||||
setupLogging();
|
setupLogging();
|
||||||
await setupUIServices();
|
await setupUIServices();
|
||||||
|
|
||||||
@@ -138,16 +151,30 @@ void main() async {
|
|||||||
),
|
),
|
||||||
BlocProvider<CropBackgroundBloc>(
|
BlocProvider<CropBackgroundBloc>(
|
||||||
create: (_) => GetIt.I.get<CropBackgroundBloc>(),
|
create: (_) => GetIt.I.get<CropBackgroundBloc>(),
|
||||||
)
|
),
|
||||||
|
BlocProvider<ShareSelectionBloc>(
|
||||||
|
create: (_) => GetIt.I.get<ShareSelectionBloc>(),
|
||||||
|
),
|
||||||
|
BlocProvider<ServerInfoBloc>(
|
||||||
|
create: (_) => GetIt.I.get<ServerInfoBloc>(),
|
||||||
|
),
|
||||||
|
BlocProvider<DevicesBloc>(
|
||||||
|
create: (_) => GetIt.I.get<DevicesBloc>(),
|
||||||
|
),
|
||||||
|
BlocProvider<OwnDevicesBloc>(
|
||||||
|
create: (_) => GetIt.I.get<OwnDevicesBloc>(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MyApp(navKey),
|
child: TranslationProvider(
|
||||||
|
child: MyApp(navKey),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
|
|
||||||
const MyApp(this.navigationKey, { Key? key }) : super(key: key);
|
const MyApp(this.navigationKey, { super.key });
|
||||||
final GlobalKey<NavigatorState> navigationKey;
|
final GlobalKey<NavigatorState> navigationKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -162,8 +189,44 @@ class MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
|
_setupSharingHandler();
|
||||||
|
|
||||||
// Lift the UI block
|
// Lift the UI block
|
||||||
GetIt.I.get<Completer<void>>().complete();
|
GetIt.I.get<SynchronizedQueue<Map<String, dynamic>?>>().removeQueueLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleSharedMedia(SharedMedia media) async {
|
||||||
|
final attachments = media.attachments ?? [];
|
||||||
|
GetIt.I.get<ShareSelectionBloc>().add(
|
||||||
|
ShareSelectionRequestedEvent(
|
||||||
|
attachments.map((a) => a!.path).toList(),
|
||||||
|
media.content,
|
||||||
|
media.content != null ? ShareSelectionType.text : ShareSelectionType.media,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setupSharingHandler() async {
|
||||||
|
final handler = ShareHandlerPlatform.instance;
|
||||||
|
final media = await handler.getInitialSharedMedia();
|
||||||
|
|
||||||
|
// Shared while the app was closed
|
||||||
|
if (media != null) {
|
||||||
|
if (GetIt.I.get<UIDataService>().isLoggedIn) {
|
||||||
|
await _handleSharedMedia(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
await handler.resetInitialSharedMedia();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared while the app is stil running
|
||||||
|
handler.sharedMediaStream.listen((SharedMedia media) async {
|
||||||
|
if (GetIt.I.get<UIDataService>().isLoggedIn) {
|
||||||
|
await _handleSharedMedia(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
await handler.resetInitialSharedMedia();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -199,44 +262,12 @@ class MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
locale: TranslationProvider.of(context).flutterLocale,
|
||||||
|
supportedLocales: LocaleSettings.supportedLocales,
|
||||||
|
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||||
title: 'Moxxy',
|
title: 'Moxxy',
|
||||||
theme: ThemeData(
|
theme: getThemeData(context, Brightness.light),
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
darkTheme: getThemeData(context, Brightness.dark),
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
primary: primaryColor,
|
|
||||||
onPrimary: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textButtonTheme: TextButtonThemeData(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
primary: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// NOTE: Mainly for the SettingsSection
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
secondary: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
darkTheme: ThemeData(
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
primary: primaryColor,
|
|
||||||
onPrimary: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textButtonTheme: TextButtonThemeData(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
primary: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// NOTE: Mainly for the SettingsSection
|
|
||||||
colorScheme: const ColorScheme.dark(
|
|
||||||
secondary: primaryColor,
|
|
||||||
),
|
|
||||||
|
|
||||||
backgroundColor: const Color(0xff303030),
|
|
||||||
),
|
|
||||||
navigatorKey: widget.navigationKey,
|
navigatorKey: widget.navigationKey,
|
||||||
onGenerateRoute: (settings) {
|
onGenerateRoute: (settings) {
|
||||||
switch (settings.name) {
|
switch (settings.name) {
|
||||||
@@ -255,7 +286,6 @@ class MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
case settingsRoute: return SettingsPage.route;
|
case settingsRoute: return SettingsPage.route;
|
||||||
case aboutRoute: return SettingsAboutPage.route;
|
case aboutRoute: return SettingsAboutPage.route;
|
||||||
case licensesRoute: return SettingsLicensesPage.route;
|
case licensesRoute: return SettingsLicensesPage.route;
|
||||||
case appearanceRoute: return AppearancePage.route;
|
|
||||||
case networkRoute: return NetworkPage.route;
|
case networkRoute: return NetworkPage.route;
|
||||||
case privacyRoute: return PrivacyPage.route;
|
case privacyRoute: return PrivacyPage.route;
|
||||||
case debuggingRoute: return DebuggingPage.route;
|
case debuggingRoute: return DebuggingPage.route;
|
||||||
@@ -263,6 +293,12 @@ class MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
case cropRoute: return CropPage.route;
|
case cropRoute: return CropPage.route;
|
||||||
case sendFilesRoute: return SendFilesPage.route;
|
case sendFilesRoute: return SendFilesPage.route;
|
||||||
case backgroundCroppingRoute: return CropBackgroundPage.route;
|
case backgroundCroppingRoute: return CropBackgroundPage.route;
|
||||||
|
case shareSelectionRoute: return ShareSelectionPage.route;
|
||||||
|
case serverInfoRoute: return ServerInfoPage.route;
|
||||||
|
case conversationSettingsRoute: return ConversationSettingsPage.route;
|
||||||
|
case devicesRoute: return DevicesPage.route;
|
||||||
|
case ownDevicesRoute: return OwnDevicesPage.route;
|
||||||
|
case appearanceRoute: return AppearanceSettingsPage.route;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:get_it/get_it.dart';
|
|||||||
import 'package:hex/hex.dart';
|
import 'package:hex/hex.dart';
|
||||||
import 'package:image_size_getter/image_size_getter.dart';
|
import 'package:image_size_getter/image_size_getter.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:moxlib/moxlib.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
import 'package:moxxyv2/service/conversation.dart';
|
import 'package:moxxyv2/service/conversation.dart';
|
||||||
import 'package:moxxyv2/service/preferences.dart';
|
import 'package:moxxyv2/service/preferences.dart';
|
||||||
import 'package:moxxyv2/service/roster.dart';
|
import 'package:moxxyv2/service/roster.dart';
|
||||||
@@ -12,14 +14,6 @@ import 'package:moxxyv2/service/service.dart';
|
|||||||
import 'package:moxxyv2/service/xmpp.dart';
|
import 'package:moxxyv2/service/xmpp.dart';
|
||||||
import 'package:moxxyv2/shared/avatar.dart';
|
import 'package:moxxyv2/shared/avatar.dart';
|
||||||
import 'package:moxxyv2/shared/events.dart';
|
import 'package:moxxyv2/shared/events.dart';
|
||||||
import 'package:moxxyv2/shared/helpers.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/connection.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/managers/namespaces.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/namespaces.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0030/helpers.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0030/xep_0030.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0054.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0084.dart';
|
|
||||||
|
|
||||||
/// Removes line breaks and spaces from [original]. This might happen when we request the
|
/// Removes line breaks and spaces from [original]. This might happen when we request the
|
||||||
/// avatar data. Returns the cleaned version.
|
/// avatar data. Returns the cleaned version.
|
||||||
@@ -93,7 +87,10 @@ class AvatarService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchAndUpdateAvatarForJid(String jid, String oldHash) async {
|
Future<void> fetchAndUpdateAvatarForJid(String jid, String oldHash) async {
|
||||||
final items = (await _getDiscoManager().discoItemsQuery(jid)) ?? [];
|
final response = await _getDiscoManager().discoItemsQuery(jid);
|
||||||
|
final items = response.isType<DiscoError>() ?
|
||||||
|
<DiscoItem>[] :
|
||||||
|
response.get<List<DiscoItem>>();
|
||||||
final itemNodes = items.map((i) => i.node);
|
final itemNodes = items.map((i) => i.node);
|
||||||
|
|
||||||
_log.finest('Disco items for $jid:');
|
_log.finest('Disco items for $jid:');
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
import 'package:moxxyv2/service/service.dart';
|
import 'package:moxxyv2/service/service.dart';
|
||||||
import 'package:moxxyv2/shared/events.dart';
|
import 'package:moxxyv2/shared/events.dart';
|
||||||
import 'package:moxxyv2/xmpp/connection.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/managers/namespaces.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0191.dart';
|
|
||||||
|
|
||||||
enum BlockPushType {
|
enum BlockPushType {
|
||||||
block,
|
block,
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
import 'package:moxxyv2/service/httpfiletransfer/httpfiletransfer.dart';
|
import 'package:moxxyv2/service/httpfiletransfer/httpfiletransfer.dart';
|
||||||
import 'package:moxxyv2/service/moxxmpp/reconnect.dart';
|
import 'package:moxxyv2/service/moxxmpp/reconnect.dart';
|
||||||
import 'package:moxxyv2/xmpp/connection.dart';
|
|
||||||
|
|
||||||
class ConnectivityService {
|
class ConnectivityService {
|
||||||
|
|
||||||
ConnectivityService() : _log = Logger('ConnectivityService');
|
ConnectivityService() : _log = Logger('ConnectivityService');
|
||||||
final Logger _log;
|
final Logger _log;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import 'dart:async';
|
|||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:moxxyv2/i18n/strings.g.dart';
|
||||||
import 'package:moxxyv2/service/connectivity.dart';
|
import 'package:moxxyv2/service/connectivity.dart';
|
||||||
import 'package:moxxyv2/service/notifications.dart';
|
import 'package:moxxyv2/service/notifications.dart';
|
||||||
import 'package:moxxyv2/xmpp/connection.dart';
|
|
||||||
|
|
||||||
class ConnectivityWatcherService {
|
class ConnectivityWatcherService {
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ class ConnectivityWatcherService {
|
|||||||
Future<void> _onTimerElapsed() async {
|
Future<void> _onTimerElapsed() async {
|
||||||
await GetIt.I.get<NotificationsService>().showWarningNotification(
|
await GetIt.I.get<NotificationsService>().showWarningNotification(
|
||||||
'Moxxy',
|
'Moxxy',
|
||||||
'Could not connect to server',
|
t.errors.connection.connectionTimeout,
|
||||||
);
|
);
|
||||||
_stopTimer();
|
_stopTimer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:moxxyv2/service/database.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
import 'package:moxxyv2/service/db/media.dart';
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:moxxyv2/service/database/database.dart';
|
||||||
|
import 'package:moxxyv2/service/preferences.dart';
|
||||||
import 'package:moxxyv2/shared/cache.dart';
|
import 'package:moxxyv2/shared/cache.dart';
|
||||||
import 'package:moxxyv2/shared/helpers.dart';
|
|
||||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0085.dart';
|
import 'package:moxxyv2/shared/models/message.dart';
|
||||||
|
|
||||||
class ConversationService {
|
class ConversationService {
|
||||||
|
|
||||||
ConversationService()
|
ConversationService()
|
||||||
: _conversationCache = LRUCache(100),
|
: _conversationCache = LRUCache(100),
|
||||||
_loadedConversations = false;
|
_loadedConversations = false;
|
||||||
@@ -56,27 +56,33 @@ class ConversationService {
|
|||||||
|
|
||||||
/// Wrapper around [DatabaseService]'s [updateConversation] that modifies the cache.
|
/// Wrapper around [DatabaseService]'s [updateConversation] that modifies the cache.
|
||||||
Future<Conversation> updateConversation(int id, {
|
Future<Conversation> updateConversation(int id, {
|
||||||
String? lastMessageBody,
|
int? lastChangeTimestamp,
|
||||||
int? lastChangeTimestamp,
|
Message? lastMessage,
|
||||||
bool? open,
|
bool? open,
|
||||||
int? unreadCounter,
|
int? unreadCounter,
|
||||||
String? avatarUrl,
|
String? avatarUrl,
|
||||||
List<DBSharedMedium>? sharedMedia,
|
ChatState? chatState,
|
||||||
ChatState? chatState,
|
bool? muted,
|
||||||
}
|
bool? encrypted,
|
||||||
) async {
|
}) async {
|
||||||
final conversation = await _getConversationById(id);
|
final conversation = (await _getConversationById(id))!;
|
||||||
final newConversation = await GetIt.I.get<DatabaseService>().updateConversation(
|
var newConversation = await GetIt.I.get<DatabaseService>().updateConversation(
|
||||||
id,
|
id,
|
||||||
lastMessageBody: lastMessageBody,
|
lastMessage: lastMessage,
|
||||||
lastChangeTimestamp: lastChangeTimestamp,
|
lastChangeTimestamp: lastChangeTimestamp,
|
||||||
open: open,
|
open: open,
|
||||||
unreadCounter: unreadCounter,
|
unreadCounter: unreadCounter,
|
||||||
avatarUrl: avatarUrl,
|
avatarUrl: avatarUrl,
|
||||||
sharedMedia: sharedMedia,
|
chatState: conversation.chatState,
|
||||||
chatState: conversation?.chatState ?? ChatState.gone,
|
muted: muted,
|
||||||
|
encrypted: encrypted,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Copy over the old lastMessage if a new one was not set
|
||||||
|
if (conversation.lastMessage != null && lastMessage == null) {
|
||||||
|
newConversation = newConversation.copyWith(lastMessage: conversation.lastMessage);
|
||||||
|
}
|
||||||
|
|
||||||
_conversationCache.cache(id, newConversation);
|
_conversationCache.cache(id, newConversation);
|
||||||
return newConversation;
|
return newConversation;
|
||||||
}
|
}
|
||||||
@@ -84,26 +90,39 @@ class ConversationService {
|
|||||||
/// Wrapper around [DatabaseService]'s [addConversationFromData] that updates the cache.
|
/// Wrapper around [DatabaseService]'s [addConversationFromData] that updates the cache.
|
||||||
Future<Conversation> addConversationFromData(
|
Future<Conversation> addConversationFromData(
|
||||||
String title,
|
String title,
|
||||||
String lastMessageBody,
|
Message? lastMessage,
|
||||||
String avatarUrl,
|
String avatarUrl,
|
||||||
String jid,
|
String jid,
|
||||||
int unreadCounter,
|
int unreadCounter,
|
||||||
int lastChangeTimestamp,
|
int lastChangeTimestamp,
|
||||||
List<DBSharedMedium> sharedMedia,
|
|
||||||
bool open,
|
bool open,
|
||||||
|
bool muted,
|
||||||
|
bool encrypted,
|
||||||
) async {
|
) async {
|
||||||
final newConversation = await GetIt.I.get<DatabaseService>().addConversationFromData(
|
final newConversation = await GetIt.I.get<DatabaseService>().addConversationFromData(
|
||||||
title,
|
title,
|
||||||
lastMessageBody,
|
lastMessage,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
jid,
|
jid,
|
||||||
unreadCounter,
|
unreadCounter,
|
||||||
lastChangeTimestamp,
|
lastChangeTimestamp,
|
||||||
sharedMedia,
|
|
||||||
open,
|
open,
|
||||||
|
muted,
|
||||||
|
encrypted,
|
||||||
);
|
);
|
||||||
|
|
||||||
_conversationCache.cache(newConversation.id, newConversation);
|
_conversationCache.cache(newConversation.id, newConversation);
|
||||||
return newConversation;
|
return newConversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the stanzas to the conversation with [jid] should be encrypted.
|
||||||
|
/// If not, returns false.
|
||||||
|
///
|
||||||
|
/// If the conversation does not exist, then the value of the preference for
|
||||||
|
/// enableOmemoByDefault is used.
|
||||||
|
Future<bool> shouldEncryptForConversation(JID jid) async {
|
||||||
|
final prefs = await GetIt.I.get<PreferencesService>().getPreferences();
|
||||||
|
final conversation = await getConversationByJid(jid.toString());
|
||||||
|
return conversation?.encrypted ?? prefs.enableOmemoByDefault;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
138
lib/service/cryptography/cryptography.dart
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:moxxyv2/service/cryptography/types.dart';
|
||||||
|
|
||||||
|
List<int> _randomBuffer(int length) {
|
||||||
|
final buf = List<int>.empty(growable: true);
|
||||||
|
|
||||||
|
final random = Random.secure();
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
buf.add(random.nextInt(256));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipherAlgorithm _sfsToCipher(SFSEncryptionType type) {
|
||||||
|
switch (type) {
|
||||||
|
case SFSEncryptionType.aes128GcmNoPadding: return CipherAlgorithm.aes128GcmNoPadding;
|
||||||
|
case SFSEncryptionType.aes256GcmNoPadding: return CipherAlgorithm.aes256GcmNoPadding;
|
||||||
|
case SFSEncryptionType.aes256CbcPkcs7: return CipherAlgorithm.aes256CbcPkcs7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CryptographyService {
|
||||||
|
|
||||||
|
CryptographyService() : _log = Logger('CryptographyService');
|
||||||
|
final Logger _log;
|
||||||
|
|
||||||
|
/// Encrypt the file at path [source] and write the encrypted data to [dest]. For the
|
||||||
|
/// encryption, use the algorithm indicated by [encryption].
|
||||||
|
Future<EncryptionResult> encryptFile(String source, String dest, SFSEncryptionType encryption) async {
|
||||||
|
_log.finest('Beginning encryption routine for $source');
|
||||||
|
final key = encryption == SFSEncryptionType.aes128GcmNoPadding ?
|
||||||
|
_randomBuffer(16) :
|
||||||
|
_randomBuffer(32);
|
||||||
|
final iv = _randomBuffer(12);
|
||||||
|
final result = (await MoxplatformPlugin.crypto.encryptFile(
|
||||||
|
source,
|
||||||
|
dest,
|
||||||
|
Uint8List.fromList(key),
|
||||||
|
Uint8List.fromList(iv),
|
||||||
|
_sfsToCipher(encryption),
|
||||||
|
'SHA-256',
|
||||||
|
))!;
|
||||||
|
_log.finest('Encryption done for $source ($result)');
|
||||||
|
|
||||||
|
return EncryptionResult(
|
||||||
|
key,
|
||||||
|
iv,
|
||||||
|
<String, String>{
|
||||||
|
hashSha256: base64Encode(result.plaintextHash),
|
||||||
|
},
|
||||||
|
<String, String>{
|
||||||
|
hashSha256: base64Encode(result.ciphertextHash),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt the file at [source] and write the decrypted version to [dest]. For the
|
||||||
|
/// decryption, use the algorithm indicated by [encryption] with the key [key] and the
|
||||||
|
/// IV or nonce [iv].
|
||||||
|
Future<DecryptionResult> decryptFile(
|
||||||
|
String source,
|
||||||
|
String dest,
|
||||||
|
SFSEncryptionType encryption,
|
||||||
|
List<int> key,
|
||||||
|
List<int> iv,
|
||||||
|
Map<String, String> plaintextHashes,
|
||||||
|
Map<String, String> ciphertextHashes,
|
||||||
|
) async {
|
||||||
|
_log.finest('Beginning decryption for $source');
|
||||||
|
final result = await MoxplatformPlugin.crypto.encryptFile(
|
||||||
|
source,
|
||||||
|
dest,
|
||||||
|
Uint8List.fromList(key),
|
||||||
|
Uint8List.fromList(iv),
|
||||||
|
_sfsToCipher(encryption),
|
||||||
|
// TODO(Unknown): How to we get hash agility here?
|
||||||
|
'SHA-256',
|
||||||
|
);
|
||||||
|
_log.finest('Decryption done for $source (${result != null})');
|
||||||
|
|
||||||
|
var passedPlaintextIntegrityCheck = true;
|
||||||
|
var passedCiphertextIntegrityCheck = true;
|
||||||
|
for (final entry in plaintextHashes.entries) {
|
||||||
|
if (entry.key == hashSha256) {
|
||||||
|
if (base64Encode(result!.plaintextHash) != entry.value) {
|
||||||
|
passedPlaintextIntegrityCheck = false;
|
||||||
|
} else {
|
||||||
|
passedPlaintextIntegrityCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final entry in ciphertextHashes.entries) {
|
||||||
|
if (entry.key == hashSha256) {
|
||||||
|
if (base64Encode(result!.ciphertextHash) != entry.value) {
|
||||||
|
passedCiphertextIntegrityCheck = false;
|
||||||
|
} else {
|
||||||
|
passedCiphertextIntegrityCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecryptionResult(
|
||||||
|
result != null,
|
||||||
|
passedPlaintextIntegrityCheck,
|
||||||
|
passedCiphertextIntegrityCheck,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the file at [path] and calculate the base64-encoded hash using the algorithm
|
||||||
|
/// indicated by [hash].
|
||||||
|
Future<String> hashFile(String path, HashFunction hash) async {
|
||||||
|
String hashSpec;
|
||||||
|
if (hash == HashFunction.sha256) {
|
||||||
|
hashSpec = 'SHA-256';
|
||||||
|
} else if (hash == HashFunction.sha512) {
|
||||||
|
hashSpec = 'SHA-512';
|
||||||
|
} else {
|
||||||
|
// Android itself does not provide more
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.finest('Beginning hash generation of $path');
|
||||||
|
final data = await MoxplatformPlugin.crypto.hashFile(path, hashSpec);
|
||||||
|
_log.finest('Hash generation done for $path');
|
||||||
|
return base64Encode(data!);
|
||||||
|
}
|
||||||
|
}
|
||||||
150
lib/service/cryptography/implementations.dart
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
import 'package:moxxyv2/service/cryptography/types.dart';
|
||||||
|
|
||||||
|
Future<List<int>> hashFileImpl(HashRequest request) async {
|
||||||
|
final data = await File(request.path).readAsBytes();
|
||||||
|
|
||||||
|
return CryptographicHashManager.hashFromData(data, request.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EncryptionResult> encryptFileImpl(EncryptionRequest request) async {
|
||||||
|
Cipher algorithm;
|
||||||
|
switch (request.encryption) {
|
||||||
|
case SFSEncryptionType.aes128GcmNoPadding:
|
||||||
|
algorithm = AesGcm.with128bits();
|
||||||
|
break;
|
||||||
|
case SFSEncryptionType.aes256GcmNoPadding:
|
||||||
|
algorithm = AesGcm.with256bits();
|
||||||
|
break;
|
||||||
|
case SFSEncryptionType.aes256CbcPkcs7:
|
||||||
|
// TODO(Unknown): Implement
|
||||||
|
throw Exception();
|
||||||
|
// ignore: dead_code
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a key and an IV for the file
|
||||||
|
final key = await algorithm.newSecretKey();
|
||||||
|
final iv = algorithm.newNonce();
|
||||||
|
final plaintext = await File(request.source).readAsBytes();
|
||||||
|
final secretBox = await algorithm.encrypt(
|
||||||
|
plaintext,
|
||||||
|
secretKey: key,
|
||||||
|
nonce: iv,
|
||||||
|
);
|
||||||
|
final ciphertext = [
|
||||||
|
...secretBox.cipherText,
|
||||||
|
...secretBox.mac.bytes,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Write the file
|
||||||
|
await File(request.dest).writeAsBytes(ciphertext);
|
||||||
|
|
||||||
|
return EncryptionResult(
|
||||||
|
await key.extractBytes(),
|
||||||
|
iv,
|
||||||
|
{
|
||||||
|
hashSha256: base64Encode(
|
||||||
|
await CryptographicHashManager.hashFromData(plaintext, HashFunction.sha256),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hashSha256: base64Encode(
|
||||||
|
await CryptographicHashManager.hashFromData(ciphertext, HashFunction.sha256),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(PapaTutuWawa): Somehow fail when the ciphertext hash is not matching the provided data
|
||||||
|
Future<DecryptionResult> decryptFileImpl(DecryptionRequest request) async {
|
||||||
|
Cipher algorithm;
|
||||||
|
switch (request.encryption) {
|
||||||
|
case SFSEncryptionType.aes128GcmNoPadding:
|
||||||
|
algorithm = AesGcm.with128bits();
|
||||||
|
break;
|
||||||
|
case SFSEncryptionType.aes256GcmNoPadding:
|
||||||
|
algorithm = AesGcm.with256bits();
|
||||||
|
break;
|
||||||
|
case SFSEncryptionType.aes256CbcPkcs7:
|
||||||
|
// TODO(Unknown): Implement
|
||||||
|
throw Exception();
|
||||||
|
// ignore: dead_code
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ciphertextRaw = await File(request.source).readAsBytes();
|
||||||
|
final mac = List<int>.empty(growable: true);
|
||||||
|
final ciphertext = List<int>.empty(growable: true);
|
||||||
|
// TODO(PapaTutuWawa): Somehow handle aes256CbcPkcs7
|
||||||
|
if (request.encryption == SFSEncryptionType.aes128GcmNoPadding ||
|
||||||
|
request.encryption == SFSEncryptionType.aes256GcmNoPadding) {
|
||||||
|
mac.addAll(ciphertextRaw.sublist(ciphertextRaw.length - 16));
|
||||||
|
ciphertext.addAll(ciphertextRaw.sublist(0, ciphertextRaw.length - 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
var passedCiphertextIntegrityCheck = true;
|
||||||
|
var passedPlaintextIntegrityCheck = true;
|
||||||
|
// Try to find one hash we can verify
|
||||||
|
for (final entry in request.ciphertextHashes.entries) {
|
||||||
|
if ([hashSha256, hashSha512, hashBlake2b512].contains(entry.key)) {
|
||||||
|
final hash = await CryptographicHashManager.hashFromData(
|
||||||
|
ciphertext,
|
||||||
|
hashFunctionFromName(entry.key),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (base64Encode(hash) == entry.value) {
|
||||||
|
passedCiphertextIntegrityCheck = true;
|
||||||
|
} else {
|
||||||
|
passedCiphertextIntegrityCheck = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final secretBox = SecretBox(
|
||||||
|
ciphertext,
|
||||||
|
nonce: request.iv,
|
||||||
|
mac: Mac(mac),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = await algorithm.decrypt(
|
||||||
|
secretBox,
|
||||||
|
secretKey: SecretKey(request.key),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final entry in request.plaintextHashes.entries) {
|
||||||
|
if ([hashSha256, hashSha512, hashBlake2b512].contains(entry.key)) {
|
||||||
|
final hash = await CryptographicHashManager.hashFromData(
|
||||||
|
data,
|
||||||
|
hashFunctionFromName(entry.key),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (base64Encode(hash) == entry.value) {
|
||||||
|
passedPlaintextIntegrityCheck = true;
|
||||||
|
} else {
|
||||||
|
passedPlaintextIntegrityCheck = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await File(request.dest).writeAsBytes(data);
|
||||||
|
} catch (_) {
|
||||||
|
return DecryptionResult(
|
||||||
|
false,
|
||||||
|
passedPlaintextIntegrityCheck,
|
||||||
|
passedCiphertextIntegrityCheck,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecryptionResult(
|
||||||
|
true,
|
||||||
|
passedPlaintextIntegrityCheck,
|
||||||
|
passedCiphertextIntegrityCheck,
|
||||||
|
);
|
||||||
|
}
|
||||||
64
lib/service/cryptography/types.dart
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class EncryptionResult {
|
||||||
|
|
||||||
|
const EncryptionResult(this.key, this.iv, this.plaintextHashes, this.ciphertextHashes);
|
||||||
|
final List<int> key;
|
||||||
|
final List<int> iv;
|
||||||
|
|
||||||
|
final Map<String, String> plaintextHashes;
|
||||||
|
final Map<String, String> ciphertextHashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class EncryptionRequest {
|
||||||
|
|
||||||
|
const EncryptionRequest(this.source, this.dest, this.encryption);
|
||||||
|
final String source;
|
||||||
|
final String dest;
|
||||||
|
final SFSEncryptionType encryption;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class DecryptionResult {
|
||||||
|
|
||||||
|
const DecryptionResult(
|
||||||
|
this.decryptionOkay,
|
||||||
|
this.plaintextOkay,
|
||||||
|
this.ciphertextOkay,
|
||||||
|
);
|
||||||
|
final bool decryptionOkay;
|
||||||
|
final bool plaintextOkay;
|
||||||
|
final bool ciphertextOkay;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class DecryptionRequest {
|
||||||
|
|
||||||
|
const DecryptionRequest(
|
||||||
|
this.source,
|
||||||
|
this.dest,
|
||||||
|
this.encryption,
|
||||||
|
this.key,
|
||||||
|
this.iv,
|
||||||
|
this.plaintextHashes,
|
||||||
|
this.ciphertextHashes,
|
||||||
|
);
|
||||||
|
final String source;
|
||||||
|
final String dest;
|
||||||
|
final SFSEncryptionType encryption;
|
||||||
|
final List<int> key;
|
||||||
|
final List<int> iv;
|
||||||
|
final Map<String, String> plaintextHashes;
|
||||||
|
final Map<String, String> ciphertextHashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class HashRequest {
|
||||||
|
|
||||||
|
const HashRequest(this.path, this.hash);
|
||||||
|
final String path;
|
||||||
|
final HashFunction hash;
|
||||||
|
}
|
||||||
@@ -1,427 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:get_it/get_it.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:moxxyv2/service/db/conversation.dart';
|
|
||||||
import 'package:moxxyv2/service/db/media.dart';
|
|
||||||
import 'package:moxxyv2/service/db/message.dart';
|
|
||||||
import 'package:moxxyv2/service/db/roster.dart';
|
|
||||||
import 'package:moxxyv2/service/roster.dart';
|
|
||||||
import 'package:moxxyv2/shared/error_types.dart';
|
|
||||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
|
||||||
import 'package:moxxyv2/shared/models/media.dart';
|
|
||||||
import 'package:moxxyv2/shared/models/message.dart';
|
|
||||||
import 'package:moxxyv2/shared/models/roster.dart';
|
|
||||||
import 'package:moxxyv2/xmpp/xeps/xep_0085.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
SharedMedium sharedMediumDbToModel(DBSharedMedium s) {
|
|
||||||
return SharedMedium(
|
|
||||||
s.id!,
|
|
||||||
s.path,
|
|
||||||
s.timestamp,
|
|
||||||
mime: s.mime,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Conversation conversationDbToModel(DBConversation c, bool inRoster, String subscription, ChatState chatState) {
|
|
||||||
final media = c.sharedMedia
|
|
||||||
.map(sharedMediumDbToModel)
|
|
||||||
.toList();
|
|
||||||
// ignore: cascade_invocations
|
|
||||||
media.sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
|
||||||
|
|
||||||
return Conversation(
|
|
||||||
c.title,
|
|
||||||
c.lastMessageBody,
|
|
||||||
c.avatarUrl,
|
|
||||||
c.jid,
|
|
||||||
c.unreadCounter,
|
|
||||||
c.lastChangeTimestamp,
|
|
||||||
media,
|
|
||||||
c.id!,
|
|
||||||
c.open,
|
|
||||||
inRoster,
|
|
||||||
subscription,
|
|
||||||
chatState,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
RosterItem rosterDbToModel(DBRosterItem i) {
|
|
||||||
return RosterItem(
|
|
||||||
i.id!,
|
|
||||||
i.avatarUrl,
|
|
||||||
i.avatarHash,
|
|
||||||
i.jid,
|
|
||||||
i.title,
|
|
||||||
i.subscription,
|
|
||||||
i.ask,
|
|
||||||
i.groups,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Message messageDbToModel(DBMessage m) {
|
|
||||||
return Message(
|
|
||||||
m.sender,
|
|
||||||
m.body,
|
|
||||||
m.timestamp,
|
|
||||||
m.sid,
|
|
||||||
m.id!,
|
|
||||||
m.conversationJid,
|
|
||||||
m.isMedia,
|
|
||||||
m.isFileUploadNotification,
|
|
||||||
originId: m.originId,
|
|
||||||
received: m.received,
|
|
||||||
displayed: m.displayed,
|
|
||||||
acked: m.acked,
|
|
||||||
mediaUrl: m.mediaUrl,
|
|
||||||
mediaType: m.mediaType,
|
|
||||||
thumbnailData: m.thumbnailData,
|
|
||||||
thumbnailDimensions: m.thumbnailDimensions,
|
|
||||||
srcUrl: m.srcUrl,
|
|
||||||
quotes: m.quotes.value != null ? messageDbToModel(m.quotes.value!) : null,
|
|
||||||
errorType: m.errorType,
|
|
||||||
filename: m.filename,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class DatabaseService {
|
|
||||||
|
|
||||||
DatabaseService() : _log = Logger('DatabaseService');
|
|
||||||
late Isar _isar;
|
|
||||||
|
|
||||||
final Logger _log;
|
|
||||||
|
|
||||||
Future<void> initialize() async {
|
|
||||||
final dir = await getApplicationSupportDirectory();
|
|
||||||
_isar = await Isar.open(
|
|
||||||
schemas: [
|
|
||||||
DBConversationSchema,
|
|
||||||
DBRosterItemSchema,
|
|
||||||
DBMessageSchema,
|
|
||||||
DBSharedMediumSchema
|
|
||||||
],
|
|
||||||
directory: dir.path,
|
|
||||||
);
|
|
||||||
|
|
||||||
_log.finest('Database setup done');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads all conversations from the database and adds them to the state and cache.
|
|
||||||
Future<List<Conversation>> loadConversations() async {
|
|
||||||
final conversationsRaw = await _isar.dBConversations.where().findAll();
|
|
||||||
|
|
||||||
final tmp = List<Conversation>.empty(growable: true);
|
|
||||||
for (final c in conversationsRaw) {
|
|
||||||
await c.sharedMedia.load();
|
|
||||||
final rosterItem = await GetIt.I.get<RosterService>().getRosterItemByJid(c.jid);
|
|
||||||
final conv = conversationDbToModel(
|
|
||||||
c,
|
|
||||||
rosterItem != null,
|
|
||||||
rosterItem?.subscription ?? 'none',
|
|
||||||
ChatState.gone,
|
|
||||||
);
|
|
||||||
tmp.add(conv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load messages for [jid] from the database.
|
|
||||||
Future<List<Message>> loadMessagesForJid(String jid) async {
|
|
||||||
final rawMessages = await _isar.dBMessages.where().conversationJidEqualTo(jid).findAll();
|
|
||||||
final messages = List<Message>.empty(growable: true);
|
|
||||||
for (final m in rawMessages) {
|
|
||||||
await m.quotes.load();
|
|
||||||
|
|
||||||
final msg = messageDbToModel(m);
|
|
||||||
messages.add(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the conversation with id [id] inside the database.
|
|
||||||
Future<Conversation> updateConversation(int id, {
|
|
||||||
String? lastMessageBody,
|
|
||||||
int? lastChangeTimestamp,
|
|
||||||
bool? open,
|
|
||||||
int? unreadCounter,
|
|
||||||
String? avatarUrl,
|
|
||||||
List<DBSharedMedium>? sharedMedia,
|
|
||||||
ChatState? chatState,
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
final c = (await _isar.dBConversations.get(id))!;
|
|
||||||
await c.sharedMedia.load();
|
|
||||||
if (lastMessageBody != null) {
|
|
||||||
c.lastMessageBody = lastMessageBody;
|
|
||||||
}
|
|
||||||
if (lastChangeTimestamp != null) {
|
|
||||||
c.lastChangeTimestamp = lastChangeTimestamp;
|
|
||||||
}
|
|
||||||
if (open != null) {
|
|
||||||
c.open = open;
|
|
||||||
}
|
|
||||||
if (unreadCounter != null) {
|
|
||||||
c.unreadCounter = unreadCounter;
|
|
||||||
}
|
|
||||||
if (avatarUrl != null) {
|
|
||||||
c.avatarUrl = avatarUrl;
|
|
||||||
}
|
|
||||||
if (sharedMedia != null) {
|
|
||||||
c.sharedMedia.addAll(sharedMedia);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBConversations.put(c);
|
|
||||||
await c.sharedMedia.save();
|
|
||||||
});
|
|
||||||
|
|
||||||
final rosterItem = await GetIt.I.get<RosterService>().getRosterItemByJid(c.jid);
|
|
||||||
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription ?? 'none', chatState ?? ChatState.gone);
|
|
||||||
return conversation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [Conversation] inside the database given the data. This is so that the
|
|
||||||
/// [Conversation] object can carry its database id.
|
|
||||||
Future<Conversation> addConversationFromData(
|
|
||||||
String title,
|
|
||||||
String lastMessageBody,
|
|
||||||
String avatarUrl,
|
|
||||||
String jid,
|
|
||||||
int unreadCounter,
|
|
||||||
int lastChangeTimestamp,
|
|
||||||
List<DBSharedMedium> sharedMedia,
|
|
||||||
bool open,
|
|
||||||
) async {
|
|
||||||
final c = DBConversation()
|
|
||||||
..jid = jid
|
|
||||||
..title = title
|
|
||||||
..avatarUrl = avatarUrl
|
|
||||||
..lastChangeTimestamp = lastChangeTimestamp
|
|
||||||
..unreadCounter = unreadCounter
|
|
||||||
..lastMessageBody = lastMessageBody
|
|
||||||
..open = open;
|
|
||||||
|
|
||||||
c.sharedMedia.addAll(sharedMedia);
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBConversations.put(c);
|
|
||||||
});
|
|
||||||
|
|
||||||
final rosterItem = await GetIt.I.get<RosterService>().getRosterItemByJid(c.jid);
|
|
||||||
final conversation = conversationDbToModel(c, rosterItem != null, rosterItem?.subscription ?? 'none', ChatState.gone);
|
|
||||||
return conversation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like [addConversationFromData] but for [SharedMedium].
|
|
||||||
Future<DBSharedMedium> addSharedMediumFromData(String path, int timestamp, { String? mime }) async {
|
|
||||||
final s = DBSharedMedium()
|
|
||||||
..path = path
|
|
||||||
..mime = mime
|
|
||||||
..timestamp = timestamp;
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBSharedMediums.put(s);
|
|
||||||
});
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as [addConversationFromData] but for a [Message].
|
|
||||||
Future<Message> addMessageFromData(
|
|
||||||
String body,
|
|
||||||
int timestamp,
|
|
||||||
String sender,
|
|
||||||
String conversationJid,
|
|
||||||
bool isMedia,
|
|
||||||
String sid,
|
|
||||||
bool isFileUploadNotification,
|
|
||||||
{
|
|
||||||
String? srcUrl,
|
|
||||||
String? mediaUrl,
|
|
||||||
String? mediaType,
|
|
||||||
String? thumbnailData,
|
|
||||||
String? thumbnailDimensions,
|
|
||||||
String? originId,
|
|
||||||
String? quoteId,
|
|
||||||
String? filename,
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
final m = DBMessage()
|
|
||||||
..conversationJid = conversationJid
|
|
||||||
..timestamp = timestamp
|
|
||||||
..body = body
|
|
||||||
..sender = sender
|
|
||||||
..isMedia = isMedia
|
|
||||||
..mediaType = mediaType
|
|
||||||
..mediaUrl = mediaUrl
|
|
||||||
..srcUrl = srcUrl
|
|
||||||
..sid = sid
|
|
||||||
..thumbnailData = thumbnailData
|
|
||||||
..thumbnailDimensions = thumbnailDimensions
|
|
||||||
..received = false
|
|
||||||
..displayed = false
|
|
||||||
..acked = false
|
|
||||||
..originId = originId
|
|
||||||
..errorType = noError
|
|
||||||
..isFileUploadNotification = isFileUploadNotification
|
|
||||||
..filename = filename;
|
|
||||||
|
|
||||||
if (quoteId != null) {
|
|
||||||
final quotes = await getMessageByXmppId(quoteId, conversationJid);
|
|
||||||
if (quotes != null) {
|
|
||||||
m.quotes.value = quotes;
|
|
||||||
} else {
|
|
||||||
_log.warning('Failed to add quote for message with id $quoteId');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBMessages.put(m);
|
|
||||||
});
|
|
||||||
|
|
||||||
final msg = messageDbToModel(m);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DBMessage?> getMessageByXmppId(String id, String conversationJid) async {
|
|
||||||
return _isar.dBMessages.filter()
|
|
||||||
.conversationJidEqualTo(conversationJid)
|
|
||||||
.and()
|
|
||||||
.group((q) => q
|
|
||||||
.sidEqualTo(id)
|
|
||||||
.or()
|
|
||||||
.originIdEqualTo(id),
|
|
||||||
).findFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the message item with id [id] inside the database.
|
|
||||||
Future<Message> updateMessage(int id, {
|
|
||||||
String? mediaUrl,
|
|
||||||
String? mediaType,
|
|
||||||
bool? received,
|
|
||||||
bool? displayed,
|
|
||||||
bool? acked,
|
|
||||||
int? errorType,
|
|
||||||
bool? isFileUploadNotification,
|
|
||||||
String? srcUrl,
|
|
||||||
}) async {
|
|
||||||
final i = (await _isar.dBMessages.get(id))!;
|
|
||||||
if (mediaUrl != null) {
|
|
||||||
i.mediaUrl = mediaUrl;
|
|
||||||
}
|
|
||||||
if (mediaType != null) {
|
|
||||||
i.mediaType = mediaType;
|
|
||||||
}
|
|
||||||
if (received != null) {
|
|
||||||
i.received = received;
|
|
||||||
}
|
|
||||||
if (displayed != null) {
|
|
||||||
i.displayed = displayed;
|
|
||||||
}
|
|
||||||
if (acked != null) {
|
|
||||||
i.acked = acked;
|
|
||||||
}
|
|
||||||
if (errorType != null) {
|
|
||||||
i.errorType = errorType;
|
|
||||||
}
|
|
||||||
if (isFileUploadNotification != null) {
|
|
||||||
i.isFileUploadNotification = isFileUploadNotification;
|
|
||||||
}
|
|
||||||
if (srcUrl != null) {
|
|
||||||
i.srcUrl = srcUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBMessages.put(i);
|
|
||||||
});
|
|
||||||
await i.quotes.load();
|
|
||||||
|
|
||||||
final msg = messageDbToModel(i);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads roster items from the database
|
|
||||||
Future<List<RosterItem>> loadRosterItems() async {
|
|
||||||
final roster = await _isar.dBRosterItems.where().findAll();
|
|
||||||
return roster.map(rosterDbToModel).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a roster item from the database and cache
|
|
||||||
Future<void> removeRosterItem(int id) async {
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBRosterItems.delete(id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a roster item from data
|
|
||||||
Future<RosterItem> addRosterItemFromData(
|
|
||||||
String avatarUrl,
|
|
||||||
String avatarHash,
|
|
||||||
String jid,
|
|
||||||
String title,
|
|
||||||
String subscription,
|
|
||||||
String ask,
|
|
||||||
{
|
|
||||||
List<String> groups = const [],
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
final rosterItem = DBRosterItem()
|
|
||||||
..jid = jid
|
|
||||||
..title = title
|
|
||||||
..avatarUrl = avatarUrl
|
|
||||||
..avatarHash = avatarHash
|
|
||||||
..subscription = subscription
|
|
||||||
..ask = ask
|
|
||||||
..groups = groups;
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBRosterItems.put(rosterItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
final item = rosterDbToModel(rosterItem);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the roster item with id [id] inside the database.
|
|
||||||
Future<RosterItem> updateRosterItem(
|
|
||||||
int id, {
|
|
||||||
String? avatarUrl,
|
|
||||||
String? avatarHash,
|
|
||||||
String? title,
|
|
||||||
String? subscription,
|
|
||||||
String? ask,
|
|
||||||
List<String>? groups,
|
|
||||||
}
|
|
||||||
) async {
|
|
||||||
final i = (await _isar.dBRosterItems.get(id))!;
|
|
||||||
if (avatarUrl != null) {
|
|
||||||
i.avatarUrl = avatarUrl;
|
|
||||||
}
|
|
||||||
if (avatarHash != null) {
|
|
||||||
i.avatarHash = avatarHash;
|
|
||||||
}
|
|
||||||
if (title != null) {
|
|
||||||
i.title = title;
|
|
||||||
}
|
|
||||||
if (groups != null) {
|
|
||||||
i.groups = groups;
|
|
||||||
}
|
|
||||||
if (subscription != null) {
|
|
||||||
i.subscription = subscription;
|
|
||||||
}
|
|
||||||
if (ask != null) {
|
|
||||||
i.ask = ask;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _isar.writeTxn(() async {
|
|
||||||
await _isar.dBRosterItems.put(i);
|
|
||||||
});
|
|
||||||
|
|
||||||
final item = rosterDbToModel(i);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
lib/service/database/constants.dart
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const conversationsTable = 'Conversations';
|
||||||
|
const messagesTable = 'Messages';
|
||||||
|
const rosterTable = 'RosterItems';
|
||||||
|
const mediaTable = 'SharedMedia';
|
||||||
|
const preferenceTable = 'Preferences';
|
||||||
|
const omemoDeviceTable = 'OmemoDevices';
|
||||||
|
const omemoDeviceListTable = 'OmemoDeviceList';
|
||||||
|
const omemoRatchetsTable = 'OmemoSessions';
|
||||||
|
const omemoTrustCacheTable = 'OmemoTrustCacheList';
|
||||||
|
const omemoTrustDeviceListTable = 'OmemoTrustDeviceList';
|
||||||
|
const omemoTrustEnableListTable = 'OmemoTrustEnableList';
|
||||||
|
const xmppStateTable = 'XmppState';
|
||||||
|
|
||||||
|
const typeString = 0;
|
||||||
|
const typeInt = 1;
|
||||||
|
const typeBool = 2;
|
||||||
338
lib/service/database/creation.dart
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
import 'package:moxxyv2/service/database/constants.dart';
|
||||||
|
import 'package:moxxyv2/shared/models/preference.dart';
|
||||||
|
import 'package:sqflite_sqlcipher/sqflite.dart';
|
||||||
|
|
||||||
|
Future<void> configureDatabase(Database db) async {
|
||||||
|
await db.execute('PRAGMA foreign_keys = OFF');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createDatabase(Database db, int version) async {
|
||||||
|
// XMPP state
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $xmppStateTable (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $messagesTable (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
sender TEXT NOT NULL,
|
||||||
|
body TEXT,
|
||||||
|
timestamp INTEGER NOT NULL,
|
||||||
|
sid TEXT NOT NULL,
|
||||||
|
conversationJid TEXT NOT NULL,
|
||||||
|
isMedia INTEGER NOT NULL,
|
||||||
|
isFileUploadNotification INTEGER NOT NULL,
|
||||||
|
encrypted INTEGER NOT NULL,
|
||||||
|
errorType INTEGER,
|
||||||
|
warningType INTEGER,
|
||||||
|
mediaUrl TEXT,
|
||||||
|
mediaType TEXT,
|
||||||
|
thumbnailData TEXT,
|
||||||
|
mediaWidth INTEGER,
|
||||||
|
mediaHeight INTEGER,
|
||||||
|
srcUrl TEXT,
|
||||||
|
key TEXT,
|
||||||
|
iv TEXT,
|
||||||
|
encryptionScheme TEXT,
|
||||||
|
received INTEGER,
|
||||||
|
displayed INTEGER,
|
||||||
|
acked INTEGER,
|
||||||
|
originId TEXT,
|
||||||
|
quote_id INTEGER,
|
||||||
|
filename TEXT,
|
||||||
|
plaintextHashes TEXT,
|
||||||
|
ciphertextHashes TEXT,
|
||||||
|
isDownloading INTEGER NOT NULL,
|
||||||
|
isUploading INTEGER NOT NULL,
|
||||||
|
mediaSize INTEGER,
|
||||||
|
isRetracted INTEGER,
|
||||||
|
CONSTRAINT fk_quote FOREIGN KEY (quote_id) REFERENCES $messagesTable (id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Conversations
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $conversationsTable (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
avatarUrl TEXT NOT NULL,
|
||||||
|
lastChangeTimestamp INTEGER NOT NULL,
|
||||||
|
unreadCounter INTEGER NOT NULL,
|
||||||
|
open INTEGER NOT NULL,
|
||||||
|
muted INTEGER NOT NULL,
|
||||||
|
encrypted INTEGER NOT NULL,
|
||||||
|
lastMessageId INTEGER NOT NULL,
|
||||||
|
CONSTRAINT fk_last_message FOREIGN KEY (lastMessageId) REFERENCES $messagesTable (id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Shared media
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $mediaTable (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
path TEXT NOT NULL,
|
||||||
|
mime TEXT,
|
||||||
|
timestamp INTEGER NOT NULL,
|
||||||
|
conversation_id INTEGER NOT NULL,
|
||||||
|
message_id INTEGER,
|
||||||
|
FOREIGN KEY (conversation_id) REFERENCES $conversationsTable (id),
|
||||||
|
FOREIGN KEY (message_id) REFERENCES $messagesTable (id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Roster
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $rosterTable (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
avatarUrl TEXT NOT NULL,
|
||||||
|
avatarHash TEXT NOT NULL,
|
||||||
|
subscription TEXT NOT NULL,
|
||||||
|
ask TEXT NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// OMEMO
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoRatchetsTable (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
dhs TEXT NOT NULL,
|
||||||
|
dhs_pub TEXT NOT NULL,
|
||||||
|
dhr TEXT,
|
||||||
|
rk TEXT NOT NULL,
|
||||||
|
cks TEXT,
|
||||||
|
ckr TEXT,
|
||||||
|
ns INTEGER NOT NULL,
|
||||||
|
nr INTEGER NOT NULL,
|
||||||
|
pn INTEGER NOT NULL,
|
||||||
|
ik_pub TEXT NOT NULL,
|
||||||
|
session_ad TEXT NOT NULL,
|
||||||
|
acknowledged INTEGER NOT NULL,
|
||||||
|
mkskipped TEXT NOT NULL,
|
||||||
|
kex_timestamp INTEGER NOT NULL,
|
||||||
|
kex TEXT,
|
||||||
|
PRIMARY KEY (jid, id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoTrustCacheTable (
|
||||||
|
key TEXT PRIMARY KEY NOT NULL,
|
||||||
|
trust INTEGER NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoTrustDeviceListTable (
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
device INTEGER NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoTrustEnableListTable (
|
||||||
|
key TEXT PRIMARY KEY NOT NULL,
|
||||||
|
enabled INTEGER NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoDeviceTable (
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
data TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (jid, id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $omemoDeviceListTable (
|
||||||
|
jid TEXT NOT NULL,
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (jid, id)
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
CREATE TABLE $preferenceTable (
|
||||||
|
key TEXT NOT NULL PRIMARY KEY,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
value TEXT NOT NULL
|
||||||
|
)''',
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'sendChatMarkers',
|
||||||
|
typeBool,
|
||||||
|
'true',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'sendChatStates',
|
||||||
|
typeBool,
|
||||||
|
'true',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'showSubscriptionRequests',
|
||||||
|
typeBool,
|
||||||
|
'true',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'autoDownloadWifi',
|
||||||
|
typeBool,
|
||||||
|
'true',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'autoDownloadMobile',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'maximumAutoDownloadSize',
|
||||||
|
typeInt,
|
||||||
|
'15',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'backgroundPath',
|
||||||
|
typeString,
|
||||||
|
'',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'isAvatarPublic',
|
||||||
|
typeBool,
|
||||||
|
'true',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'autoAcceptSubscriptionRequests',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'debugEnabled',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'debugPassphrase',
|
||||||
|
typeString,
|
||||||
|
'',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'debugIp',
|
||||||
|
typeString,
|
||||||
|
'',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'debugPort',
|
||||||
|
typeInt,
|
||||||
|
'-1',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'twitterRedirect',
|
||||||
|
typeString,
|
||||||
|
'',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'youtubeRedirect',
|
||||||
|
typeString,
|
||||||
|
'',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'enableTwitterRedirect',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'enableYoutubeRedirect',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'defaultMuteState',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'enableOmemoByDefault',
|
||||||
|
typeBool,
|
||||||
|
'false',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
await db.insert(
|
||||||
|
preferenceTable,
|
||||||
|
Preference(
|
||||||
|
'languageLocaleCode',
|
||||||
|
typeString,
|
||||||
|
'default',
|
||||||
|
).toDatabaseJson(),
|
||||||
|
);
|
||||||
|
}
|
||||||