From dbb9b8a50aa3e3e12f8846491b425cb14830dc76 Mon Sep 17 00:00:00 2001 From: Marco Pereira <33976946+magp18@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:49:32 +0100 Subject: [PATCH 1/4] Update EmbeddedBrowserActivity.java Support for exporting / importing data using UI --- .../mobile/EmbeddedBrowserActivity.java | 242 ++++++++++++++++-- 1 file changed, 215 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java b/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java index dafa0975..56b3dfd4 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java +++ b/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java @@ -21,25 +21,50 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; + +import android.os.StrictMode; +import android.util.Base64; +import android.util.Log; import android.view.View; import android.view.Window; import android.webkit.ConsoleMessage; +import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.Button; import android.widget.Toast; - import androidx.core.content.ContextCompat; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.BufferedReader; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.Optional; @SuppressWarnings({ "PMD.GodClass", "PMD.TooManyMethods" }) public class EmbeddedBrowserActivity extends Activity { - + Button download_button; + Button upload_button; + public String role = "Placeholder"; private WebView container; private SettingsStore settings; private String appUrl; @@ -59,13 +84,13 @@ public void onReceiveValue(String result) { } } }; + private List userData; -//> ACTIVITY LIFECYCLE METHODS + //> ACTIVITY LIFECYCLE METHODS @SuppressLint("ClickableViewAccessibility") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - trace(this, "Starting webview..."); this.filePickerHandler = new FilePickerHandler(this); @@ -80,10 +105,36 @@ public void onReceiveValue(String result) { this.settings = SettingsStore.in(this); this.appUrl = settings.getAppUrl(); - this.requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.main); + download_button = findViewById(R.id.download_button); + upload_button = findViewById(R.id.upload_button); + + download_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String userName = getUserData().get(0); + + container.evaluateJavascript("console.log('"+ "username:"+ userName + "')", null); + String script = "window.PouchDB('medic-user-"+ userName+"')" + + ".allDocs({include_docs: true, attachments: true})" + + ".then(result => medicmobile_android.saveDocs(JSON.stringify(result),'"+userName+"'));"; + container.evaluateJavascript(script, null); + } + }); + Log.d("Screen", getApplicationContext().toString()); + + upload_button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent i2 = new Intent(Intent.ACTION_GET_CONTENT); + i2.setType("*/*"); + i2.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + startActivityForResult(i2, 106); + } + }); // Add an alarming red border if using configurable (i.e. dev) // app with a medic production server. if (settings.allowsConfiguration() && appUrl != null && appUrl.contains("app.medicmobile.org")) { @@ -101,9 +152,9 @@ public void onReceiveValue(String result) { container = findViewById(R.id.wbvMain); getFragmentManager() - .beginTransaction() - .add(new OpenSettingsDialogFragment(container), OpenSettingsDialogFragment.class.getName()) - .commit(); + .beginTransaction() + .add(new OpenSettingsDialogFragment(container), OpenSettingsDialogFragment.class.getName()) + .commit(); configureUserAgent(); @@ -128,6 +179,56 @@ public void onReceiveValue(String result) { if (isValidNavigationUrl(appUrl, recentNavigation)) { container.loadUrl(recentNavigation); } + container.setWebViewClient(new WebViewClient(){ + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Log.d("URL used in the client.", url); + if (!url.contains("login") ) { + upload_button.setVisibility(View.VISIBLE); + download_button.setVisibility(View.VISIBLE); + role = getUserData().get(1); + role = role.substring(1, role.length() - 1); + boolean isUpdateRole = role != "Placeholder" && role.equals("chw_supervisor"); + boolean isDownloadRole = role != "Placeholder" && role.equals("chw"); + if (isDownloadRole){ + upload_button.setVisibility(View.GONE); + }else if (isUpdateRole){ + download_button.setVisibility(View.GONE); + } + } else { + upload_button.setVisibility(View.GONE); + download_button.setVisibility(View.GONE); + } + return super.shouldOverrideUrlLoading(view, url); + } + @Override + public void doUpdateVisitedHistory(WebView view, String url, boolean isReload){ + if (!Objects.isNull(url) && !url.contains("report") && !url.contains("login") && url.split("/").length > 3) { + Log.d(String.valueOf((url.split("/").length)),"printing URL ..."); + upload_button.setVisibility(View.VISIBLE); + download_button.setVisibility(View.VISIBLE); + if (getUserData()!=null){ + role = getUserData().get(1); + role = role.substring(1, role.length() - 1); + boolean isUpdateRole = role != "Placeholder" && role.equals("chw_supervisor"); + boolean isDownloadRole = role != "Placeholder" && role.equals("chw"); + if (isDownloadRole){ + upload_button.setVisibility(View.GONE); + }else if (isUpdateRole){ + download_button.setVisibility(View.GONE); + } + } + } else { + upload_button.setVisibility(View.GONE); + download_button.setVisibility(View.GONE); + } + super.doUpdateVisitedHistory(view,url,isReload); + } + @Override + public void onPageFinished(WebView view, String url) { + //page has finished loading + } + }); } @SuppressWarnings("PMD.CallSuperFirst") @@ -136,13 +237,17 @@ protected void onStart() { trace(this, "onStart() :: Checking Crosswalk migration ..."); XWalkMigration xWalkMigration = new XWalkMigration(this.getApplicationContext()); if (xWalkMigration.hasToMigrate()) { + log(this, "onStart() :: Running Crosswalk migration ..."); isMigrationRunning = true; Intent intent = new Intent(this, UpgradingActivity.class) - .putExtra("isClosable", false) - .putExtra("backPressedMessage", getString(R.string.waitMigration)); + .putExtra("isClosable", false) + .putExtra("backPressedMessage", getString(R.string.waitMigration)); startActivity(intent); xWalkMigration.run(); + role = getUserData().get(0); + Log.d("Role is ", role); + } else { trace(this, "onStart() :: Crosswalk installation not found - skipping migration"); } @@ -175,6 +280,7 @@ protected void onStop() { backButtonHandler); } + @SuppressLint("Recycle") @Override protected void onActivityResult(int requestCd, int resultCode, Intent intent) { Optional requestCodeOpt = RequestCode.valueOf(requestCd); @@ -190,6 +296,21 @@ protected void onActivityResult(int requestCd, int resultCode, Intent intent) { trace(this, "onActivityResult() :: requestCode=%s, resultCode=%s", requestCode.name(), resultCode); switch (requestCode) { + case PICK_FILE_REQUEST: + if (resultCode == RESULT_OK && intent != null) { + if(intent.getClipData() != null) { + int count = intent.getClipData().getItemCount(); + int currentItem = 0; + while(currentItem < count) { + Uri data_uri = intent.getClipData().getItemAt(currentItem).getUri(); + uploadFile(data_uri, currentItem); + currentItem = currentItem + 1; + } + } else{ + Uri data_uri = intent.getData(); + uploadFile(data_uri, 1); + } + } case FILE_PICKER_ACTIVITY: this.filePickerHandler.processResult(resultCode, intent); return; @@ -214,11 +335,38 @@ protected void onActivityResult(int requestCd, int resultCode, Intent intent) { } catch (Exception ex) { String action = intent == null ? null : intent.getAction(); warn(ex, "Problem handling intent %s (%s) with requestCode=%s & resultCode=%s", - intent, action, requestCode.name(), resultCode); + intent, action, requestCode.name(), resultCode); } } - -//> ACCESSORS + private void uploadFile(Uri data_uri, int currentItem){ + String content; + try { + InputStream in = getContentResolver().openInputStream(data_uri); + BufferedReader r = new BufferedReader(new InputStreamReader(in)); + StringBuilder total = new StringBuilder(); + for (String line; (line = r.readLine()) != null; ) { + total.append(line); + } + if (Build.VERSION.SDK_INT > 9) { + StrictMode.ThreadPolicy policy = + new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(policy); + } + content =total.toString().replaceAll("\"total_rows\".*\"rows\":","\"docs\":"); + String script = "new PouchDB('temp')" + + ".bulkDocs("+content+");"; + container.evaluateJavascript(script, null); + String script_sync = "window.PouchDB('temp')" + + ".replicate.to('"+appUrl+"/medic').then(result =>" + + "medicmobile_android.toastResult('Uploaded file number "+(currentItem+1)+" Successfully')).catch(err =>" + + "medicmobile_android.toastResult(JSON.stringify(err)));"; + container.evaluateJavascript(script_sync, null); + }catch (Exception e) { + warn(e, "Could not open the specified file"); + toast("Could not open the specified file"); + } + } + //> ACCESSORS MrdtSupport getMrdtSupport() { return this.mrdt; } @@ -230,8 +378,7 @@ SmsSender getSmsSender() { ChtExternalAppHandler getChtExternalAppHandler() { return this.chtExternalAppHandler; } - -//> PUBLIC API + //> PUBLIC API public void evaluateJavascript(final String js) { evaluateJavascript(js, true); } @@ -272,17 +419,57 @@ public boolean getLocationPermissions() { trace(this, "getLocationPermissions() :: Fine or Coarse location not granted before, requesting access..."); startActivityForResult( - new Intent(this, RequestLocationPermissionActivity.class), - RequestCode.ACCESS_LOCATION_PERMISSION.getCode() + new Intent(this, RequestLocationPermissionActivity.class), + RequestCode.ACCESS_LOCATION_PERMISSION.getCode() ); return false; } -//> PRIVATE HELPERS + //> PRIVATE HELPERS private void locationRequestResolved() { evaluateJavascript("window.CHTCore.AndroidApi.v1.locationPermissionRequestResolved();"); } + public List getUserData(){ + List userData = new ArrayList<>(); + try { + String cookies = CookieManager.getInstance().getCookie(appUrl); + if (Objects.isNull(cookies)){ + return null; + } + Log.d("updating user data", ": updated"); + + if ( cookies != null && !cookies.isEmpty()){ + String encodedUserCtxCookie = Arrays.stream(cookies.split(";")) + .map(field -> field.split("=")) + .filter(pair -> "userCtx".equals(pair[0].trim())) + .map(pair -> pair[1].trim()) + .findAny() + .get(); + String userCtxData = URLDecoder.decode(encodedUserCtxCookie, "utf-8") + .replace("{", "") + .replace("}", ""); + String userName = Arrays.stream(userCtxData.split(",")) + .map(field -> field.split(":")) + .filter(pair -> "\"name\"".equals(pair[0].trim())) + .map(pair -> pair[1].replace("\"", "").trim()) + .findAny() + .get(); + userData.add(userName); + role = (Arrays.stream(userCtxData.split(",")) + .map(field -> field.split(":")) + .filter(pair -> "\"roles\"".equals(pair[0].trim())) + .map(pair -> pair[1].replace("\"", "").trim()) + .findAny() + .get()); + userData.add(role); + return userData; + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } private void processChtExternalAppResult(int resultCode, Intent intentData) { String script = this.chtExternalAppHandler.processResult(resultCode, intentData); trace(this, "ChtExternalAppHandler :: Executing JavaScript: %s", script); @@ -311,10 +498,10 @@ private void processStoragePermissionResult(int resultCode, Intent intent) { } trace( - this, - "EmbeddedBrowserActivity :: No handling for trigger: %s, requestCode: %s", - triggerClass, - RequestCode.ACCESS_STORAGE_PERMISSION.name() + this, + "EmbeddedBrowserActivity :: No handling for trigger: %s, requestCode: %s", + triggerClass, + RequestCode.ACCESS_STORAGE_PERMISSION.name() ); } @@ -398,14 +585,15 @@ private void registerRetryConnectionBroadcastReceiver() { registerReceiver(broadcastReceiver, new IntentFilter("retryConnection")); } -//> ENUMS + //> ENUMS public enum RequestCode { ACCESS_LOCATION_PERMISSION(100), ACCESS_STORAGE_PERMISSION(101), ACCESS_SEND_SMS_PERMISSION(102), CHT_EXTERNAL_APP_ACTIVITY(103), GRAB_MRDT_PHOTO_ACTIVITY(104), - FILE_PICKER_ACTIVITY(105); + FILE_PICKER_ACTIVITY(105), + PICK_FILE_REQUEST(106); private final int requestCode; @@ -415,9 +603,9 @@ public enum RequestCode { public static Optional valueOf(int code) { return Arrays - .stream(RequestCode.values()) - .filter(e -> e.getCode() == code) - .findFirst(); + .stream(RequestCode.values()) + .filter(e -> e.getCode() == code) + .findFirst(); } public int getCode() { From b069155e07c07f76686887181ce28230f64a3b52 Mon Sep 17 00:00:00 2001 From: Marco Pereira <33976946+magp18@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:51:25 +0100 Subject: [PATCH 2/4] Update MedicAndroidJavascript.java Added JS functions to prepare document when exporting it --- .../webapp/mobile/MedicAndroidJavascript.java | 155 +++++++++++++++++- 1 file changed, 149 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java b/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java index aac1e627..605bdb56 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java +++ b/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java @@ -1,9 +1,14 @@ package org.medicmobile.webapp.mobile; +import static android.os.Environment.DIRECTORY_DOCUMENTS; +import static android.os.Environment.getExternalStorageDirectory; +import static android.os.Environment.getExternalStoragePublicDirectory; + import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManager.MemoryInfo; import android.app.DatePickerDialog; +import android.content.Context; import android.content.pm.PackageInfo; import android.net.ConnectivityManager; import android.net.NetworkCapabilities; @@ -11,15 +16,22 @@ import android.net.TrafficStats; import android.net.Uri; import android.os.Process; +import android.util.Log; +import android.webkit.JavascriptInterface; import android.widget.DatePicker; import android.os.Build; import android.os.Environment; import android.os.StatFs; +import android.widget.Toast; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; @@ -28,10 +40,15 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.Objects; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -40,6 +57,7 @@ import static java.util.Calendar.YEAR; import static java.util.Locale.UK; import static org.medicmobile.webapp.mobile.MedicLog.log; +import static org.medicmobile.webapp.mobile.MedicLog.warn; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -74,7 +92,7 @@ public void setConnectivityManager(ConnectivityManager connectivityManager) { this.connectivityManager = connectivityManager; } -//> JavascriptInterface METHODS + //> JavascriptInterface METHODS @android.webkit.JavascriptInterface public String getAppVersion() { try { @@ -86,6 +104,114 @@ public String getAppVersion() { } } + @JavascriptInterface + public void toastResult(String result){ + Toast.makeText(parent, result, Toast.LENGTH_LONG).show(); + } + @JavascriptInterface + public void saveDocs(String docs, String username) throws IOException{ + JSONArray newDocs = new JSONArray(); + try { + docs = docs.replaceAll("\\\\/", "/"); + JSONObject docs_obj = new JSONObject(docs); + JSONArray docs_list = docs_obj.getJSONArray("rows"); + for (int i = 0; i < docs_list.length(); i++) { + docs_list.getJSONObject(i).remove("key"); + docs_list.getJSONObject(i).remove("value"); + docs_list.getJSONObject(i).getJSONObject("doc").remove("_rev"); + if(docs_list.getJSONObject(i).has("_rev")){ + docs_list.getJSONObject(i).remove("_rev"); + } + if(docs_list.getJSONObject(i).getJSONObject("doc").has("doc")){ + docs_list.remove(i); + } + if (docs_list.getJSONObject(i).getString("id").startsWith("form")|| docs_list.getJSONObject(i).getString("id").equals("settings") || docs_list.getJSONObject(i).getString("id").startsWith("service") ||docs_list.getJSONObject(i).getString("id").equals("resources") || docs_list.getJSONObject(i).getString("id").equals("branding") || docs_list.getJSONObject(i).getString("id").startsWith("_design") || docs_list.getJSONObject(i).getJSONObject("doc").has("type") && (docs_list.getJSONObject(i).getJSONObject("doc").get("type").toString().equals("translations") || docs_list.getJSONObject(i).getJSONObject("doc").get("type").toString().equals("target"))) { + Log.d("Translations: ", "found"); + }else{ + if(docs_list.getJSONObject(i).getJSONObject("doc").has("_attachments")){ + String contentType = docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").getString("content_type"); + String digest = docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").getString("digest"); + docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").remove("content_type"); + docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").remove("digest"); + contentType = "application/xml"; + digest= digest.replace("\\\\/","/"); + docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").put("content_type", contentType); + docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").put("revpos", 1); + docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").put("digest", digest); + + Log.d("application / content", docs_list.getJSONObject(i).getJSONObject("doc").getJSONObject("_attachments").getJSONObject("content").getString("content_type").toString()); + } + Iterator keys = docs_list.getJSONObject(i).getJSONObject("doc").keys(); + while(keys.hasNext()) { + String key = keys.next(); + docs_list.getJSONObject(i).put(key,docs_list.getJSONObject(i).getJSONObject("doc").get(key)); + } + docs_list.getJSONObject(i).remove("doc"); + + if (docs_list.getJSONObject(i).has("id")){ + docs_list.getJSONObject(i).remove("id"); + } + newDocs.put(docs_list.getJSONObject(i)); + } + + } + } catch (JSONException e) { + Log.d("Error json type", "json file"); + e.printStackTrace(); + } + if (newDocs != null && newDocs.length() > 0 ){ + docs= "{\"docs\":"+newDocs.toString()+"}"; + } + File file = null; + DateFormat df = new SimpleDateFormat("yyyyMMddhhmmss"); + Log.d("storage", String.valueOf(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))); + Log.d("version", String.valueOf(Build.VERSION.SDK_INT)); + File cht_data = new File(this.parent.getExternalFilesDir("Documents")+"/cht_data/"); + if (!cht_data.exists()){ + cht_data.mkdirs(); + } + if (android.os.Build.VERSION.SDK_INT >= 26) { + file = new File(this.parent.getExternalFilesDir("Documents")+"/cht_data/", username+"_"+ LocalDateTime.now() + .truncatedTo(ChronoUnit.SECONDS) + .toString() + .replace("-","") + .replace(":","")+ ".txt"); + } + else { + file = new File(this.parent.getExternalFilesDir("Documents")+"/cht_data/", username+"_"+LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS) + .toString() + .replace("-","") + .replace(":","")+ ".txt"); + } + Log.d("file", file.toString()); + if(!file.getParentFile().exists()){ + Log.d("Creating parent", file.getParent().toString()); + file.getParentFile().mkdirs(); + } + if(!file.exists()){ + try { + file.createNewFile(); + }catch (Exception e){ + e.printStackTrace(); + } + } + Log.d("file exists", String.valueOf(file.exists())); + try { + FileWriter fileWriter = new FileWriter(file); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + bufferedWriter.write(docs); + bufferedWriter.close(); + toastResult("Download Completed"); + } catch (FileNotFoundException e){ + toastResult("Could not find the file"); + e.printStackTrace(); + } catch (IOException e) { + toastResult("Could not write the file to storage"); + e.printStackTrace(); + } + + + } @android.webkit.JavascriptInterface public void playAlert() { try { @@ -302,8 +428,25 @@ public String getDeviceInfo() { return jsonError("Problem fetching device info: ", ex); } } - -//> PRIVATE HELPER METHODS + public JSONObject parseJSON(JSONObject json) + { + Iterator iter = json.keys(); + while (iter.hasNext()) + { + String key = iter.next(); + if (key.equals("-xmlns:i") || + key.equals("-i:nil") || + key.equals("-xmlns:d4p1") || + key.equals("-i:type") || + key.equals("#text")) // I want to delete items with those ID strings + { + json.remove(key); + } + //Object value = json.get(key); + } + return json; + } + //> PRIVATE HELPER METHODS private void datePicker(String targetElement, Calendar initialDate) { // Remove single-quotes from the `targetElement` CSS selecter, as // we'll be using these to enclose the entire string in JS. We @@ -337,8 +480,8 @@ public void onDateSet(DatePicker view, int year, int month, int day) { private static HashMap getCPUInfo() throws IOException { try( - Reader fileReader = new InputStreamReader(new FileInputStream("/proc/cpuinfo"), StandardCharsets.UTF_8); - BufferedReader bufferedReader = new BufferedReader(fileReader); + Reader fileReader = new InputStreamReader(new FileInputStream("/proc/cpuinfo"), StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { String line; HashMap output = new HashMap(); @@ -376,7 +519,7 @@ private void logException(Exception ex) { parent.errorToJsConsole("Exception thrown in JavascriptInterface function: %s", stacktrace); } -//> STATIC HELPERS + //> STATIC HELPERS private static String jsonError(String message, Exception ex) { return jsonError(message + ex.getClass() + ": " + ex.getMessage()); } From b89fd46459ec5c9ec831c0c6b25ef2a4d921401e Mon Sep 17 00:00:00 2001 From: Marco Pereira <33976946+magp18@users.noreply.github.com> Date: Thu, 17 Nov 2022 07:56:58 +0100 Subject: [PATCH 3/4] Update AndroidManifest.xml --- src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index e9d68e7d..00fbb194 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ - + From ca533f5c60054eac14e7069c1ccd55066af3bb33 Mon Sep 17 00:00:00 2001 From: Marco Pereira <33976946+magp18@users.noreply.github.com> Date: Thu, 17 Nov 2022 08:01:13 +0100 Subject: [PATCH 4/4] Update main.xml --- src/main/res/layout/main.xml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml index fadffce8..a9032df8 100644 --- a/src/main/res/layout/main.xml +++ b/src/main/res/layout/main.xml @@ -3,7 +3,27 @@ android:id="@+id/lytWebView" android:layout_width="fill_parent" android:layout_height="fill_parent"> - + + +