Skip to content
Merged

Apps #176

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ dependencies {
implementation(libs.androidx.compose.foundation)
implementation(libs.material)
implementation(libs.androidx.material3)
implementation(libs.androidx.foundation.layout)
implementation(libs.androidx.foundation)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(platform(libs.androidx.compose.bom))
Expand Down Expand Up @@ -98,6 +100,9 @@ dependencies {

// Google Maps & Location
implementation(libs.play.services.location)
implementation(libs.play.services.wearable)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.gson)

// Kotlin Reflect for dynamic sealed class serialization
implementation(kotlin("reflect"))
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-feature android:name="android.hardware.camera.flashlight" android:required="false" />

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
Expand Down Expand Up @@ -29,6 +30,7 @@
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<permission android:name="com.sameerasw.permission.ESSENTIALS_AIRSYNC_BRIDGE" android:protectionLevel="signature" />
<uses-permission android:name="com.sameerasw.permission.ESSENTIALS_AIRSYNC_BRIDGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />


<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
Expand Down Expand Up @@ -87,6 +89,12 @@
android:label="Settings"
android:theme="@style/Theme.Essentials">
</activity>
<activity
android:name=".AppUpdatesActivity"
android:exported="false"
android:label="@string/tab_app_updates_title"
android:theme="@style/Theme.Essentials">
</activity>

<activity
android:name=".ui.activities.LocationAlarmActivity"
Expand Down Expand Up @@ -616,6 +624,16 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

</manifest>
393 changes: 393 additions & 0 deletions app/src/main/java/com/sameerasw/essentials/AppUpdatesActivity.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class EssentialsApp : Application() {
// Init Automation
com.sameerasw.essentials.domain.diy.DIYRepository.init(this)
com.sameerasw.essentials.services.automation.AutomationManager.init(this)
com.sameerasw.essentials.services.CalendarSyncManager.init(this)

val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,13 @@ class FeatureSettingsActivity : FragmentActivity() {
highlightSetting = highlightSetting
)
}
"Calendar Sync" -> {
com.sameerasw.essentials.ui.composables.configs.CalendarSyncSettingsUI(
viewModel = viewModel,
modifier = Modifier.padding(top = 16.dp),
highlightKey = highlightSetting
)
}
// else -> default UI (optional cleanup)
}
}
Expand Down
356 changes: 351 additions & 5 deletions app/src/main/java/com/sameerasw/essentials/MainActivity.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import com.sameerasw.essentials.domain.DIYTabs
import com.sameerasw.essentials.domain.registry.PermissionRegistry
import com.sameerasw.essentials.ui.components.sheets.InstructionsBottomSheet
import java.text.SimpleDateFormat
Expand Down Expand Up @@ -297,9 +298,13 @@ fun SettingsContent(viewModel: MainViewModel, modifier: Modifier = Modifier) {

val defaultTab by viewModel.defaultTab
RoundedCardContainer {
val availableTabs = remember(isDeveloperModeEnabled) {
if (isDeveloperModeEnabled) DIYTabs.entries else listOf(DIYTabs.ESSENTIALS, DIYTabs.FREEZE, DIYTabs.DIY)
}
DefaultTabPicker(
selectedTab = defaultTab,
onTabSelected = { viewModel.setDefaultTab(it, context) }
onTabSelected = { viewModel.setDefaultTab(it, context) },
options = availableTabs
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.sameerasw.essentials.data.repository

import com.google.gson.Gson
import com.sameerasw.essentials.domain.model.github.DeviceCodeResponse
import com.sameerasw.essentials.domain.model.github.TokenResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException

class GitHubAuthRepository {
private val client = OkHttpClient()
private val gson = Gson()
private val clientId = "Ov23lisMyhKfjlM5M5ec" // Provided by user

suspend fun requestDeviceCode(): DeviceCodeResponse? = withContext(Dispatchers.IO) {
try {
val requestBody = FormBody.Builder()
.add("client_id", clientId)
.add("scope", "public_repo")
.build()

val request = Request.Builder()
.url("https://github.com/login/device/code")
.header("Accept", "application/json")
.post(requestBody)
.build()

val response = client.newCall(request).execute()
if (!response.isSuccessful) return@withContext null

val responseBody = response.body?.string() ?: return@withContext null
gson.fromJson(responseBody, DeviceCodeResponse::class.java)
} catch (e: Exception) {
e.printStackTrace()
null
}
}

suspend fun pollForToken(deviceCode: String, interval: Int): TokenResponse? = withContext(Dispatchers.IO) {
val requestBody = FormBody.Builder()
.add("client_id", clientId)
.add("device_code", deviceCode)
.add("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
.build()

val request = Request.Builder()
.url("https://github.com/login/oauth/access_token")
.header("Accept", "application/json")
.post(requestBody)
.build()

try {
val response = client.newCall(request).execute()
if (!response.isSuccessful) return@withContext null

val responseBody = response.body?.string() ?: return@withContext null
gson.fromJson(responseBody, TokenResponse::class.java)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.sameerasw.essentials.data.repository

import com.google.gson.Gson
import com.sameerasw.essentials.domain.model.github.GitHubRelease
import com.sameerasw.essentials.domain.model.github.GitHubRepo
import com.sameerasw.essentials.domain.model.github.GitHubUser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.HttpURLConnection
import java.net.URL
import java.util.Base64

class GitHubRepository {
private val gson = Gson()

suspend fun getRepoInfo(owner: String, repo: String, token: String? = null): GitHubRepo? = withContext(Dispatchers.IO) {
try {
val url = URL("https://api.github.com/repos/$owner/$repo")
val connection = url.openConnection() as HttpURLConnection
if (token != null) {
connection.setRequestProperty("Authorization", "Bearer $token")
}
if (connection.responseCode == 200) {
val data = connection.inputStream.bufferedReader().readText()
gson.fromJson(data, GitHubRepo::class.java)
} else if (connection.responseCode == 403 || connection.responseCode == 429) {
throw Exception("RATE_LIMIT")
} else null
} catch (e: Exception) {
if (e.message == "RATE_LIMIT") throw e
e.printStackTrace()
null
}
}

suspend fun getLatestRelease(owner: String, repo: String, token: String? = null): GitHubRelease? = withContext(Dispatchers.IO) {
try {
val url = URL("https://api.github.com/repos/$owner/$repo/releases/latest")
val connection = url.openConnection() as HttpURLConnection
if (token != null) {
connection.setRequestProperty("Authorization", "Bearer $token")
}
if (connection.responseCode == 200) {
val data = connection.inputStream.bufferedReader().readText()
gson.fromJson(data, GitHubRelease::class.java)
} else if (connection.responseCode == 403 || connection.responseCode == 429) {
throw Exception("RATE_LIMIT")
} else null
} catch (e: Exception) {
if (e.message == "RATE_LIMIT") throw e
e.printStackTrace()
null
}
}

suspend fun getReleases(owner: String, repo: String, token: String? = null): List<GitHubRelease> = withContext(Dispatchers.IO) {
try {
val url = URL("https://api.github.com/repos/$owner/$repo/releases")
val connection = url.openConnection() as HttpURLConnection
if (token != null) {
connection.setRequestProperty("Authorization", "Bearer $token")
}
if (connection.responseCode == 200) {
val data = connection.inputStream.bufferedReader().readText()
val listType = object : com.google.gson.reflect.TypeToken<List<GitHubRelease>>() {}.type
gson.fromJson(data, listType)
} else if (connection.responseCode == 403 || connection.responseCode == 429) {
throw Exception("RATE_LIMIT")
} else emptyList()
} catch (e: Exception) {
if (e.message == "RATE_LIMIT") throw e
e.printStackTrace()
emptyList()
}
}

suspend fun getReadme(owner: String, repo: String, token: String? = null): String? = withContext(Dispatchers.IO) {
try {
val url = URL("https://api.github.com/repos/$owner/$repo/readme")
val connection = url.openConnection() as HttpURLConnection
if (token != null) {
connection.setRequestProperty("Authorization", "Bearer $token")
}
if (connection.responseCode == 200) {
val data = connection.inputStream.bufferedReader().readText()
val readmeMap = gson.fromJson(data, Map::class.java)
val content = readmeMap["content"] as? String ?: return@withContext null
val encoding = readmeMap["encoding"] as? String
if (encoding == "base64") {
String(android.util.Base64.decode(content, android.util.Base64.DEFAULT))
} else content
} else if (connection.responseCode == 403 || connection.responseCode == 429) {
throw Exception("RATE_LIMIT")
} else null
} catch (e: Exception) {
if (e.message == "RATE_LIMIT") throw e
e.printStackTrace()
null
}
}

suspend fun getUserProfile(token: String): GitHubUser? = withContext(Dispatchers.IO) {
try {
val url = URL("https://api.github.com/user")
val connection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Authorization", "Bearer $token")
connection.setRequestProperty("Accept", "application/vnd.github+json")

if (connection.responseCode == 200) {
val data = connection.inputStream.bufferedReader().readText()
gson.fromJson(data, GitHubUser::class.java)
} else if (connection.responseCode == 403 || connection.responseCode == 429) {
throw Exception("RATE_LIMIT")
} else null
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
Loading