diff --git a/android/app/src/main/java/com/mealtracker/app/MainActivity.java b/android/app/src/main/java/com/mealtracker/app/MainActivity.java index c0409ed..6dbfba0 100644 --- a/android/app/src/main/java/com/mealtracker/app/MainActivity.java +++ b/android/app/src/main/java/com/mealtracker/app/MainActivity.java @@ -9,7 +9,7 @@ import android.net.NetworkInfo; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; -import android.view.inputmethod.EditorInfo; +import android.webkit.CookieManager; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; @@ -29,12 +29,14 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; public class MainActivity extends AppCompatActivity { private static final String PREFS_NAME = "mealtracker_prefs"; - private static final String KEY_PUBLIC_URL = "public_url"; + private static final String KEY_SERVER_URL = "server_url"; private WebView webView; private SwipeRefreshLayout swipeRefresh; private ProgressBar progressBar; private TextView errorView; + private TextView toolbarTitle; + private String serverUrl; private String currentUrl; @SuppressLint("SetJavaScriptEnabled") @@ -47,6 +49,7 @@ public class MainActivity extends AppCompatActivity { swipeRefresh = findViewById(R.id.swipe_refresh); progressBar = findViewById(R.id.progress_bar); errorView = findViewById(R.id.error_view); + toolbarTitle = findViewById(R.id.toolbar_title); ImageButton btnSettings = findViewById(R.id.btn_settings); ImageButton btnRefresh = findViewById(R.id.btn_refresh); @@ -56,12 +59,13 @@ public class MainActivity extends AppCompatActivity { btnRefresh.setOnClickListener(v -> webView.reload()); btnSettings.setOnClickListener(v -> showSettingsDialog()); - // Load saved URL or show setup SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); - String savedUrl = prefs.getString(KEY_PUBLIC_URL, ""); - if (!savedUrl.isEmpty()) { - loadUrl(savedUrl); + serverUrl = prefs.getString(KEY_SERVER_URL, ""); + if (!serverUrl.isEmpty()) { + loadUrl(serverUrl); + toolbarTitle.setText(getString(R.string.app_name)); } else { + toolbarTitle.setText(R.string.app_name); showSettingsDialog(); } } @@ -71,6 +75,7 @@ public class MainActivity extends AppCompatActivity { WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); + settings.setDatabaseEnabled(true); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); settings.setSupportZoom(true); @@ -78,12 +83,22 @@ public class MainActivity extends AppCompatActivity { settings.setDisplayZoomControls(false); settings.setCacheMode(WebSettings.LOAD_DEFAULT); settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + settings.setAllowFileAccess(false); + settings.setSaveFormData(true); + + // Accept all cookies so login sessions persist + CookieManager.getInstance().setAcceptCookie(true); + CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true); + + String ua = settings.getUserAgentString(); + settings.setUserAgentString(ua + " MealTrackerApp/1.0"); webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { progressBar.setVisibility(View.VISIBLE); errorView.setVisibility(View.GONE); + webView.setVisibility(View.VISIBLE); } @Override @@ -111,8 +126,9 @@ public class MainActivity extends AppCompatActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + String url = request.getUrl().toString(); // Keep all navigation inside the WebView - view.loadUrl(request.getUrl().toString()); + view.loadUrl(url); return true; } }); @@ -125,17 +141,24 @@ public class MainActivity extends AppCompatActivity { swipeRefresh.setRefreshing(false); } } + + @Override + public void onReceivedTitle(WebView view, String title) { + if (title != null && !title.isEmpty() && !title.startsWith("http")) { + toolbarTitle.setText(title); + } + } }); - // Retry on error tap errorView.setOnClickListener(v -> { - if (currentUrl != null) { - loadUrl(currentUrl); + if (serverUrl != null) { + loadUrl(serverUrl); } }); } private void loadUrl(String url) { + serverUrl = url; currentUrl = url; errorView.setVisibility(View.GONE); webView.setVisibility(View.VISIBLE); @@ -151,32 +174,61 @@ public class MainActivity extends AppCompatActivity { TextView hintView = dialogView.findViewById(R.id.url_hint); SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); - String savedUrl = prefs.getString(KEY_PUBLIC_URL, ""); + String savedUrl = prefs.getString(KEY_SERVER_URL, ""); urlInput.setText(savedUrl); - hintView.setText(R.string.url_hint_text); builder.setView(dialogView); builder.setPositiveButton(R.string.save, (dialog, which) -> { String url = urlInput.getText().toString().trim(); if (!url.isEmpty()) { + // Normalize: strip trailing slashes, auto-add https:// + url = url.replaceAll("/+$", ""); if (!url.startsWith("http://") && !url.startsWith("https://")) { url = "https://" + url; } - prefs.edit().putString(KEY_PUBLIC_URL, url).apply(); + prefs.edit().putString(KEY_SERVER_URL, url).apply(); loadUrl(url); Toast.makeText(this, R.string.url_saved, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, R.string.url_empty, Toast.LENGTH_SHORT).show(); } }); - builder.setNegativeButton(R.string.cancel, null); + builder.setNegativeButton(R.string.cancel, (dialog, which) -> { + if (serverUrl == null || serverUrl.isEmpty()) { + finish(); + } + }); + builder.setNeutralButton(R.string.clear_data, (dialog, which) -> { + clearAllData(); + prefs.edit().remove(KEY_SERVER_URL).apply(); + serverUrl = null; + webView.loadUrl("about:blank"); + webView.setVisibility(View.GONE); + errorView.setText(R.string.server_not_configured); + errorView.setVisibility(View.VISIBLE); + toolbarTitle.setText(R.string.app_name); + Toast.makeText(this, R.string.data_cleared, Toast.LENGTH_SHORT).show(); + }); builder.setCancelable(false); builder.show(); } + private void clearAllData() { + webView.clearCache(true); + webView.clearHistory(); + webView.clearFormData(); + CookieManager.getInstance().removeAllCookies(null); + CookieManager.getInstance().flush(); + // Clear WebView storage + webView.clearSslPreferences(); + // Nuke DOM/local storage + webView.evaluateJavascript("localStorage.clear(); sessionStorage.clear();", null); + } + private boolean isNetworkAvailable() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + if (cm == null) return false; NetworkInfo info = cm.getActiveNetworkInfo(); return info != null && info.isConnected(); } @@ -189,4 +241,16 @@ public class MainActivity extends AppCompatActivity { } return super.onKeyDown(keyCode, event); } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + webView.saveState(outState); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + webView.restoreState(savedInstanceState); + } } diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index e533eb0..70225ea 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -20,16 +20,26 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - app:title="@string/app_name" - app:titleTextColor="@android:color/white" - android:background="@color/colorPrimary"> + android:background="@color/colorPrimary" + app:contentInsetStart="12dp"> + + @@ -39,7 +49,6 @@ android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="end" - android:layout_marginEnd="4dp" android:src="@drawable/ic_settings" android:background="?attr/selectableItemBackgroundBorderless" android:contentDescription="@string/settings" /> diff --git a/android/app/src/main/res/layout/dialog_settings.xml b/android/app/src/main/res/layout/dialog_settings.xml index ade161c..e941d29 100644 --- a/android/app/src/main/res/layout/dialog_settings.xml +++ b/android/app/src/main/res/layout/dialog_settings.xml @@ -12,18 +12,19 @@ android:text="@string/url_label" android:textSize="14sp" android:textColor="@android:color/darker_gray" - android:paddingBottom="4dp" /> + android:paddingBottom="6dp" /> + android:importantForAutofill="no" + android:autofillHints="no" /> Meal Tracker Refresh Settings - Public URL - Enter the public sharing URL for your household: - Paste the link your household admin shared with you. It looks like:\nhttps://.../public/abc123... + Server Configuration + Enter your Meal Tracker server URL: + This is the URL where your household\'s meal tracker is hosted.\nExample: https://meals.example.com Save Cancel - URL saved - Please enter a URL + Clear Data + Connected to server + Please enter a server URL No network connection.\nTap to retry. - Could not load the page.\nTap to retry. + Could not reach the server.\nCheck the URL and tap to retry. + No server configured.\nTap the gear icon to set up. + Local data cleared