Skip to content

Commit b0f6a9a

Browse files
Initial GameDeck scaffold: Compose UI, overlay service, CI workflow
1 parent bd48c7b commit b0f6a9a

17 files changed

Lines changed: 842 additions & 32 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
1-
name: Build Android APK
1+
name: Build APK
22

33
on:
44
push:
5-
branches: [ master, main ]
5+
branches: [main]
66
workflow_dispatch:
77

88
jobs:
99
build:
1010
runs-on: ubuntu-latest
11-
1211
steps:
13-
- name: Checkout repository
14-
uses: actions/checkout@v4
15-
16-
- name: Set up JDK 17
17-
uses: actions/setup-java@v4
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-java@v4
1814
with:
19-
java-version: '17'
20-
distribution: 'temurin'
21-
22-
- name: Create debug keystore
23-
run: keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"
24-
25-
- name: Grant execute permission for gradlew
26-
run: chmod +x gradlew
27-
28-
- name: Build APK with Wrapper
29-
run: ./gradlew assembleDebug --stacktrace
30-
31-
- name: Upload APK Artifact
15+
distribution: temurin
16+
java-version: "17"
17+
- uses: gradle/actions/setup-gradle@v3
18+
with:
19+
gradle-version: "8.7"
20+
- name: Build debug APK
21+
run: gradle assembleDebug --no-daemon
22+
- name: Upload APK
3223
uses: actions/upload-artifact@v4
3324
with:
34-
name: GameDeck-Debug-APK
25+
name: gamedeck-debug
3526
path: app/build/outputs/apk/debug/app-debug.apk
27+
retention-days: 30

‎.gitignore‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
*.iml
2-
.gradle
3-
.kotlin
2+
.gradle/
43
/local.properties
5-
/.idea/caches
6-
/.idea/libraries
7-
/.idea/modules.xml
8-
/.idea/workspace.xml
9-
/.idea/navEditor.xml
10-
/.idea/assetWizardSettings.xml
4+
/.idea/
115
.DS_Store
126
/build
7+
/app/build
138
/captures
149
.externalNativeBuild
1510
.cxx
1611
local.properties
17-
.env
18-
debug.keystore

‎app/build.gradle.kts‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
plugins {
2+
id("com.android.application")
3+
id("org.jetbrains.kotlin.android")
4+
}
5+
6+
android {
7+
namespace = "com.joebuildsthings.gamedeck"
8+
compileSdk = 34
9+
10+
defaultConfig {
11+
applicationId = "com.joebuildsthings.gamedeck"
12+
minSdk = 30
13+
targetSdk = 34
14+
versionCode = 1
15+
versionName = "0.1.0"
16+
}
17+
18+
buildTypes {
19+
release {
20+
isMinifyEnabled = false
21+
}
22+
}
23+
24+
compileOptions {
25+
sourceCompatibility = JavaVersion.VERSION_17
26+
targetCompatibility = JavaVersion.VERSION_17
27+
}
28+
29+
kotlinOptions {
30+
jvmTarget = "17"
31+
}
32+
33+
buildFeatures {
34+
compose = true
35+
}
36+
37+
composeOptions {
38+
kotlinCompilerExtensionVersion = "1.5.14"
39+
}
40+
}
41+
42+
dependencies {
43+
implementation("androidx.core:core-ktx:1.13.1")
44+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.5")
45+
implementation("androidx.activity:activity-compose:1.9.2")
46+
implementation(platform("androidx.compose:compose-bom:2024.09.03"))
47+
implementation("androidx.compose.ui:ui")
48+
implementation("androidx.compose.ui:ui-graphics")
49+
implementation("androidx.compose.material3:material3")
50+
implementation("androidx.compose.material:material-icons-extended")
51+
}

