Skip to content

Commit 13545aa

Browse files
committed
Initial commit
0 parents  commit 13545aa

80 files changed

Lines changed: 4840 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
.idea/
5+
.DS_Store
6+
/build
7+
/captures
8+
.externalNativeBuild
9+
.cxx
10+
local.properties
11+
.kotlin
12+
.idea
13+
**/build/
14+
app/release

‎README.md‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# StreamGate Android App Project
2+
3+
StreamGate is an open-source native Android application designed as a technical showcase for capturing, processing, and uploading video content directly to the cloud. Built purely for developers, it demonstrates how to handle complex mobile media workflows—such as local video selection, camera capture, background screen recording, and reliable direct-to-cloud resumable uploads using FastPix infrastructure.
4+
5+
Once an upload completes, StreamGate generates an instant, shareable playback link.
6+
7+
---
8+
9+
## Core Features & Architecture
10+
11+
StreamGate is built using a modern Android tech stack (100% Kotlin, Jetpack Compose, Dagger Hilt) and focuses on the following core capabilities:
12+
13+
1. **Camera Capture**: Integrates `CameraX` (`camera-camera2`, `camera-video`, etc.) for robust, native video recording directly within the app.
14+
2. **Screen Recording**: Uses Android's `MediaProjection` API combined with Foreground Services to capture device-wide screen activity reliably.
15+
3. **Gallery Picker**: Implements native Android intent workflows to browse and select existing local video files.
16+
4. **Direct Cloud Uploading**: Utilizes the FastPix Resumable Uploads SDK to push large media files securely to the cloud without needing to route them through an intermediary backend server.
17+
5. **Local Playback/Preview**: Integrates the FastPix Android Player SDK (`io.fastpix.player:android`) to preview the selected or recorded video locally before generating the final shareable link.
18+
19+
---
20+
21+
## Tech Stack & Dependencies
22+
23+
* **Language**: Kotlin
24+
* **UI Framework**: Jetpack Compose (Material 3, `activity-compose`)
25+
* **Dependency Injection**: Dagger Hilt (`hilt-android`, `hilt-navigation-compose`)
26+
* **Camera**: CameraX (`camera-camera2`, `camera-video`, `camera-view`, `camera-lifecycle`, `camera-extensions`)
27+
* **Networking**: Retrofit with Kotlinx Serialization (`converter-kotlinx-serialization`)
28+
* **Media Loading**: Coil (`coil-compose`)
29+
* **Local Storage**: Jetpack DataStore (`datastore-preferences`)
30+
* **Build Constraints**: `minSdk = 24`, `targetSdk = 37`, `compileSdk = 37`, Java 11
31+
32+
---
33+
34+
## Setup & Build Instructions
35+
36+
Because StreamGate relies on FastPix's private GitHub Package Registry for its SDKs, and requires specific API keys to initialize uploads, you must configure your local environment before building.
37+
38+
### 1. Prerequisites
39+
You will need:
40+
* A **FastPix Account** (to retrieve your API Token ID and Secret Key).
41+
* A **GitHub Personal Access Token (PAT)** with `read:packages` permissions. FastPix SDKs are hosted on GitHub Packages (`maven.pkg.github.com/FastPix/android-uploads-sdk`), and Gradle requires this token to download them.
42+
43+
### 2. Configure `local.properties`
44+
Clone the repository and open the project in Android Studio. Before syncing Gradle, create or open the `local.properties` file in the root directory and add the following credentials:
45+
46+
```properties
47+
# FastPix API Credentials
48+
fastpix.tokenId=YOUR_FASTPIX_TOKEN_ID
49+
fastpix.secretKey=YOUR_FASTPIX_SECRET_KEY
50+
51+
# GitHub Credentials for Maven package resolution
52+
github.username=YOUR_GITHUB_USERNAME
53+
github.token=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN
54+
```
55+
56+
*Note: The FastPix keys are injected into the app at compile-time via `buildConfigField` as `BuildConfig.FASTPIX_TOKEN_ID` and `BuildConfig.FASTPIX_SECRET_KEY`. Never commit this file to version control.*
57+
58+
### 3. Build & Run (Command Line)
59+
If you prefer building from the terminal, use the following Gradle wrapper commands:
60+
61+
```bash
62+
# Clone the repository
63+
git clone https://github.com/your-username/StreamGate.git
64+
cd StreamGate
65+
66+
# Ensure local.properties is set up as detailed above, then clean the project
67+
./gradlew clean
68+
69+
# Build the debug APK
70+
./gradlew assembleDebug
71+
72+
# Install on a connected emulator or physical device
73+
./gradlew installDebug
74+
```
75+
76+
Alternatively, just hit **"Run"** in Android Studio once the Gradle sync successfully resolves the FastPix dependencies.
77+
78+
---
79+
80+
## Important Reference Links
81+
82+
* FastPix Platform: [fastpix.com](https://fastpix.com/)
83+
* FastPix Access Token Guide: [Activate Your Account](https://fastpix.com/docs/getting-started/activate-your-account)
84+
* FastPix VOD Upload API Docs: [Direct Upload Video Media](https://fastpix.com/docs/video-on-demand-api/upload-and-import-videos/direct-upload-video-media)
85+
* FastPix Uploads SDK Repo: [android-uploads-sdk](https://github.com/FastPix/android-uploads-sdk)
86+
* FastPix Player SDK Repo: [fastpix-android-player](https://github.com/FastPix/fastpix-android-player)
87+
88+
## License
89+
StreamGate is licensed under the MIT License.

‎app/.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

‎app/build.gradle.kts‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import java.util.Properties
2+
3+
plugins {
4+
alias(libs.plugins.android.application)
5+
alias(libs.plugins.kotlin.compose)
6+
alias(libs.plugins.dagger.hilt.android)
7+
alias(libs.plugins.kotlin.ksp)
8+
alias(libs.plugins.kotlin.serialization)
9+
}
10+
11+
android {
12+
namespace = "dev.streamgate.android"
13+
compileSdk = 37
14+
15+
defaultConfig {
16+
applicationId = "dev.streamgate.android"
17+
minSdk = 24
18+
targetSdk = 37
19+
versionCode = 1
20+
versionName = "1.0"
21+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
22+
23+
val localProperties = Properties()
24+
val localPropertiesFile = rootProject.file("local.properties")
25+
if (localPropertiesFile.exists()) {
26+
localPropertiesFile.inputStream().use { localProperties.load(it) }
27+
}
28+
29+
val fastPixTokenId = localProperties.getProperty("fastpix.tokenId") ?: ""
30+
val fastPixSecretKey = localProperties.getProperty("fastpix.secretKey") ?: ""
31+
32+
buildConfigField("String", "FASTPIX_TOKEN_ID", "\"$fastPixTokenId\"")
33+
buildConfigField("String", "FASTPIX_SECRET_KEY", "\"${fastPixSecretKey}\"")
34+
35+
}
36+
37+
buildTypes {
38+
release {
39+
isMinifyEnabled = true
40+
isShrinkResources = true
41+
proguardFiles(
42+
getDefaultProguardFile("proguard-android-optimize.txt"),
43+
"proguard-rules.pro"
44+
)
45+
signingConfig = signingConfigs.getByName("debug")
46+
}
47+
48+
debug {
49+
applicationIdSuffix = ".debug"
50+
versionNameSuffix = "-DEBUG"
51+
}
52+
}
53+
54+
compileOptions {
55+
sourceCompatibility = JavaVersion.VERSION_11
56+
targetCompatibility = JavaVersion.VERSION_11
57+
}
58+
buildFeatures {
59+
compose = true
60+
buildConfig = true
61+
}
62+
}
63+
64+
dependencies {
65+
implementation(platform(libs.androidx.compose.bom))
66+
implementation(libs.androidx.activity.compose)
67+
implementation(libs.androidx.compose.material3)
68+
implementation(libs.androidx.compose.ui)
69+
implementation(libs.androidx.compose.ui.graphics)
70+
implementation(libs.androidx.compose.ui.tooling.preview)
71+
implementation(libs.androidx.core.ktx)
72+
implementation(libs.androidx.lifecycle.runtime.ktx)
73+
testImplementation(libs.junit)
74+
androidTestImplementation(platform(libs.androidx.compose.bom))
75+
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
76+
androidTestImplementation(libs.androidx.espresso.core)
77+
androidTestImplementation(libs.androidx.junit)
78+
debugImplementation(libs.androidx.compose.ui.test.manifest)
79+
debugImplementation(libs.androidx.compose.ui.tooling)
80+
81+
implementation(libs.hilt.android)
82+
ksp(libs.hilt.compiler)
83+
ksp(libs.androidx.hilt.compiler) // for enabling dependency injection of certain classes from the androidx libraries
84+
85+
implementation(libs.androidx.hilt.navigation.compose)
86+
implementation(libs.kotlinx.serialization.json)
87+
88+
implementation(libs.fastpix.uploads)
89+
implementation(libs.fastpix.android.player)
90+
91+
implementation(libs.androidx.compose.material.icons.core)
92+
implementation(libs.androidx.compose.material.icons.extended)
93+
94+
implementation(libs.coil.compose)
95+
96+
implementation(libs.androidx.camera.camera2)
97+
implementation(libs.androidx.camera.lifecycle)
98+
implementation(libs.androidx.camera.video)
99+
implementation(libs.androidx.camera.view)
100+
implementation(libs.androidx.camera.extensions1)
101+
102+
implementation(libs.androidx.datastore.preferences)
103+
104+
implementation(libs.retrofit)
105+
implementation(libs.converter.kotlinx.serialization)
106+
implementation(libs.logging.interceptor)
107+
108+
}

‎app/proguard-rules.pro‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Tell R8 to suppress warnings about missing Parcelize runtime definitions from dependencies
2+
-dontwarn kotlinx.parcelize.Parcelize
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.streamgate.android
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("dev.streamgate.android", appContext.packageName)
23+
}
24+
}

‎app/src/main/AndroidManifest.xml‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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-feature
6+
android:name="android.hardware.camera.any" />
7+
8+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
9+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
10+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
11+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
12+
<uses-permission android:name="android.permission.CAMERA" />
13+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
14+
15+
<application
16+
android:name=".StreamGateApp"
17+
android:allowBackup="true"
18+
android:dataExtractionRules="@xml/data_extraction_rules"
19+
android:fullBackupContent="@xml/backup_rules"
20+
android:icon="@mipmap/ic_launcher"
21+
android:label="@string/app_name"
22+
android:roundIcon="@mipmap/ic_launcher_round"
23+
android:supportsRtl="true"
24+
android:theme="@style/Theme.StreamGate" >
25+
<activity
26+
android:name=".MainActivity"
27+
android:launchMode="singleTop"
28+
android:exported="true"
29+
android:theme="@style/Theme.StreamGate" >
30+
<intent-filter>
31+
<action android:name="android.intent.action.MAIN" />
32+
33+
<category android:name="android.intent.category.LAUNCHER" />
34+
</intent-filter>
35+
</activity>
36+
37+
<service
38+
android:name=".data.remote.VideoUploadService"
39+
android:foregroundServiceType="dataSync"
40+
android:exported="false" />
41+
42+
<service
43+
android:name=".data.local.ScreenRecordService"
44+
android:foregroundServiceType="mediaProjection"
45+
android:exported="false" />
46+
47+
<meta-data
48+
android:name="preloaded_fonts"
49+
android:resource="@array/preloaded_fonts" />
50+
51+
</application>
52+
53+
</manifest>
10.8 KB
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dev.streamgate.android
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
7+
import androidx.compose.material3.Surface
8+
import dagger.hilt.android.AndroidEntryPoint
9+
import dev.streamgate.android.ui.navigation.AppNavigation
10+
import dev.streamgate.android.ui.theme.StreamGateTheme
11+
12+
@AndroidEntryPoint
13+
class MainActivity : ComponentActivity() {
14+
override fun onCreate(savedInstanceState: Bundle?) {
15+
super.onCreate(savedInstanceState)
16+
enableEdgeToEdge()
17+
setContent {
18+
StreamGateTheme {
19+
Surface {
20+
AppNavigation()
21+
}
22+
}
23+
}
24+
}
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dev.streamgate.android
2+
3+
import android.app.Application
4+
import dagger.hilt.android.HiltAndroidApp
5+
6+
@HiltAndroidApp
7+
class StreamGateApp: Application()

0 commit comments

Comments
 (0)