Initial commit: DuckAI Android app with WebView and search widget

This commit is contained in:
Alex Abudaev 2026-04-03 18:28:13 +08:00
commit 80e07cefef
14 changed files with 281 additions and 0 deletions

39
app/build.gradle Normal file
View file

@ -0,0 +1,39 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.duckai.app'
compileSdk 34
defaultConfig {
applicationId "com.duckai.app"
minSdk 31
targetSdk 34
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.DuckAI">
<activity
android:name=".web.MainActivity"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="duckai"
android:host="search" />
</intent-filter>
</activity>
<receiver
android:name=".widget.SearchWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/search_widget_info" />
</receiver>
</application>
</manifest>

View file

@ -0,0 +1,43 @@
package com.duckai.app.web
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import com.duckai.app.R
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webView)
webView.settings.javaScriptEnabled = true
webView.webViewClient = WebViewClient()
val query = intent?.data?.getQueryParameter("q")
val url = if (query != null) {
"https://duck.ai/?q=${Uri.encode(query)}"
} else {
"https://duck.ai/"
}
webView.loadUrl(url)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
this.intent = intent
val query = intent?.data?.getQueryParameter("q")
if (query != null) {
val url = "https://duck.ai/?q=${Uri.encode(query)}"
webView.loadUrl(url)
}
}
}

View file

@ -0,0 +1,38 @@
package com.duckai.app.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import com.duckai.app.R
import com.duckai.app.web.MainActivity
class SearchWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val views = RemoteViews(context.packageName, R.layout.widget_search).apply {
setOnClickPendingIntent(R.id.widget_container, pendingIntent)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#757575"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<corners android:radius="8dp" />
<stroke
android:width="1dp"
android:color="#E0E0E0" />
</shape>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webViewContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_background"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_search"
android:contentDescription="@string/search" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/search_duckai"
android:textColor="#757575"
android:textSize="14sp" />
</LinearLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">DuckAI</string>
<string name="search">Search</string>
<string name="search_duckai">Search DuckAI</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.DuckAI" parent="Theme.Material3.DayNight.NoActionBar" />
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="250dp"
android:minHeight="50dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/widget_search"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen" />