initial gui implemented

This commit is contained in:
Acid
2026-03-22 20:31:26 -04:00
commit 66242f9443
22 changed files with 2002 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# Build artifacts
.gradle/
build/
app/build/
*.apk
*.aar
*.jks
# IDE settings
.idea/
*.iml
.DS_Store
captures/
# Sensitive info
local.properties
# Generated files
.cxx/
.externalNativeBuild/
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AppInsightsSettings">
<option name="selectedTabId" value="Android vitals" />
</component>
</project>
+1538
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-03-23T00:21:31.459746219Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/mahalo/.var/app/com.google.AndroidStudio/config/.android/avd/Medium_Phone.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK" />
</project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="StudioBotProjectSettings">
<option name="shareContext" value="OptedIn" />
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+70
View File
@@ -0,0 +1,70 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.stims'
compileSdk 34
defaultConfig {
applicationId "com.example.stims"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.8'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.8.2'
implementation platform('androidx.compose:compose-bom:2023.10.01')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.material:material-icons-extended'
// Added for XML theme support
implementation 'com.google.android.material:material:1.11.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation platform('androidx.compose:compose-bom:2023.10.01')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
}
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required to query all installed apps on Android 11+ -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:allowBackup="true"
android:icon="@android:drawable/sym_def_app_icon"
android:label="@string/app_name"
android:roundIcon="@android:drawable/sym_def_app_icon"
android:supportsRtl="true"
android:theme="@style/Theme.Stims">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,206 @@
package com.example.stims
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
data class AppInfo(
val name: String,
val packageName: String,
val icon: Drawable? = null
)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
AppListScreen()
}
}
}
}
@Composable
fun AppListScreen() {
val context = LocalContext.current
val packageManager = context.packageManager
var allApps by remember { mutableStateOf<List<AppInfo>>(emptyList()) }
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
allApps = withContext(Dispatchers.IO) {
val intent = Intent(Intent.ACTION_MAIN, null).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
// Query for all activities that can be launched
val resolveInfos = packageManager.queryIntentActivities(intent, 0)
resolveInfos.map { resolveInfo ->
AppInfo(
name = resolveInfo.loadLabel(packageManager).toString(),
packageName = resolveInfo.activityInfo.packageName,
icon = resolveInfo.loadIcon(packageManager)
)
}.distinctBy { it.packageName } // Avoid duplicates if an app has multiple launchers
.sortedBy { it.name }
}
isLoading = false
}
var searchQuery by remember { mutableStateOf("") }
val selectedApps = remember { mutableStateListOf<AppInfo>() }
val filteredApps = remember(searchQuery, allApps) {
if (searchQuery.isBlank()) {
allApps
} else {
allApps.filter {
it.name.contains(searchQuery, ignoreCase = true) ||
it.packageName.contains(searchQuery, ignoreCase = true)
}
}
}
if (isLoading) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
} else {
Column {
OutlinedTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
placeholder = { Text("Search apps...") },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
singleLine = true
)
if (selectedApps.isNotEmpty()) {
Text(
text = "${selectedApps.size} apps selected",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
color = MaterialTheme.colorScheme.primary
)
}
LazyColumn(modifier = Modifier.weight(1f)) {
items(filteredApps) { app ->
val isSelected = selectedApps.any { it.packageName == app.packageName }
AppItem(
app = app,
isSelected = isSelected,
onToggleSelection = {
if (isSelected) {
selectedApps.removeAll { it.packageName == app.packageName }
} else {
selectedApps.add(app)
}
}
)
}
}
if (selectedApps.isNotEmpty()) {
Button(
onClick = {
val names = selectedApps.joinToString { it.name }
Toast.makeText(context, "Selected: $names", Toast.LENGTH_LONG).show()
},
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text("Confirm Selection")
}
}
}
}
}
@Composable
fun AppItem(
app: AppInfo,
isSelected: Boolean,
onToggleSelection: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onToggleSelection)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
if (app.icon != null) {
Image(
bitmap = app.icon.toBitmap().asImageBitmap(),
contentDescription = null,
modifier = Modifier.size(48.dp)
)
} else {
Surface(
modifier = Modifier.size(48.dp),
color = MaterialTheme.colorScheme.primaryContainer,
shape = MaterialTheme.shapes.small
) {}
}
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) {
Text(text = app.name, style = MaterialTheme.typography.bodyLarge)
Text(
text = app.packageName,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Checkbox(
checked = isSelected,
onCheckedChange = { onToggleSelection() }
)
}
}
@Preview(showBackground = true)
@Composable
fun AppItemPreview() {
MaterialTheme {
AppItem(
app = AppInfo("Example App", "com.example.app", null),
isSelected = true,
onToggleSelection = {}
)
}
}
+3
View File
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Stims</string>
</resources>
+6
View File
@@ -0,0 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Stims" parent="Theme.Material3.Light.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
+18
View File
@@ -0,0 +1,18 @@
// Top-level build file
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
+3
View File
@@ -0,0 +1,3 @@
android.useAndroidX=true
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+2
View File
@@ -0,0 +1,2 @@
rootProject.name = "stims"
include ':app'