package dev.n1ko.nikogpt import android.annotation.SuppressLint import android.app.DownloadManager import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.net.Uri import android.net.http.SslError import android.os.Bundle import android.os.Environment import android.view.View import android.webkit.CookieManager import android.webkit.DownloadListener import android.webkit.JavascriptInterface import android.webkit.SslErrorHandler import android.webkit.URLUtil import android.webkit.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebSettings import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Button import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.credentials.CredentialManager import androidx.credentials.GetCredentialRequest import androidx.credentials.exceptions.GetCredentialException import androidx.lifecycle.lifecycleScope import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import kotlinx.coroutines.launch /** * The whole app: a thin, open-source WebView around the NikoGPT web chat. * * Only chat.n1ko.dev is loaded inside; every other link opens in the system * browser. File attachments go through the system document picker, downloads * through DownloadManager. TLS errors are never bypassed. */ class MainActivity : AppCompatActivity() { companion object { private const val START_URL = "https://chat.n1ko.dev/" private const val HOST = "chat.n1ko.dev" private const val UA_SUFFIX = " NikoGPT-Android/1.0" } private lateinit var webView: WebView private lateinit var refresh: SwipeRefreshLayout private lateinit var progress: ProgressBar private lateinit var errorBox: LinearLayout private lateinit var errorText: TextView private var fileCallback: ValueCallback>? = null private val filePicker: ActivityResultLauncher> = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris -> fileCallback?.onReceiveValue(uris?.toTypedArray() ?: arrayOf()) fileCallback = null } @SuppressLint("SetJavaScriptEnabled") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContentView(R.layout.activity_main) webView = findViewById(R.id.webview) refresh = findViewById(R.id.refresh) progress = findViewById(R.id.progress) errorBox = findViewById(R.id.error_box) errorText = findViewById(R.id.error_text) findViewById