diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7b46144..7b3006b 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,16 +4,16 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 0a68ca9..fcf0a27 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - - + diff --git a/app/build.gradle b/app/build.gradle index c89cc7e..597b260 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,14 +3,14 @@ plugins { } android { - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "nl.privacydragon.bookwyrm" minSdk 23 - targetSdk 33 - versionCode 11 - versionName "1.3.4" + targetSdk 34 + versionCode 12 + versionName "1.3.5" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -30,14 +30,13 @@ android { dependencies { - implementation 'androidx.appcompat:appcompat:1.6.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' + implementation platform('org.jetbrains.kotlin:kotlin-bom:2.1.0') testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'com.google.zxing:core:3.5.1' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + implementation 'com.google.zxing:core:3.5.3' implementation 'com.journeyapps:zxing-android-embedded:4.3.0@aar' - //implementation 'com.github.yuriy-budiyev:code-scanner:2.1.2' - } \ No newline at end of file diff --git a/app/release/Bookwyrm-v1.3.4.apk b/app/release/Bookwyrm-v1.3.4.apk deleted file mode 100644 index 1712f2b..0000000 Binary files a/app/release/Bookwyrm-v1.3.4.apk and /dev/null differ diff --git a/app/release/Bookwyrm-v1.3.5.apk b/app/release/Bookwyrm-v1.3.5.apk new file mode 100644 index 0000000..dde785c Binary files /dev/null and b/app/release/Bookwyrm-v1.3.5.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 08e2aa0..aaba0f9 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,10 +11,27 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 11, - "versionName": "1.3.4", + "versionCode": 12, + "versionName": "1.3.5", "outputFile": "app-release.apk" } ], - "elementType": "File" + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/app-release.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/app-release.dm" + ] + } + ], + "minSdkVersionForDexing": 23 } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a2db687..c3bbd7a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,492 +18,738 @@ + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + omhooglader; @SuppressLint("SetJavaScriptEnabled") @Override protected void onCreate(Bundle savedInstanceState) { @@ -68,11 +64,26 @@ public class HandlerActivity extends AppCompatActivity { String appLinkAction = appLinkIntent.getAction(); Uri appLinkData = appLinkIntent.getData(); // End of auto-generated stuff + ActivityResultLauncher voodooLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + // There are no request codes + Intent data = result.getData(); + if (omhooglader == null) + return; + omhooglader.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), data)); + } else { + omhooglader.onReceiveValue(null); + } + }); LoadIndicator = (ProgressBar) findViewById(R.id.progressBar3); myWebView = (WebView) findViewById(R.id.webview); myWebView.setVisibility(View.GONE); myWebView.getSettings().setJavaScriptEnabled(true); myWebView.getSettings().setDomStorageEnabled(true); + myWebView.getSettings().setAllowFileAccess(true); + myWebView.getSettings().setAllowContentAccess(true); myWebView.addJavascriptInterface(new Object() { @JavascriptInterface // For API 17+ @@ -84,6 +95,24 @@ public class HandlerActivity extends AppCompatActivity { } }, "scan"); + myWebView.setWebChromeClient(new WebChromeClient() { + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + if (omhooglader != null) { + //omhooglader.onReceiveValue(null); + omhooglader = null; + } + omhooglader = filePathCallback; + Intent intent = fileChooserParams.createIntent(); + try { + voodooLauncher.launch(intent); + } catch (ActivityNotFoundException grrr){ + omhooglader = null; + return false; + } + return true; + } + }); //myWebView.addJavascriptInterface(new HandlerActivity.WebAppInterface(this), "Android"); //The user credentials are stored in the shared preferences, so first they have to be read from there. String defaultValue = "none"; @@ -101,9 +130,10 @@ public class HandlerActivity extends AppCompatActivity { } String pathMaybe = appLinkData.getPath(); String toGoServer = "bla"; - if (pathMaybe.contains("user")) { + //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... + if (pathMaybe.contains("user") || pathMaybe.contains("book")) { //If the path contains 'user', it is a user profile, unless it is followed by something like 'review'. - if (pathMaybe.contains("review") || pathMaybe.contains("generatednote") || pathMaybe.contains("quotation") || pathMaybe.contains("comment") ) { + if (pathMaybe.contains("review") || pathMaybe.contains("generatednote") || pathMaybe.contains("quotation") || pathMaybe.contains("comment") || pathMaybe.contains("book")) { toGoServer = "https://" + appLinkData.getHost() + pathMaybe; } else { @@ -112,6 +142,10 @@ public class HandlerActivity extends AppCompatActivity { toGoServer = "https://" + server + "/user/" + atUser; } } else { + //If the toGoServer string remains "bla", dan zal de user when they teruggaan uitkomen op https://bla/, which is 'not allowed'. + //So, maybe here, just to be sure, assign the value of the user's own server to the toGoServer variabele. + //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. + toGoServer = "https://" + server; startActivity(new Intent(HandlerActivity.this, nl.privacydragon.bookwyrm.StartActivity.class)); } //Then all the decryption stuff has to happen. There are a lot of try-catch stuff, because apparently that seems to be needed. @@ -181,9 +215,9 @@ public class HandlerActivity extends AppCompatActivity { view.loadUrl("javascript:(function() { document.getElementById('id_password').value = '" + passw + "'; ;})()"); view.loadUrl("javascript:(function() { document.getElementById('id_localname').value = '" + name + "'; ;})()"); - view.loadUrl("javascript:(function() { if (window.location.href == '" + finalToGoServer + "' && !/(review|generatednote|quotation|comment)/i.test(window.location.href)) { document.getElementsByName(\"login\")[0].submit();} ;})()"); + view.loadUrl("javascript:(function() { if (window.location.href == '" + finalToGoServer + "' && !/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) { document.getElementsByName(\"login\")[0].submit();} ;})()"); view.loadUrl("javascript:(function() { if (window.location.href == 'https://" + server + "') { document.getElementsByName(\"login\")[0].submit();} ;})()"); - view.loadUrl("javascript:(function() { if (/(review|generatednote|quotation|comment)/i.test(window.location.href)) {" + + view.loadUrl("javascript:(function() { if (/(review|generatednote|quotation|comment|book)/i.test(window.location.href)) {" + "blocks = document.getElementsByClassName('block');" + "for (let element of blocks){" + "if (element.localName == 'header') { " + @@ -236,10 +270,27 @@ public class HandlerActivity extends AppCompatActivity { } });*/ //Here, load the login page of the server. That actually does all that is needed. - //myWebView.loadUrl("https://serratus.github.io/quaggaJS/examples/live_w_locator.html"); myWebView.loadUrl(toGoServer); } + private final ActivityResultLauncher barcodeLanceerder = registerForActivityResult(new ScanContract(), + result -> { + if(result.getContents() == null) { + Toast.makeText(HandlerActivity.this, "Cancelled", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(HandlerActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); + myWebView.loadUrl("Javascript:(function() {" + + "try {" + + "document.getElementById('tour-search').value = " + result.getContents() + ";" + + "} catch {" + + "document.getElementById('search_input').value = " + result.getContents() + ";" + + "}" + + "document.getElementsByTagName('form')[0].submit();" + + ";})()"); + LoadIndicator.setVisibility(View.VISIBLE); + } + }); + public void ScanBarCode() { String permission = Manifest.permission.CAMERA; int grant = ContextCompat.checkSelfPermission(HandlerActivity.this, permission); @@ -248,57 +299,17 @@ public class HandlerActivity extends AppCompatActivity { permission_list[0] = permission; ActivityCompat.requestPermissions(HandlerActivity.this, permission_list, 1); } - - IntentIntegrator intentIntegrator = new IntentIntegrator(HandlerActivity.this); - intentIntegrator.setDesiredBarcodeFormats(intentIntegrator.EAN_13); - intentIntegrator.setBeepEnabled(true); - intentIntegrator.setCameraId(0); - intentIntegrator.setPrompt("SCAN"); - intentIntegrator.setBarcodeImageEnabled(false); - intentIntegrator.initiateScan(); + ScanOptions eisen = new ScanOptions(); + eisen.setDesiredBarcodeFormats(ScanOptions.EAN_13); + eisen.setBeepEnabled(true); + eisen.setCameraId(0); + eisen.setPrompt("SCAN ISBN"); + eisen.setBarcodeImageEnabled(false); + barcodeLanceerder.launch(eisen); //return "blup"; //return "bla"; } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - IntentResult Result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); - if (Result != null) { - if (Result.getContents() == null) { - Toast.makeText(this, "cancelled", Toast.LENGTH_SHORT).show(); - } else { - Log.d("MainActivity", "Scanned"); - myWebView.loadUrl("Javascript:(function() {" + - "try {" + - "document.getElementById('tour-search').value = " + Result.getContents() + ";" + - "} catch {" + - "document.getElementById('search_input').value = " + Result.getContents() + ";" + - "}" + - "document.getElementsByTagName('form')[0].submit();" + - ";})()"); - LoadIndicator.setVisibility(View.VISIBLE); - - } - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - /*public class WebAppInterface { - Context mContext; - - //Instantiate the interface and set the context - WebAppInterface(Context c) { - mContext = c; - } - - // Show a toast from the web page - @JavascriptInterface - public void showToast(String toast) { - Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); - } - }*/ - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Check if the key event was the Back button and if there's history @@ -310,7 +321,15 @@ public class HandlerActivity extends AppCompatActivity { // system behavior (probably exit the activity) return super.onKeyDown(keyCode, event); } - //Here is code to make sure that links of the bookwyrm server are handled withing the webview client, instead of having it open in the default browser. + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + finish(); + return true; + } + return super.onKeyLongPress(keyCode, event); + } + //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. //Yes, I used the web for this too. private class MyWebViewClient extends WebViewClient { @Override diff --git a/app/src/main/java/nl/privacydragon/bookwyrm/StartActivity.java b/app/src/main/java/nl/privacydragon/bookwyrm/StartActivity.java index 02edf46..73255c0 100644 --- a/app/src/main/java/nl/privacydragon/bookwyrm/StartActivity.java +++ b/app/src/main/java/nl/privacydragon/bookwyrm/StartActivity.java @@ -2,30 +2,35 @@ package nl.privacydragon.bookwyrm; import android.Manifest; import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -//import android.support.v7.app.AppCompatActivity; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.net.Uri; import android.os.Bundle; import android.util.Base64; -import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.webkit.JavascriptInterface; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import com.google.zxing.integration.android.IntentIntegrator; -import com.google.zxing.integration.android.IntentResult; +import com.journeyapps.barcodescanner.ScanContract; +import com.journeyapps.barcodescanner.ScanOptions; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -47,12 +52,27 @@ import javax.crypto.spec.GCMParameterSpec; public class StartActivity extends AppCompatActivity { WebView myWebView; ProgressBar LoadIndicator; + public ValueCallback omhooglader; @SuppressLint("SetJavaScriptEnabled") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start); LoadIndicator = (ProgressBar) findViewById(R.id.progressBar3); + ActivityResultLauncher voodooLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + // There are no request codes + Intent data = result.getData(); + if (omhooglader == null) + return; + omhooglader.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), data)); + } + else { + omhooglader.onReceiveValue(null); + } + }); myWebView = (WebView) findViewById(R.id.webview); myWebView.setVisibility(View.GONE); myWebView.getSettings().setJavaScriptEnabled(true); @@ -67,6 +87,31 @@ public class StartActivity extends AppCompatActivity { } }, "scan"); + myWebView.setWebChromeClient(new WebChromeClient() { + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + if (omhooglader != null) { + //omhooglader.onReceiveValue(null); + omhooglader = null; + } + omhooglader = filePathCallback; + Intent intent = fileChooserParams.createIntent(); + try { +// String toestemming = Manifest.permission.READ_EXTERNAL_STORAGE; +// int grant = ContextCompat.checkSelfPermission(StartActivity.this, toestemming); +// if (grant != PackageManager.PERMISSION_GRANTED) { +// String[] permission_list = new String[1]; +// permission_list[0] = toestemming; +// ActivityCompat.requestPermissions(StartActivity.this, permission_list, 1); +// } + voodooLauncher.launch(intent); + } catch (ActivityNotFoundException grrr){ + omhooglader = null; + return false; + } + return true; + } + }); //The user credentials are stored in the shared preferences, so first they have to be read from there. String defaultValue = "none"; SharedPreferences sharedPref = StartActivity.this.getSharedPreferences(getString(R.string.server), Context.MODE_PRIVATE); @@ -154,6 +199,24 @@ public class StartActivity extends AppCompatActivity { //Here, load the login page of the server. That actually does all that is needed. myWebView.loadUrl("https://" + server + "/login"); } + private final ActivityResultLauncher barcodeLanceerder = registerForActivityResult(new ScanContract(), + result -> { + if(result.getContents() == null) { + Toast.makeText(StartActivity.this, "Cancelled", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(StartActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show(); + myWebView.loadUrl("Javascript:(function() {" + + "try {" + + "document.getElementById('tour-search').value = " + result.getContents() + ";" + + "} catch {" + + "document.getElementById('search_input').value = " + result.getContents() + ";" + + "}" + + "document.getElementsByTagName('form')[0].submit();" + + ";})()"); + LoadIndicator.setVisibility(View.VISIBLE); + } + }); + public void ScanBarCode() { String permission = Manifest.permission.CAMERA; int grant = ContextCompat.checkSelfPermission(StartActivity.this, permission); @@ -162,43 +225,17 @@ public class StartActivity extends AppCompatActivity { permission_list[0] = permission; ActivityCompat.requestPermissions(StartActivity.this, permission_list, 1); } - - IntentIntegrator intentIntegrator = new IntentIntegrator(StartActivity.this); - intentIntegrator.setDesiredBarcodeFormats(IntentIntegrator.EAN_13); - intentIntegrator.setBeepEnabled(false); - intentIntegrator.setCameraId(0); - intentIntegrator.setPrompt("SCAN ISBN"); - intentIntegrator.setBarcodeImageEnabled(false); - intentIntegrator.initiateScan(); - + ScanOptions eisen = new ScanOptions(); + eisen.setDesiredBarcodeFormats(ScanOptions.EAN_13); + eisen.setBeepEnabled(true); + eisen.setCameraId(0); + eisen.setPrompt("SCAN ISBN"); + eisen.setBarcodeImageEnabled(false); + barcodeLanceerder.launch(eisen); //return "blup"; //return "bla"; } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - IntentResult Result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); - if (Result != null) { - if (Result.getContents() == null) { - Toast.makeText(this, "cancelled", Toast.LENGTH_SHORT).show(); - } else { - Log.d("MainActivity", "Scanned"); - myWebView.loadUrl("Javascript:(function() {" + - "try {" + - "document.getElementById('tour-search').value = " + Result.getContents() + ";" + - "} catch {" + - "document.getElementById('search_input').value = " + Result.getContents() + ";" + - "}" + - "document.getElementsByTagName('form')[0].submit();" + - ";})()"); - LoadIndicator.setVisibility(View.VISIBLE); - - } - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Check if the key event was the Back button and if there's history @@ -210,7 +247,15 @@ public class StartActivity extends AppCompatActivity { // system behavior (probably exit the activity) return super.onKeyDown(keyCode, event); } - //Here is code to make sure that links of the bookwyrm server are handled withing the webview client, instead of having it open in the default browser. + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + finish(); + return true; + } + return super.onKeyLongPress(keyCode, event); + } + //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. //Yes, I used the web for this too. private class MyWebViewClient extends WebViewClient { @Override diff --git a/build.gradle b/build.gradle index b0e1af8..70517a6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.1.1' apply false - id 'com.android.library' version '7.1.1' apply false + id 'com.android.application' version '8.7.3' apply false + id 'com.android.library' version '8.7.3' apply false } task clean(type: Delete) { diff --git a/fastlane/metadata/android/en-US/changelogs/12.txt b/fastlane/metadata/android/en-US/changelogs/12.txt new file mode 100644 index 0000000..109ed5d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/12.txt @@ -0,0 +1,6 @@ +* It is finally possible to add book covers from your own device storage. +* You can now return to the initial log-in screen by long-pressing the 'back' button. +* Dependencies have been updated to newest versions. +* Compiled for up to Android 14 now. +* Deprecated pieces of code have been rewritten. +* More Bookwyrm instances have been added. \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9bf2bbf..a8439ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,4 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.nonTransitiveRClass=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b555e26..987c093 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Feb 14 18:09:26 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME