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