‎app/src/main/AndroidManifest.xml‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
6+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
7+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
8+
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
9+
tools:ignore="ProtectedPermissions" />
10+
11+
<application
12+
android:label="GameDeck"
13+
android:icon="@drawable/ic_launcher"
14+
android:theme="@style/Theme.GameDeck"
15+
android:allowBackup="true"
16+
android:supportsRtl="true">
17+
18+
<activity
19+
android:name=".MainActivity"
20+
android:exported="true"
21+
android:theme="@style/Theme.GameDeck">
22+
<intent-filter>
23+
<action android:name="android.intent.action.MAIN" />
24+
<category android:name="android.intent.category.LAUNCHER" />
25+
</intent-filter>
26+
</activity>
27+
28+
<service
29+
android:name=".OverlayService"
30+
android:exported="false"
31+
android:foregroundServiceType="specialUse" />
32+
33+
</application>
34+
</manifest>
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.joebuildsthings.gamedeck
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
import android.os.Bundle
6+
import android.provider.Settings
7+
import androidx.activity.ComponentActivity
8+
import androidx.activity.compose.setContent
9+
import androidx.compose.foundation.background
10+
import androidx.compose.foundation.layout.*
11+
import androidx.compose.material.icons.Icons
12+
import androidx.compose.material.icons.filled.Home
13+
import androidx.compose.material.icons.filled.Settings
14+
import androidx.compose.material.icons.filled.SportsEsports
15+
import androidx.compose.material3.*
16+
import androidx.compose.runtime.*
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.graphics.vector.ImageVector
19+
import androidx.compose.ui.text.font.FontWeight
20+
import androidx.compose.ui.unit.dp
21+
import androidx.compose.ui.unit.sp
22+
import com.joebuildsthings.gamedeck.screens.GamesScreen
23+
import com.joebuildsthings.gamedeck.screens.HomeScreen
24+
import com.joebuildsthings.gamedeck.screens.SettingsScreen
25+
import com.joebuildsthings.gamedeck.ui.theme.AccentRed
26+
import com.joebuildsthings.gamedeck.ui.theme.BgDark
27+
import com.joebuildsthings.gamedeck.ui.theme.GameDeckTheme
28+
import com.joebuildsthings.gamedeck.ui.theme.InkLight
29+
import com.joebuildsthings.gamedeck.ui.theme.LineDark
30+
import com.joebuildsthings.gamedeck.ui.theme.MutedGrey
31+
32+
data class Tab(val label: String, val icon: ImageVector)
33+
34+
class MainActivity : ComponentActivity() {
35+
override fun onCreate(savedInstanceState: Bundle?) {
36+
super.onCreate(savedInstanceState)
37+
setContent {
38+
GameDeckTheme {
39+
Surface(
40+
modifier = Modifier.fillMaxSize(),
41+
color = BgDark
42+
) {
43+
GameDeckApp(
44+
onRequestOverlay = { requestOverlayPermission() },
45+
onToggleOverlay = { enable -> toggleOverlay(enable) }
46+
)
47+
}
48+
}
49+
}
50+
}
51+
52+
private fun requestOverlayPermission() {
53+
if (!Settings.canDrawOverlays(this)) {
54+
startActivity(
55+
Intent(
56+
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
57+
Uri.parse("package:$packageName")
58+
)
59+
)
60+
}
61+
}
62+
63+
private fun toggleOverlay(enable: Boolean) {
64+
val intent = Intent(this, OverlayService::class.java).apply {
65+
action = if (enable) OverlayService.ACTION_START else OverlayService.ACTION_STOP
66+
}
67+
if (enable) startForegroundService(intent) else startService(intent)
68+
}
69+
}
70+
71+
@OptIn(ExperimentalMaterial3Api::class)
72+
@Composable
73+
fun GameDeckApp(
74+
onRequestOverlay: () -> Unit,
75+
onToggleOverlay: (Boolean) -> Unit,
76+
) {
77+
val tabs = listOf(
78+
Tab("Home", Icons.Filled.Home),
79+
Tab("Games", Icons.Filled.SportsEsports),
80+
Tab("Settings", Icons.Filled.Settings),
81+
)
82+
var current by remember { mutableIntStateOf(0) }
83+
84+
Scaffold(
85+
containerColor = BgDark,
86+
topBar = {
87+
TopAppBar(
88+
title = {
89+
Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) {
90+
Text(
91+
"GameDeck",
92+
color = InkLight,
93+
fontWeight = FontWeight.SemiBold,
94+
fontSize = 18.sp,
95+
)
96+
Spacer(Modifier.width(10.dp))
97+
Text(
98+
"v0.1",
99+
color = MutedGrey,
100+
fontSize = 11.sp,
101+
)
102+
}
103+
},
104+
colors = TopAppBarDefaults.topAppBarColors(
105+
containerColor = BgDark,
106+
)
107+
)
108+
},
109+
bottomBar = {
110+
NavigationBar(containerColor = BgDark) {
111+
tabs.forEachIndexed { i, tab ->
112+
NavigationBarItem(
113+
selected = current == i,
114+
onClick = { current = i },
115+
icon = { Icon(tab.icon, contentDescription = tab.label) },
116+
label = { Text(tab.label, fontSize = 11.sp) },
117+
colors = NavigationBarItemDefaults.colors(
118+
selectedIconColor = AccentRed,
119+
selectedTextColor = AccentRed,
120+
unselectedIconColor = MutedGrey,
121+
unselectedTextColor = MutedGrey,
122+
indicatorColor = LineDark,
123+
)
124+
)
125+
}
126+
}
127+
}
128+
) { pad ->
129+
Box(
130+
Modifier
131+
.fillMaxSize()
132+
.background(BgDark)
133+
.padding(pad)
134+
) {
135+
when (current) {
136+
0 -> HomeScreen(
137+
onRequestOverlay = onRequestOverlay,
138+
onToggleOverlay = onToggleOverlay,
139+
)
140+
1 -> GamesScreen()
141+
2 -> SettingsScreen(onRequestOverlay = onRequestOverlay)
142+
}
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)