├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── migrations.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ ├── Bookwyrm-v1.3.8.apk │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── nl │ │ └── privacydragon │ │ └── bookwyrm │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── nl │ │ │ └── privacydragon │ │ │ └── bookwyrm │ │ │ ├── HandlerActivity.java │ │ │ ├── MainActivity.java │ │ │ └── StartActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_start.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ ├── ic_launcher_round.xml │ │ ├── ic_launcher_wyrm.xml │ │ └── ic_launcher_wyrm_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.webp │ │ ├── ic_launcher_wyrm.png │ │ ├── ic_launcher_wyrm_foreground.png │ │ └── ic_launcher_wyrm_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.webp │ │ ├── ic_launcher_wyrm.png │ │ ├── ic_launcher_wyrm_foreground.png │ │ └── ic_launcher_wyrm_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.webp │ │ ├── ic_launcher_wyrm.png │ │ ├── ic_launcher_wyrm_foreground.png │ │ └── ic_launcher_wyrm_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.webp │ │ ├── ic_launcher_wyrm.png │ │ ├── ic_launcher_wyrm_foreground.png │ │ └── ic_launcher_wyrm_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.webp │ │ ├── ic_launcher_wyrm.png │ │ ├── ic_launcher_wyrm_foreground.png │ │ └── ic_launcher_wyrm_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_wyrm_background.xml │ │ ├── strings.xml │ │ ├── theme_overlays.xml │ │ └── themes.xml │ │ └── xml │ │ └── backup_rules.xml │ └── test │ └── java │ └── nl │ └── privacydragon │ └── bookwyrm │ └── ExampleUnitTest.java ├── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ ├── 10.txt │ ├── 11.txt │ ├── 12.txt │ ├── 13.txt │ ├── 14.txt │ ├── 15.txt │ ├── 6.txt │ ├── 7.txt │ ├── 8.txt │ └── 9.txt │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ └── 5.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Experiencing a bug? Report it so it can be caught! 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **How to reproduce** 14 | Steps to reproduce the behaviour: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behaviour** 21 | A clear and concise description of what you expected to happen (unless that is so obvious that it would be particularly hard to miss). 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **System details** 27 | - Android version [e.g. Android 7] 28 | - App version [e.g. 1.3.8] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | 33 | **Relevant?** 34 | - [ ] This is an issue specifically with the Android app 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Bookwyrm -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Privacy_Dragon 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bookwyrm-android 2 | An Android application for Bookwyrm. Basically, it is just bookwyrm put into a 'webview' element. 3 | What it does? It enables you to use BookWyrm on your Android phone without having to use a browser to go to it every time. 4 | It can also open links to userpages of Bookwyrm users and it can scan ISBN codes. 5 | 6 | This application works on: Android 6 and above. 7 | 8 | And if you want to know, I am [`@StoryDragon@books.storydragon.nl`](https://books.storydragon.nl/user/storydragon) on BookWyrm. 9 | 10 | For the ISBN-scanning I use the library zxing-android-embedded. 11 | The barcode icon that is added for instances that do not have ISBN-scanning by default comes from the Remix Icon project. 12 | 13 | [Get it on F-Droid](https://f-droid.org/packages/nl.privacydragon.bookwyrm/) 16 | 17 | Or download the latest APK from the [Releases Section](https://github.com/PrivacyDragon/Bookwyrm_Android/releases/latest). 18 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 34 7 | 8 | defaultConfig { 9 | applicationId "nl.privacydragon.bookwyrm" 10 | minSdk 23 11 | targetSdk 34 12 | versionCode 15 13 | versionName "1.3.8" 14 | 15 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | vcsInfo.include false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | namespace 'nl.privacydragon.bookwyrm' 30 | dependenciesInfo { 31 | includeInApk false 32 | includeInBundle false 33 | } 34 | } 35 | 36 | dependencies { 37 | 38 | implementation 'androidx.appcompat:appcompat:1.7.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.2.0' 40 | implementation 'com.google.android.material:material:1.12.0' 41 | implementation platform('org.jetbrains.kotlin:kotlin-bom:2.1.0') 42 | testImplementation 'junit:junit:4.13.2' 43 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 44 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 45 | implementation 'com.google.zxing:core:3.5.3' 46 | implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar' 47 | implementation 'org.conscrypt:conscrypt-android:2.5.2' 48 | implementation 'com.squareup.okhttp3:okhttp:4.12.0' 49 | 50 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/release/Bookwyrm-v1.3.8.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/release/Bookwyrm-v1.3.8.apk -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "nl.privacydragon.bookwyrm", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 15, 15 | "versionName": "1.3.8", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File", 20 | "baselineProfiles": [ 21 | { 22 | "minApi": 28, 23 | "maxApi": 30, 24 | "baselineProfiles": [ 25 | "baselineProfiles/1/app-release.dm" 26 | ] 27 | }, 28 | { 29 | "minApi": 31, 30 | "maxApi": 2147483647, 31 | "baselineProfiles": [ 32 | "baselineProfiles/0/app-release.dm" 33 | ] 34 | } 35 | ], 36 | "minSdkVersionForDexing": 23 37 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/nl/privacydragon/bookwyrm/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package nl.privacydragon.bookwyrm; 2 | 3 | import android.content.Context; 4 | import androidx.test.InstrumentationRegistry; 5 | import androidx.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | assertEquals("nl.privacydragon.bookwyrm", appContext.getPackageName()); 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 759 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/nl/privacydragon/bookwyrm/HandlerActivity.java: -------------------------------------------------------------------------------- 1 | package nl.privacydragon.bookwyrm; 2 | 3 | import android.Manifest; 4 | import android.annotation.SuppressLint; 5 | import android.app.Activity; 6 | import android.content.ActivityNotFoundException; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.content.pm.PackageManager; 11 | import android.graphics.Bitmap; 12 | import android.net.Uri; 13 | import android.os.Bundle; 14 | import android.view.KeyEvent; 15 | import android.view.View; 16 | import android.webkit.JavascriptInterface; 17 | import android.webkit.ValueCallback; 18 | import android.webkit.WebChromeClient; 19 | import android.webkit.WebResourceRequest; 20 | import android.webkit.WebView; 21 | import android.webkit.WebViewClient; 22 | import android.widget.ProgressBar; 23 | import android.widget.Toast; 24 | 25 | import androidx.activity.result.ActivityResultLauncher; 26 | import androidx.activity.result.contract.ActivityResultContracts; 27 | import androidx.appcompat.app.AppCompatActivity; 28 | import androidx.core.app.ActivityCompat; 29 | import androidx.core.content.ContextCompat; 30 | 31 | import com.journeyapps.barcodescanner.ScanContract; 32 | import com.journeyapps.barcodescanner.ScanOptions; 33 | 34 | public class HandlerActivity extends AppCompatActivity { 35 | 36 | WebView myWebView; 37 | ProgressBar LoadIndicator; 38 | public ValueCallback omhooglader; 39 | @SuppressLint("SetJavaScriptEnabled") 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_start); 44 | // ATTENTION: This was auto-generated to handle app links. 45 | Intent appLinkIntent = getIntent(); 46 | String appLinkAction = appLinkIntent.getAction(); 47 | Uri appLinkData = appLinkIntent.getData(); 48 | // End of auto-generated stuff 49 | ActivityResultLauncher voodooLauncher = registerForActivityResult( 50 | new ActivityResultContracts.StartActivityForResult(), 51 | result -> { 52 | if (result.getResultCode() == Activity.RESULT_OK) { 53 | // There are no request codes 54 | Intent data = result.getData(); 55 | if (omhooglader == null) 56 | return; 57 | omhooglader.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), data)); 58 | } else { 59 | omhooglader.onReceiveValue(null); 60 | } 61 | }); 62 | LoadIndicator = (ProgressBar) findViewById(R.id.progressBar3); 63 | myWebView = (WebView) findViewById(R.id.webview); 64 | myWebView.setVisibility(View.GONE); 65 | myWebView.getSettings().setJavaScriptEnabled(true); 66 | myWebView.getSettings().setDomStorageEnabled(true); 67 | myWebView.getSettings().setAllowFileAccess(true); 68 | myWebView.getSettings().setAllowContentAccess(true); 69 | myWebView.addJavascriptInterface(new Object() 70 | { 71 | @JavascriptInterface // For API 17+ 72 | public void performClick(String what) 73 | { 74 | if (!what.contains("[object Window]")) { //For some reason the function has to be called when the event listener is attached to the button. So, by adding in 'this', it is possible to make sure to only act when the thing that called the function is NOT the window, but the button. 75 | ScanBarCode(); 76 | } 77 | 78 | } 79 | }, "scan"); 80 | myWebView.setWebChromeClient(new WebChromeClient() { 81 | @Override 82 | public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { 83 | if (omhooglader != null) { 84 | omhooglader = null; 85 | } 86 | omhooglader = filePathCallback; 87 | Intent intent = fileChooserParams.createIntent(); 88 | try { 89 | voodooLauncher.launch(intent); 90 | } catch (ActivityNotFoundException grrr){ 91 | omhooglader = null; 92 | return false; 93 | } 94 | return true; 95 | } 96 | }); 97 | //The name of the user's server is stored in the shared preferences, so first it has to be read from there. 98 | String defaultValue = "none"; 99 | SharedPreferences sharedPref = HandlerActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 100 | String server = sharedPref.getString(getString(R.string.server), defaultValue); 101 | if (server.equals("none")) { 102 | startActivity(new Intent(HandlerActivity.this, nl.privacydragon.bookwyrm.MainActivity.class)); 103 | } 104 | String pathMaybe = appLinkData.getPath(); 105 | String toGoServer = "bla"; 106 | //This bit of code regelt wanneer de webpagina wordt weergegeven. It is quite handig then om dan ook "book" toe te laten, zodat ook boeken in de app bekeken kunnen worden... 107 | if (pathMaybe.contains("user") || pathMaybe.contains("book")) { 108 | //If the path contains 'user', it is a user profile, unless it is followed by something like 'review'. 109 | if (pathMaybe.contains("review") || pathMaybe.contains("generatednote") || pathMaybe.contains("quotation") || pathMaybe.contains("comment") || pathMaybe.contains("book")) { 110 | toGoServer = "https://" + appLinkData.getHost() + pathMaybe; 111 | } 112 | else { 113 | String notAtUser = pathMaybe.substring(pathMaybe.indexOf("user") + 5); //This line gets the username. 114 | String atUser = notAtUser + "@" + appLinkData.getHost(); //This appends @[HOST] to the string, so we have the full username thing needed. 115 | toGoServer = "https://" + server + "/user/" + atUser; 116 | } 117 | } else { 118 | //If the toGoServer string remains "bla", dan zal de user when they teruggaan uitkomen op https://bla/, which is 'not allowed'. 119 | //So, maybe here, just to be sure, assign the value of the user's own server to the toGoServer variabele. 120 | //After that, since apparently I have decided that the URL the user tried to follow is not valid in my application, redirect them to StartActivity. 121 | toGoServer = "https://" + server; 122 | startActivity(new Intent(HandlerActivity.this, nl.privacydragon.bookwyrm.StartActivity.class)); 123 | } 124 | 125 | //A webviewclient thing is needed for some stuff. To automatically log in, the credentials are put in the form by the javascript that is loaded once the page is fully loaded. Then it is automatically submitted if the current page is the login page. 126 | String finalToGoServer = toGoServer; 127 | myWebView.setWebViewClient(new HandlerActivity.MyWebViewClient() { 128 | public void onPageFinished(WebView view, String url) { 129 | LoadIndicator.setVisibility(View.GONE); 130 | myWebView.setVisibility(View.VISIBLE); 131 | view.loadUrl("javascript:(function() { if (/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) {" + 132 | "blocks = document.getElementsByClassName('block');" + 133 | "for (let element of blocks){" + 134 | "if (element.localName == 'header') { " + 135 | "element.innerHTML = ` \n" + 136 | "\n" + 137 | "Back to homeserver\n" + 138 | "`;" + 139 | "break;" + 140 | "}" + 141 | "}" + 142 | "} ;})()"); 143 | view.loadUrl("javascript:(function() { " + 144 | "if (document.querySelectorAll(\"[data-modal-open]\")[0]) {" + 145 | "let ISBN_Button = document.querySelectorAll(\"[data-modal-open]\")[0];" + 146 | "ISBN_Button.replaceWith(ISBN_Button.cloneNode(true));" + 147 | "document.querySelectorAll(\"[data-modal-open]\")[0].addEventListener('click', () => {" + 148 | "scan.performClick(this);" + 149 | "});" + 150 | "} else {" + 151 | "let ISBN = document.createElement(\"div\");" + 152 | "ISBN.class = 'control';" + 153 | "ISBN.innerHTML = '';" + 154 | "nav = document.getElementsByClassName(\"field has-addons\")[0];" + 155 | "nav.appendChild(ISBN);" + 156 | "}" + 157 | ";})()"); 158 | } 159 | }); 160 | //Here, load the login page of the server. That actually does all that is needed. 161 | myWebView.loadUrl(toGoServer); 162 | } 163 | 164 | private final ActivityResultLauncher barcodeLanceerder = registerForActivityResult(new ScanContract(), 165 | result -> { 166 | if(result.getContents() == null) { 167 | Toast.makeText(HandlerActivity.this, "Cancelled", Toast.LENGTH_LONG).show(); 168 | } else { 169 | Toast.makeText(HandlerActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); 170 | myWebView.loadUrl("Javascript:(function() {" + 171 | "try {" + 172 | "document.getElementById('tour-search').value = " + result.getContents() + ";" + 173 | "} catch {" + 174 | "document.getElementById('search_input').value = " + result.getContents() + ";" + 175 | "}" + 176 | "document.getElementsByTagName('form')[0].submit();" + 177 | ";})()"); 178 | LoadIndicator.setVisibility(View.VISIBLE); 179 | } 180 | }); 181 | 182 | public void ScanBarCode() { 183 | String permission = Manifest.permission.CAMERA; 184 | int grant = ContextCompat.checkSelfPermission(HandlerActivity.this, permission); 185 | if (grant != PackageManager.PERMISSION_GRANTED) { 186 | String[] permission_list = new String[1]; 187 | permission_list[0] = permission; 188 | ActivityCompat.requestPermissions(HandlerActivity.this, permission_list, 1); 189 | } 190 | ScanOptions eisen = new ScanOptions(); 191 | eisen.setDesiredBarcodeFormats(ScanOptions.EAN_13); 192 | eisen.setBeepEnabled(true); 193 | eisen.setCameraId(0); 194 | eisen.setPrompt("SCAN ISBN"); 195 | eisen.setBarcodeImageEnabled(false); 196 | barcodeLanceerder.launch(eisen); 197 | } 198 | 199 | @Override 200 | public boolean onKeyUp(int keyCode, KeyEvent event) { 201 | // Check if the key event was the Back button and if there's history 202 | if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { 203 | myWebView.goBack(); 204 | return true; 205 | } 206 | // If it wasn't the Back key or there's no web page history, bubble up to the default 207 | // system behavior (probably exit the activity) 208 | return super.onKeyUp(keyCode, event); 209 | } 210 | @Override 211 | public boolean onKeyLongPress(int keyCode, KeyEvent event) { 212 | if (keyCode == KeyEvent.KEYCODE_BACK) { 213 | finish(); 214 | return true; 215 | } 216 | return super.onKeyLongPress(keyCode, event); 217 | } 218 | //Here is code to make sure that links of the bookwyrm server are handled within the webview client, instead of having it open in the default browser. 219 | //Yes, I used the web for this too. 220 | private class MyWebViewClient extends WebViewClient { 221 | @Override 222 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { 223 | // ATTENTION: This was auto-generated to handle app links. 224 | Intent appLinkIntent = getIntent(); 225 | String appLinkAction = appLinkIntent.getAction(); 226 | Uri appLinkData = appLinkIntent.getData(); 227 | // End of auto-generated stuff 228 | String strangeHost = appLinkData.getHost(); 229 | SharedPreferences sharedPref = HandlerActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 230 | String defaultValue = "none"; 231 | String server = sharedPref.getString(getString(R.string.server), defaultValue); 232 | if (server.equals(request.getUrl().getHost())) { 233 | //If the server is the same as the bookwyrm, load it in the webview. 234 | return false; 235 | } else if (strangeHost.equals(request.getUrl().getHost())) { 236 | return false; 237 | } 238 | // Otherwise, it should go to the default browser instead. 239 | Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); 240 | startActivity(intent); 241 | return true; 242 | } 243 | 244 | @Override 245 | public void onPageStarted(WebView view, String url, Bitmap favicon) { 246 | LoadIndicator.setVisibility(View.VISIBLE); 247 | } 248 | 249 | } 250 | } -------------------------------------------------------------------------------- /app/src/main/java/nl/privacydragon/bookwyrm/MainActivity.java: -------------------------------------------------------------------------------- 1 | package nl.privacydragon.bookwyrm; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.graphics.Color; 7 | import android.security.keystore.KeyGenParameterSpec; 8 | import android.security.keystore.KeyProperties; 9 | import android.os.Bundle; 10 | import android.util.Base64; 11 | import android.view.View; 12 | import android.widget.EditText; 13 | import android.widget.TextView; 14 | 15 | import androidx.appcompat.app.AppCompatActivity; 16 | 17 | import java.io.IOException; 18 | import java.security.InvalidAlgorithmParameterException; 19 | import java.security.InvalidKeyException; 20 | import java.security.Key; 21 | import java.security.KeyStore; 22 | import java.security.KeyStoreException; 23 | import java.security.NoSuchAlgorithmException; 24 | import java.security.NoSuchProviderException; 25 | import java.security.UnrecoverableKeyException; 26 | import java.security.cert.CertificateException; 27 | import java.util.Random; 28 | 29 | import javax.crypto.BadPaddingException; 30 | import javax.crypto.Cipher; 31 | import javax.crypto.IllegalBlockSizeException; 32 | import javax.crypto.KeyGenerator; 33 | import javax.crypto.NoSuchPaddingException; 34 | import javax.crypto.spec.GCMParameterSpec; 35 | 36 | public class MainActivity extends AppCompatActivity { 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_main); 42 | //Check whether there is something stored. Only if there is already something stored, proceed to BookWyrm. 43 | SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 44 | String defaultValue = "none"; 45 | String server = sharedPref.getString(getString(R.string.server), defaultValue); 46 | if (!"none".equals(server)) { 47 | startActivity(new Intent(MainActivity.this, nl.privacydragon.bookwyrm.StartActivity.class)); 48 | } 49 | } 50 | 51 | private static String getRandomString() //I just copied this from internet. Yes, I am lazy :). (https://stackoverflow.com/questions/12116092/android-random-string-generator#answer-12116194) 52 | { 53 | String ALLOWED_CHARACTERS ="0123456789qwertyuiopasdfghjklzxcvbnm!@#$%^&*()_+=][{}"; 54 | final Random random=new Random(); 55 | final StringBuilder sb=new StringBuilder(12); 56 | for(int i = 0; i< 12; ++i) { 57 | sb.append(ALLOWED_CHARACTERS.charAt(random.nextInt(ALLOWED_CHARACTERS.length()))); 58 | } 59 | return sb.toString(); 60 | } 61 | 62 | public void LogIn(View view) throws IllegalBlockSizeException, BadPaddingException, KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, UnrecoverableKeyException, NoSuchPaddingException, InvalidKeyException { 63 | //Declaring some things needed. Getting the user input. 64 | EditText serverInput = (EditText) findViewById(R.id.Instance); 65 | String server = String.valueOf(serverInput.getText()); 66 | EditText passInput = (EditText) findViewById(R.id.Password); 67 | String pass = String.valueOf(passInput.getText()); 68 | EditText nameInput = (EditText) findViewById(R.id.Username); 69 | String name = String.valueOf(nameInput.getText()); 70 | //All fields are required, so if one of them is empty, the user should see a warning. 71 | if (server.isEmpty() || pass.isEmpty() || name.isEmpty()) { 72 | TextView ErrorMessage = (TextView) findViewById(R.id.textView5); 73 | ErrorMessage.setTextColor(Color.YELLOW); 74 | ErrorMessage.setText("ERROR: All fields are required!"); 75 | } else { 76 | //Likely this will be the first time the program is run. So create a new key thing in the android key store happening. 77 | KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 78 | keyStore.load(null); 79 | if (!keyStore.containsAlias("BookWyrm")) { //Actually, the new key is made here, if it does not exist already. 80 | KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); 81 | keyGenerator.init( 82 | new KeyGenParameterSpec.Builder("BookWyrm", 83 | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 84 | .setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 85 | .setRandomizedEncryptionRequired(false) 86 | .build()); 87 | keyGenerator.generateKey(); 88 | } 89 | //Grab the key and initiate the encryption process stuff. For this, a random fixed IV code is generated. 90 | Key DragonLikeKey = keyStore.getKey("BookWyrm", null); 91 | Cipher c = Cipher.getInstance("AES/GCM/NoPadding"); 92 | String magicCode = getRandomString(); 93 | c.init(Cipher.ENCRYPT_MODE, DragonLikeKey, new GCMParameterSpec(128, magicCode.getBytes())); 94 | //And now do the encryption! 95 | byte[] passBytes = c.doFinal(pass.getBytes()); 96 | String passUse = Base64.encodeToString(passBytes, Base64.DEFAULT); 97 | //And then all the things are stored in the shared preferences. 98 | //Therefore, first all the shared preferences objects are loaded. 99 | SharedPreferences sharedPref = MainActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 100 | SharedPreferences sharedPrefName = MainActivity.this.getSharedPreferences(getString(R.string.name), Context.MODE_PRIVATE); 101 | SharedPreferences sharedPrefPass = MainActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE); 102 | SharedPreferences sharedPrefMagic = MainActivity.this.getSharedPreferences(getString(R.string.q), Context.MODE_PRIVATE); 103 | //Then the 'edit' stuff are made for them. 104 | SharedPreferences.Editor editorName = sharedPrefName.edit(); 105 | SharedPreferences.Editor editorPass = sharedPrefPass.edit(); 106 | SharedPreferences.Editor editorMagic = sharedPrefMagic.edit(); 107 | SharedPreferences.Editor editor = sharedPref.edit(); 108 | //And finally, the values are written to them. 109 | editor.putString(getString(R.string.server), server); 110 | editor.apply(); 111 | editorName.putString(getString(R.string.name), name); 112 | editorName.apply(); 113 | editorPass.putString(getString(R.string.pw), passUse); 114 | editorPass.apply(); 115 | editorMagic.putString(getString(R.string.q), magicCode); 116 | editorMagic.apply(); 117 | //Once all that has been done, Bookwyrm can be opened and such! 118 | startActivity(new Intent(MainActivity.this, nl.privacydragon.bookwyrm.StartActivity.class)); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /app/src/main/java/nl/privacydragon/bookwyrm/StartActivity.java: -------------------------------------------------------------------------------- 1 | package nl.privacydragon.bookwyrm; 2 | 3 | import android.Manifest; 4 | import android.annotation.SuppressLint; 5 | import android.app.Activity; 6 | import android.content.ActivityNotFoundException; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.content.pm.PackageManager; 11 | import android.graphics.Bitmap; 12 | import android.net.Uri; 13 | import android.os.Bundle; 14 | import android.util.Base64; 15 | import android.view.KeyEvent; 16 | import android.view.View; 17 | import android.webkit.CookieManager; 18 | import android.webkit.JavascriptInterface; 19 | import android.webkit.ValueCallback; 20 | import android.webkit.WebChromeClient; 21 | import android.webkit.WebResourceRequest; 22 | import android.webkit.WebView; 23 | import android.webkit.WebViewClient; 24 | import android.widget.ProgressBar; 25 | import android.widget.Toast; 26 | 27 | import androidx.activity.result.ActivityResultLauncher; 28 | import androidx.activity.result.contract.ActivityResultContracts; 29 | import androidx.appcompat.app.AppCompatActivity; 30 | import androidx.core.app.ActivityCompat; 31 | import androidx.core.content.ContextCompat; 32 | 33 | import com.journeyapps.barcodescanner.ScanContract; 34 | import com.journeyapps.barcodescanner.ScanOptions; 35 | 36 | import org.conscrypt.Conscrypt; 37 | 38 | import java.io.IOException; 39 | import java.io.UnsupportedEncodingException; 40 | import java.net.URLEncoder; 41 | import java.nio.charset.StandardCharsets; 42 | import java.security.InvalidAlgorithmParameterException; 43 | import java.security.InvalidKeyException; 44 | import java.security.Key; 45 | import java.security.KeyStore; 46 | import java.security.KeyStoreException; 47 | import java.security.NoSuchAlgorithmException; 48 | import java.security.Security; 49 | import java.security.UnrecoverableKeyException; 50 | import java.security.cert.CertificateException; 51 | 52 | import javax.crypto.BadPaddingException; 53 | import javax.crypto.Cipher; 54 | import javax.crypto.IllegalBlockSizeException; 55 | import javax.crypto.NoSuchPaddingException; 56 | import javax.crypto.spec.GCMParameterSpec; 57 | 58 | import okhttp3.FormBody; 59 | import okhttp3.Headers; 60 | import okhttp3.Interceptor; 61 | import okhttp3.OkHttpClient; 62 | import okhttp3.Request; 63 | import okhttp3.RequestBody; 64 | import okhttp3.Response; 65 | 66 | public class StartActivity extends AppCompatActivity { 67 | WebView myWebView; 68 | ProgressBar LoadIndicator; 69 | public ValueCallback omhooglader; 70 | String putje = ""; 71 | String sessie = ""; 72 | @SuppressLint("SetJavaScriptEnabled") 73 | @Override 74 | protected void onCreate(Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | setContentView(R.layout.activity_start); 77 | LoadIndicator = (ProgressBar) findViewById(R.id.progressBar3); 78 | ActivityResultLauncher voodooLauncher = registerForActivityResult( 79 | new ActivityResultContracts.StartActivityForResult(), 80 | result -> { 81 | if (result.getResultCode() == Activity.RESULT_OK) { 82 | // There are no request codes 83 | Intent data = result.getData(); 84 | if (omhooglader == null) 85 | return; 86 | omhooglader.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), data)); 87 | } 88 | else { 89 | omhooglader.onReceiveValue(null); 90 | } 91 | }); 92 | myWebView = (WebView) findViewById(R.id.webview); 93 | myWebView.setVisibility(View.GONE); 94 | myWebView.getSettings().setUserAgentString(getString(R.string.gebruikersagent)); 95 | myWebView.getSettings().setJavaScriptEnabled(true); 96 | myWebView.addJavascriptInterface(new Object() 97 | { 98 | @JavascriptInterface // For API 17+ 99 | public void performClick(String what) 100 | { 101 | if (!what.contains("[object Window]")) { //For some reason the function has to be called when the event listener is attached to the button. So, by adding in 'this', it is possible to make sure to only act when the thing that called the function is NOT the window, but the button. 102 | ScanBarCode(); 103 | } 104 | 105 | } 106 | }, "scan"); 107 | myWebView.setWebChromeClient(new WebChromeClient() { 108 | @Override 109 | public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { 110 | if (omhooglader != null) { 111 | //omhooglader.onReceiveValue(null); 112 | omhooglader = null; 113 | } 114 | omhooglader = filePathCallback; 115 | Intent intent = fileChooserParams.createIntent(); 116 | try { 117 | voodooLauncher.launch(intent); 118 | } catch (ActivityNotFoundException grrr){ 119 | omhooglader = null; 120 | return false; 121 | } 122 | return true; 123 | } 124 | }); 125 | //The user credentials are stored in the shared preferences, so first they have to be read from there. 126 | String defaultValue = "none"; 127 | SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 128 | String server = sharedPref.getString(getString(R.string.server), defaultValue); 129 | SharedPreferences sharedPrefName = StartActivity.this.getSharedPreferences(getString(R.string.name), Context.MODE_PRIVATE); 130 | String name = sharedPrefName.getString(getString(R.string.name), defaultValue); 131 | SharedPreferences sharedPrefPass = StartActivity.this.getSharedPreferences(getString(R.string.pw), Context.MODE_PRIVATE); 132 | String pass = sharedPrefPass.getString(getString(R.string.pw), defaultValue); 133 | SharedPreferences sharedPrefMagic = StartActivity.this.getSharedPreferences(getString(R.string.q), Context.MODE_PRIVATE); 134 | String codeMagic = sharedPrefMagic.getString(getString(R.string.q), defaultValue); 135 | //Then all the decryption stuff has to happen. There are a lot of try-catch stuff, because apparently that seems to be needed. 136 | //First get the keystore thing. 137 | KeyStore keyStore = null; 138 | try { 139 | keyStore = KeyStore.getInstance("AndroidKeyStore"); 140 | } catch (KeyStoreException e) { 141 | e.printStackTrace(); 142 | } 143 | //Then, load it. or something. To make sure that it can be used. 144 | try { 145 | keyStore.load(null); 146 | } catch (CertificateException | IOException | NoSuchAlgorithmException e) { 147 | e.printStackTrace(); 148 | } 149 | //Next, retrieve the key to be used for the decryption. 150 | Key DragonLikeKey = null; 151 | try { 152 | DragonLikeKey = keyStore.getKey("BookWyrm", null); 153 | } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { 154 | e.printStackTrace(); 155 | } 156 | //Do something with getting the/a cipher or something. 157 | Cipher c = null; 158 | try { 159 | c = Cipher.getInstance("AES/GCM/NoPadding"); 160 | } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 161 | e.printStackTrace(); 162 | } 163 | //And then initiating the cipher, so it can be used. 164 | try { 165 | assert c != null; 166 | c.init(Cipher.DECRYPT_MODE, DragonLikeKey, new GCMParameterSpec(128, codeMagic.getBytes())); 167 | } catch (InvalidAlgorithmParameterException | InvalidKeyException e) { 168 | e.printStackTrace(); 169 | } 170 | //Decrypt the password! 171 | byte[] truePass = null; 172 | try { 173 | truePass = c.doFinal(Base64.decode(pass, Base64.DEFAULT)); 174 | } catch (BadPaddingException | IllegalBlockSizeException e) { 175 | e.printStackTrace(); 176 | } 177 | //Convert the decrypted password back to a string. 178 | String passw = new String(truePass, StandardCharsets.UTF_8); 179 | //String wacht = passw.replaceAll("'", "\\\\'"); 180 | 181 | //A webviewclient thing is needed for some stuff. To automatically log in, the credentials are put in the form by the javascript that is loaded once the page is fully loaded. Then it is automatically submitted if the current page is the login page. 182 | myWebView.setWebViewClient(new MyWebViewClient(){ 183 | public void onPageFinished(WebView view, String url) { 184 | LoadIndicator.setVisibility(View.GONE); 185 | myWebView.setVisibility(View.VISIBLE); 186 | view.loadUrl("javascript:(function() { " + 187 | "if (document.querySelectorAll(\"[data-modal-open]\")[0]) {" + 188 | "let ISBN_Button = document.querySelectorAll(\"[data-modal-open]\")[0];" + 189 | "ISBN_Button.replaceWith(ISBN_Button.cloneNode(true));" + 190 | "document.querySelectorAll(\"[data-modal-open]\")[0].addEventListener('click', () => {" + 191 | "scan.performClick(this);" + 192 | "});" + 193 | "} else {" + 194 | "let ISBN = document.createElement(\"div\");" + 195 | "ISBN.class = 'control';" + 196 | "ISBN.innerHTML = '';" + 197 | "nav = document.getElementsByClassName(\"field has-addons\")[0];" + 198 | "nav.appendChild(ISBN);" + 199 | "}" + 200 | ";})()"); //This lines replace the ISBN-scan button event listener with one that points to the on-device scanning implementation, if it is available on the instance. If not, the button is added. 201 | 202 | } 203 | }); 204 | CookieManager oven = CookieManager.getInstance(); 205 | String koek = oven.getCookie("https://" + server); 206 | if (koek != null) { 207 | if (koek.indexOf("sessionid") != -1) { 208 | myWebView.loadUrl("https://" + server); 209 | } else { 210 | //This should get the login page, retreive the csrf-middlewaretoken, and then log the user in using a POST-request. 211 | try { 212 | getMiddleWareTokenAndLogIn(server, name, passw); 213 | } catch (IOException e) { 214 | throw new RuntimeException(e); 215 | } 216 | } 217 | } else { 218 | //This should get the login page, retreive the csrf-middlewaretoken, and then log the user in using a POST-request. 219 | try { 220 | getMiddleWareTokenAndLogIn(server, name, passw); 221 | } catch (IOException e) { 222 | throw new RuntimeException(e); 223 | } 224 | } 225 | 226 | 227 | } 228 | 229 | public void getMiddleWareTokenAndLogIn(String server, String name, String passw) throws IOException { 230 | //Het idee is dat deze functie de loginpagina van de server laadt en dan de 'csrfmiddlewaretoken' uit het inlogformulier haalt, 231 | //Zodat dat dan gebruikt kan worden bij het inloggen. 232 | //Becuase network operations cannot be done on the main/ui thread, create a new thread for this complete function. Yay! 233 | Thread draadje = new Thread(new Runnable() { 234 | @Override 235 | public void run() { 236 | try { 237 | //Load the login page, and do not forget to take some cookies. 238 | Security.insertProviderAt(Conscrypt.newProvider(), 1); 239 | String speculaas = ""; 240 | String speculaasBeslag = ""; 241 | //The login page loading is done using OkHttpClient. 242 | OkHttpClient client = new OkHttpClient(); 243 | Request aanvraag = new Request.Builder() 244 | .url("https://" + server + "/") 245 | .header("User-Agent", getString(R.string.gebruikersagent)) 246 | .build(); 247 | //Get an answer! 248 | try (Response antwoord = client.newCall(aanvraag).execute()) { 249 | if (!antwoord.isSuccessful()) throw new IOException("Unexpected code " + antwoord); 250 | //Search the headers for the 'set-cookie' header so we can eat a cookie! 251 | Headers cenna = antwoord.headers(); 252 | for (int i = 0; i < cenna.size(); i++) { 253 | if (cenna.name(i).equals("set-cookie")) { 254 | speculaas = cenna.value(i); 255 | speculaasBeslag = speculaas.split(";")[0]; 256 | } 257 | } 258 | //And then get the HTML body. 259 | assert antwoord.body() != null; 260 | String zooi = antwoord.body().string(); 261 | //Very easy to get the token by taking the text that it is preceded by in the raw html as the regex for a split() function! 262 | String[] opgebroken = zooi.split("name=\"csrfmiddlewaretoken\" value=\""); 263 | //For that gives as second element the token, followed by all the following html code. Then strip that code off, using the immediately following characters as regex. 264 | String[] breukjes = opgebroken[1].split("\">"); 265 | //Of course, the token is then the first element in our array. 266 | String token = breukjes[0]; 267 | //Log.d("botbreuk", token); 268 | String gegevens = null; 269 | //And then set the data string up for use in the POST request, with the csrf middleware token, the username, and the password. 270 | try { 271 | gegevens = "csrfmiddlewaretoken=" + URLEncoder.encode(token, "UTF-8") + "&localname=" + URLEncoder.encode(name, "UTF-8") + "&password=" + URLEncoder.encode(passw, "UTF-8"); 272 | } catch (UnsupportedEncodingException e) { 273 | throw new RuntimeException(e); 274 | } 275 | RequestBody keurslijf = new FormBody.Builder() 276 | .add("csrfmiddlewaretoken", token) 277 | .add("localname", name) 278 | .add("password", passw) 279 | .build(); 280 | 281 | String finalGegevens = gegevens; 282 | //Log.d("gegevens", finalGegevens); 283 | //Log.d("beslag", speculaasBeslag); 284 | String finalSpeculaas = speculaas; 285 | //Log in using a POST request, and shove the resulting web-page into a public string. (putje) 286 | //For this, we have to give the log-in function the servername, the request body, and the value of the csrf cookie. 287 | logInAndGetHTML(server, keurslijf, speculaasBeslag); 288 | //Then we have to run a bit of code on the main (UI) thread. To be able to work with the webview... 289 | runOnUiThread(new Runnable() { 290 | @Override 291 | public void run() { 292 | //First we have to get the cookie manager of the webview, so we can hand it the csrf cookie. 293 | //Without being fed the correct csrf cookie, the Wyrm will refuse our request. The wyrm is a very picky eater! 294 | CookieManager oven = CookieManager.getInstance(); 295 | //Bake the cookie into the webview. 296 | oven.setCookie("https://" + server, finalSpeculaas); 297 | //And bake the session cookie as well. 298 | oven.setCookie("https://" + server, sessie); 299 | //And then finally it is time to load everything into the webview. 300 | myWebView.loadDataWithBaseURL("https://" + server, putje, null, null, "https://" + server + "/login"); 301 | } 302 | }); 303 | 304 | } 305 | } catch (Exception e) { 306 | throw new RuntimeException(e); 307 | } 308 | } 309 | }); 310 | //^Here ends all that new Thread() code. 311 | //⇓Run all the code in the thread. 312 | draadje.start(); 313 | } 314 | public void logInAndGetHTML(String server, RequestBody lichaam, String speculoos) throws IOException { 315 | Security.insertProviderAt(Conscrypt.newProvider(), 1); 316 | //Create a client using CookieMonster, so we can retrieve cookies from the redirect after sending the log-in data. 317 | OkHttpClient client = new OkHttpClient.Builder() 318 | .addNetworkInterceptor(new CookieMonster()) 319 | .build(); 320 | //Create the POST request. 321 | Request verzoek = new Request.Builder() 322 | .url("https://" + server + "/login?next=/") 323 | .header("User-Agent", getString(R.string.gebruikersagent)) 324 | .addHeader("origin", "https://" + server) 325 | .addHeader("cookie", speculoos) 326 | .post(lichaam) 327 | .build(); 328 | //And then get the response. 329 | try (Response reactie = client.newCall(verzoek).execute()) { 330 | if (!reactie.isSuccessful()) 331 | throw new IOException("Unexpected code " + reactie); 332 | assert reactie.body() != null; 333 | //Shove the response body into the public string 'putje', to be used for sending the body to the webview. 334 | putje = reactie.body().string(); 335 | } 336 | } 337 | private final ActivityResultLauncher barcodeLanceerder = registerForActivityResult(new ScanContract(), 338 | result -> { 339 | if(result.getContents() == null) { 340 | Toast.makeText(StartActivity.this, "Cancelled", Toast.LENGTH_LONG).show(); 341 | } else { 342 | Toast.makeText(StartActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); 343 | myWebView.loadUrl("Javascript:(function() {" + 344 | "try {" + 345 | "document.getElementById('tour-search').value = " + result.getContents() + ";" + 346 | "} catch {" + 347 | "document.getElementById('search_input').value = " + result.getContents() + ";" + 348 | "}" + 349 | "document.getElementsByTagName('form')[0].submit();" + 350 | ";})()"); 351 | LoadIndicator.setVisibility(View.VISIBLE); 352 | } 353 | }); 354 | 355 | public void ScanBarCode() { 356 | String permission = Manifest.permission.CAMERA; 357 | int grant = ContextCompat.checkSelfPermission(StartActivity.this, permission); 358 | if (grant != PackageManager.PERMISSION_GRANTED) { 359 | String[] permission_list = new String[1]; 360 | permission_list[0] = permission; 361 | ActivityCompat.requestPermissions(StartActivity.this, permission_list, 1); 362 | } 363 | ScanOptions eisen = new ScanOptions(); 364 | eisen.setDesiredBarcodeFormats(ScanOptions.EAN_13); 365 | eisen.setBeepEnabled(true); 366 | eisen.setCameraId(0); 367 | eisen.setPrompt("SCAN ISBN"); 368 | eisen.setBarcodeImageEnabled(false); 369 | barcodeLanceerder.launch(eisen); 370 | } 371 | 372 | @Override 373 | public boolean onKeyUp(int keyCode, KeyEvent event) { 374 | // Check if the key event was the Back button and if there's history 375 | if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { 376 | myWebView.goBack(); 377 | return true; 378 | } 379 | // If it wasn't the Back key or there's no web page history, bubble up to the default 380 | // system behavior (probably exit the activity) 381 | return super.onKeyUp(keyCode, event); 382 | } 383 | @Override 384 | public boolean onKeyLongPress(int keyCode, KeyEvent event) { 385 | if (keyCode == KeyEvent.KEYCODE_BACK) { 386 | finish(); 387 | return true; 388 | } 389 | return super.onKeyLongPress(keyCode, event); 390 | } 391 | final class CookieMonster implements Interceptor { 392 | @Override public Response intercept(Interceptor.Chain chain) throws IOException { 393 | //Om ingelogd te blijven moeten we het sessiekoekje aan kunnen bieden. 394 | //Die moeten we dan wel eerst uit het koekblik pakken! 395 | Request eersteVerzoek = chain.request(); 396 | //Eerst moeten we controleren of er al een sessiekoekje is. Als dat niet zo is, dan is dit het echte eerste verzoek. 397 | if (sessie.isEmpty()) { 398 | //In dat geval halen we de reactie op om het koekje te kunnen pakken! 399 | Response eersteReactie = chain.proceed(chain.request()); 400 | Headers hoofden = eersteReactie.headers(); 401 | for (int i = 0; i < hoofden.size(); i++) { 402 | if (hoofden.name(i).equals("set-cookie") && hoofden.value(i).startsWith("session")) { 403 | sessie = hoofden.value(i); 404 | } 405 | } 406 | //Nadat we het koekje hebben moet de reactie doorgebriefd worden aan de 'client', 407 | //die dan het volgende verzoek zal gaan doen vanwege de 302-redirect bij het inloggen. 408 | return eersteReactie; 409 | } 410 | //Het koekje is er! Hoera! 411 | //Het nieuwe verzoek moet wel met het sessiekoekje verzonden worden, anders zijn we alsnog niet ingelogd! 412 | Request nieuwVerzoek = eersteVerzoek.newBuilder() 413 | .addHeader("cookie", sessie) 414 | .build(); 415 | return chain.proceed(nieuwVerzoek); 416 | } 417 | } 418 | //Here is code to make sure that links of the bookwyrm server are handled within the webview client, instead of having it open in the default browser. 419 | //Yes, I used the web for this too. 420 | private class MyWebViewClient extends WebViewClient { 421 | @Override 422 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { 423 | SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); 424 | String defaultValue = "none"; 425 | String server = sharedPref.getString(getString(R.string.server), defaultValue); 426 | if (server.equals(request.getUrl().getHost())) { 427 | //If the server is the same as the bookwyrm, load it in the webview. 428 | return false; 429 | } 430 | // Otherwise, it should go to the default browser instead. 431 | Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); 432 | startActivity(intent); 433 | return true; 434 | } 435 | 436 | @Override 437 | public void onPageStarted(WebView view, String url, Bitmap favicon) { 438 | LoadIndicator.setVisibility(View.VISIBLE); 439 | } 440 | } 441 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 24 | 25 | 39 | 40 | 55 | 56 | 70 | 71 | 87 | 88 | 102 | 103 | 117 | 118 | 132 | 133 | 149 | 150 | 165 | 166 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_start.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_wyrm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_wyrm_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_wyrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-hdpi/ic_launcher_wyrm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_wyrm_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-hdpi/ic_launcher_wyrm_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_wyrm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-hdpi/ic_launcher_wyrm_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_wyrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher_wyrm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_wyrm_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher_wyrm_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_wyrm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-mdpi/ic_launcher_wyrm_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xhdpi/ic_launcher_wyrm_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxhdpi/ic_launcher_wyrm_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_wyrm_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #FD00FF 3 | #003351 4 | #FFFFFF 5 | #074B72 6 | #CCE5FF 7 | #B8C8DA 8 | #23323F 9 | #394857 10 | #D4E4F6 11 | #D1BFE7 12 | #372A4A 13 | #4E4161 14 | #EDDCFF 15 | #FFB4AB 16 | #690005 17 | #93000A 18 | #FFDAD6 19 | #101418 20 | #E0E2E8 21 | #101418 22 | #E0E2E8 23 | #42474E 24 | #C2C7CE 25 | #8C9198 26 | #42474E 27 | #000000 28 | #E0E2E8 29 | #2D3135 30 | #2C638B 31 | #CCE5FF 32 | #001D31 33 | #99CCFA 34 | #074B72 35 | #D4E4F6 36 | #0D1D2A 37 | #B8C8DA 38 | #394857 39 | #EDDCFF 40 | #221534 41 | #D1BFE7 42 | #4E4161 43 | #101418 44 | #36393E 45 | #0B0F12 46 | #181C20 47 | #1C2024 48 | #272A2E 49 | #313539 50 | #C0E0FF 51 | #002841 52 | #6396C1 53 | #000000 54 | #CEDEF0 55 | #182734 56 | #8392A3 57 | #000000 58 | #E8D5FE 59 | #2C203E 60 | #9A8AAF 61 | #000000 62 | #FFD2CC 63 | #540003 64 | #FF5449 65 | #000000 66 | #101418 67 | #E0E2E8 68 | #101418 69 | #FFFFFF 70 | #42474E 71 | #D8DDE4 72 | #ADB2BA 73 | #8B9198 74 | #000000 75 | #E0E2E8 76 | #272A2E 77 | #0A4C73 78 | #CCE5FF 79 | #001321 80 | #99CCFA 81 | #00395A 82 | #D4E4F6 83 | #03121F 84 | #B8C8DA 85 | #293845 86 | #EDDCFF 87 | #170A28 88 | #D1BFE7 89 | #3D3050 90 | #101418 91 | #414549 92 | #05080B 93 | #1A1E22 94 | #24282C 95 | #2F3337 96 | #3A3E42 97 | #E6F1FF 98 | #000000 99 | #95C8F5 100 | #000C18 101 | #E6F1FF 102 | #000000 103 | #B4C4D6 104 | #000C18 105 | #F7ECFF 106 | #000000 107 | #CDBBE3 108 | #110522 109 | #FFECE9 110 | #000000 111 | #FFAEA4 112 | #220001 113 | #101418 114 | #E0E2E8 115 | #101418 116 | #FFFFFF 117 | #42474E 118 | #FFFFFF 119 | #EBF0F8 120 | #BEC3CB 121 | #000000 122 | #E0E2E8 123 | #000000 124 | #0A4C73 125 | #CCE5FF 126 | #000000 127 | #99CCFA 128 | #001321 129 | #D4E4F6 130 | #000000 131 | #B8C8DA 132 | #03121F 133 | #EDDCFF 134 | #000000 135 | #D1BFE7 136 | #170A28 137 | #101418 138 | #4D5055 139 | #000000 140 | #1C2024 141 | #2D3135 142 | #383C40 143 | #43474C 144 | 145 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_wyrm_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Bookwyrm 3 | bla 4 | blup 5 | gloep 6 | wheeeee 7 | Bookwyrm Android/1.3.8 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/theme_overlays.xml: -------------------------------------------------------------------------------- 1 | 2 | 50 | 98 | 99 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/test/java/nl/privacydragon/bookwyrm/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package nl.privacydragon.bookwyrm; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.8.0' apply false 4 | id 'com.android.library' version '8.8.0' apply false 5 | } 6 | 7 | task clean(type: Delete) { 8 | delete rootProject.buildDir 9 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/10.txt: -------------------------------------------------------------------------------- 1 | * Fixed a bug with the barcode scanning. Now you can scan barcodes again. 2 | * Dependencies have been updated to newest versions. 3 | * More Bookwyrm instances have been added. 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/11.txt: -------------------------------------------------------------------------------- 1 | The same as release 1.3.3, but not the one bug is actually fixed for real... 2 | * Fixed a bug with the barcode scanning. Now you can scan barcodes again. 3 | * Dependencies have been updated to newest versions. 4 | * More Bookwyrm instances have been added. 5 | 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/12.txt: -------------------------------------------------------------------------------- 1 | * It is finally possible to add book covers from your own device storage. 2 | * You can now return to the initial log-in screen by long-pressing the 'back' button. 3 | * Dependencies have been updated to newest versions. 4 | * Compiled for up to Android 14 now. 5 | * Deprecated pieces of code have been rewritten. 6 | * More Bookwyrm instances have been added. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/13.txt: -------------------------------------------------------------------------------- 1 | * Fixed some log-in problems. 2 | * Changed colour of warning text to be better visible. 3 | * Disabled inclusion of dependencies info and VCS info, fixing build problems for F-Droid. 4 | * Long-press now actually works. 5 | * Other minor changes -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/14.txt: -------------------------------------------------------------------------------- 1 | * Significant log-in improvements, including better security. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/15.txt: -------------------------------------------------------------------------------- 1 | Fixing what turned out not to work for unreleased version 1.3.7: 2 | * Significant log-in improvements, including better security. 3 | Further changes: 4 | * The app now uses a custom User-Agent string. 5 | * Two new dependencies have been added: okhttp3, and conscrypt. -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/6.txt: -------------------------------------------------------------------------------- 1 | Initial release for F-Droid. 2 | 3 | * Added support for four more BookWyrm instances. 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/7.txt: -------------------------------------------------------------------------------- 1 | * Added a loading bar 2 | * You can now scan ISBN's, on any instance! 3 | 4 | More technical change: 5 | * Mitigated the project to use Androidx libraries, for that is necessary for the ISBN scanning... 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/8.txt: -------------------------------------------------------------------------------- 1 | * Improved the loading bar. 2 | * The ISBN-scanning feature now works by simply clicking the ISBN button that is used by the instances that have scanning already available. 3 | * For instances that do not support the scanning server-side, there is now instead of the text a button for ISBN-scanning, similar to the standard one. 4 | * More bookwyrm instances added to the ones that can be opened in the app. 5 | * Small bug-fix, so now only ISBN-codes (And other EAN-13 codes) will be scanned. 6 | * Another bug-fix. The modified back button works again. 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/9.txt: -------------------------------------------------------------------------------- 1 | * Changed the initial screen to dark mode. 2 | * The login screen things are now better aligned. 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | This is an Android client for BookWyrm – a federated social network for tracking your reading, talking about books, writing reviews, and discovering what to read next. It is just BookWyrm put into a webview element, nothing special – but it adds some convenience on top: 2 | 3 | * bookwyrm links can be made to open with the app (e.g. clicking on the link to someone's userprofile will open that from your own bookwyrm instance) 4 | * thanks to that, you can immediately follow them easily if you want. 5 | * it lets you easily return to your own home timeline when viewing something like posts, on another bookwyrm instance, because the 'back' button is then modified for that. 6 | * You can also search for books by scanning the ISBN, no matter what instance of Bookwyrm you use. 7 | 8 | This application is not an official client! (An official client does not exist yet, as far as I know) 9 | 10 | The ISBN scanning feature is created with the zxing-android-embedded library. 11 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A BookWyrm client for Android. 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | BookWyrm 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Enables namespacing of each library's R class so that its R class includes only the 15 | # resources declared in the library itself and none from the library's dependencies, 16 | # thereby reducing the size of the R class for that library 17 | android.nonTransitiveRClass=true 18 | 19 | android.useAndroidX=true 20 | android.enableJetifier=true 21 | android.defaults.buildfeatures.buildconfig=true 22 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PrivacyDragon/Bookwyrm_Android/df65414f25fe76ae6e4298e2dd0f01ce8bbd99f0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Feb 14 18:09:26 CET 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | //maven { url 'https://jitpack.io' } 14 | } 15 | } 16 | 17 | rootProject.name = "Bookwyrm" 18 | include ':app' --------------------------------------------------------------------------------