Skip to content

Commit 0744be9

Browse files
authored
Merge pull request #9 from sameerasw/develop
Develop
2 parents f7a0884 + a2da326 commit 0744be9

Some content is hidden

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

41 files changed

+1367
-23
lines changed

app/build.gradle.kts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
applicationId = "com.sameerasw.essentials"
1515
minSdk = 23
1616
targetSdk = 36
17-
versionCode = 5
18-
versionName = "4.0"
17+
versionCode = 6
18+
versionName = "5.0"
1919

2020
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2121
}
@@ -48,12 +48,18 @@ dependencies {
4848
implementation(libs.androidx.activity.compose)
4949
implementation(platform(libs.androidx.compose.bom))
5050

51+
// Android 12+ SplashScreen API with backward compatibility attributes
52+
implementation("androidx.core:core-splashscreen:1.0.1")
53+
5154
// Force latest Material3 1.5.0-alpha10 for ToggleButton & ButtonGroup support
5255
implementation("androidx.compose.material3:material3:1.5.0-alpha10")
5356

5457
implementation(libs.androidx.compose.ui)
5558
implementation(libs.androidx.compose.ui.graphics)
5659
implementation(libs.androidx.compose.ui.tooling.preview)
60+
implementation(libs.androidx.appcompat)
61+
implementation("androidx.activity:activity-compose:1.8.0")
62+
implementation("io.coil-kt:coil-compose:2.5.0")
5763
testImplementation(libs.junit)
5864
androidTestImplementation(libs.androidx.junit)
5965
androidTestImplementation(libs.androidx.espresso.core)

app/src/main/AndroidManifest.xml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
1414
<uses-permission android:name="moe.shizuku.manager.permission.API_V23" />
1515
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
16+
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
1617

1718
<application
1819
android:name=".EssentialsApp"
@@ -28,12 +29,16 @@
2829
android:name=".MainActivity"
2930
android:exported="true"
3031
android:label="@string/app_name"
31-
android:theme="@style/Theme.Essentials">
32+
android:theme="@style/Theme.Essentials.Splash">
3233
<intent-filter>
3334
<action android:name="android.intent.action.MAIN" />
3435

3536
<category android:name="android.intent.category.LAUNCHER" />
3637
</intent-filter>
38+
<intent-filter>
39+
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
40+
<category android:name="android.intent.category.DEFAULT" />
41+
</intent-filter>
3742
</activity>
3843

3944
<activity
@@ -50,6 +55,46 @@
5055
android:theme="@style/Theme.Essentials">
5156
</activity>
5257

58+
<activity
59+
android:name=".LinkPickerActivity"
60+
android:exported="true"
61+
android:label="Open Link"
62+
android:theme="@style/Theme.Essentials">
63+
<intent-filter>
64+
<action android:name="android.intent.action.SEND" />
65+
<category android:name="android.intent.category.DEFAULT" />
66+
<data android:mimeType="text/plain" />
67+
</intent-filter>
68+
<intent-filter>
69+
<action android:name="android.intent.action.VIEW" />
70+
<category android:name="android.intent.category.BROWSABLE" />
71+
<category android:name="android.intent.category.DEFAULT" />
72+
<data android:scheme="http" />
73+
<data android:scheme="https" />
74+
<data android:scheme="about" />
75+
<data android:scheme="javascript" />
76+
<data android:scheme="mailto" />
77+
<data android:scheme="geo" />
78+
<data android:scheme="file" />
79+
<data android:scheme="rtsp" />
80+
<data android:scheme="rtmp" />
81+
<data android:scheme="ftp" />
82+
<data android:scheme="sftp" />
83+
<data android:scheme="skype" />
84+
<data android:scheme="sms" />
85+
<data android:scheme="smsto" />
86+
<data android:scheme="mms" />
87+
<data android:scheme="mmsto" />
88+
<data android:scheme="spotify" />
89+
<data android:scheme="tel" />
90+
<data android:scheme="voicemail" />
91+
<data android:scheme="view-source" />
92+
<data android:scheme="sqlite" />
93+
<data android:scheme="store" />
94+
<data android:scheme="android" />
95+
</intent-filter>
96+
</activity>
97+
5398
<service
5499
android:name=".services.ScreenOffAccessibilityService"
55100
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"

app/src/main/java/com/sameerasw/essentials/FeatureSettingsActivity.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ import androidx.compose.ui.Modifier
3030
import androidx.compose.ui.input.nestedscroll.nestedScroll
3131
import androidx.compose.ui.platform.LocalContext
3232
import androidx.compose.ui.unit.dp
33+
import androidx.core.net.toUri
3334
import com.sameerasw.essentials.ui.components.ReusableTopAppBar
3435
import com.sameerasw.essentials.ui.theme.EssentialsTheme
3536
import com.sameerasw.essentials.utils.HapticFeedbackType
3637
import androidx.lifecycle.viewmodel.compose.viewModel
38+
import com.sameerasw.essentials.ui.components.linkActions.LinkPickerScreen
3739
import com.sameerasw.essentials.ui.composables.configs.StatusBarIconSettingsUI
3840
import com.sameerasw.essentials.ui.composables.configs.CaffeinateSettingsUI
3941
import com.sameerasw.essentials.ui.composables.configs.ScreenOffWidgetSettingsUI
@@ -57,7 +59,8 @@ class FeatureSettingsActivity : ComponentActivity() {
5759
"Statusbar icons" to "Control statusbar icons visibility",
5860
"Caffeinate" to "Keep the screen awake",
5961
"Edge lighting" to "Preview edge lighting effects on new notifications",
60-
"Sound mode tile" to "QS tile to toggle sound mode"
62+
"Sound mode tile" to "QS tile to toggle sound mode",
63+
"Link actions" to "Handle links with multiple apps"
6164
)
6265
val description = featureDescriptions[feature] ?: ""
6366
setContent {
@@ -250,6 +253,18 @@ class FeatureSettingsActivity : ComponentActivity() {
250253
"Sound mode tile" -> {
251254
SoundModeTileSettingsUI(modifier = Modifier.padding(top = 16.dp))
252255
}
256+
"Link actions" -> {
257+
setContent {
258+
EssentialsTheme {
259+
LinkPickerScreen(
260+
uri = "https://sameerasw.com".toUri(),
261+
onFinish = { finish() },
262+
modifier = Modifier.fillMaxSize(),
263+
demo = true
264+
)
265+
}
266+
}
267+
}
253268
else -> {
254269
ScreenOffWidgetSettingsUI(
255270
viewModel = viewModel,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.sameerasw.essentials
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
import android.os.Bundle
6+
import androidx.activity.compose.setContent
7+
import androidx.activity.enableEdgeToEdge
8+
import androidx.appcompat.app.AppCompatActivity
9+
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.ui.Modifier
11+
import com.sameerasw.essentials.ui.components.linkActions.LinkPickerScreen
12+
import com.sameerasw.essentials.ui.theme.EssentialsTheme
13+
14+
class LinkPickerActivity : AppCompatActivity() {
15+
override fun onCreate(savedInstanceState: Bundle?) {
16+
super.onCreate(savedInstanceState)
17+
18+
val uri = when (intent.action) {
19+
Intent.ACTION_SEND -> {
20+
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
21+
extractUrl(text)?.let { Uri.parse(it) }
22+
}
23+
else -> intent.data
24+
}
25+
26+
if (uri == null) {
27+
finish()
28+
return
29+
}
30+
31+
enableEdgeToEdge()
32+
33+
setContent {
34+
EssentialsTheme {
35+
LinkPickerScreen(uri = uri, onFinish = { finish() }, modifier = Modifier.fillMaxSize())
36+
}
37+
}
38+
}
39+
}
40+
41+
private fun extractUrl(text: String): String? {
42+
val urlRegex = Regex("https?://[\\w\\.-]+(?:\\:[0-9]+)?(?:/[^\\s]*)?")
43+
return urlRegex.find(text)?.value
44+
}

app/src/main/java/com/sameerasw/essentials/MainActivity.kt

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.sameerasw.essentials
22

3+
import android.animation.ObjectAnimator
34
import android.content.Intent
45
import android.os.Bundle
6+
import android.util.Log
7+
import android.view.animation.AnticipateInterpolator
58
import androidx.activity.ComponentActivity
69
import androidx.activity.compose.setContent
710
import androidx.activity.enableEdgeToEdge
@@ -16,23 +19,83 @@ import androidx.compose.runtime.*
1619
import androidx.compose.ui.input.nestedscroll.nestedScroll
1720
import androidx.compose.ui.Modifier
1821
import androidx.compose.ui.platform.LocalContext
19-
import androidx.compose.ui.tooling.preview.Preview
22+
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
23+
import androidx.core.animation.doOnEnd
2024
import com.sameerasw.essentials.ui.components.ReusableTopAppBar
2125
import com.sameerasw.essentials.ui.composables.SetupFeatures
2226
import com.sameerasw.essentials.ui.theme.EssentialsTheme
23-
import com.sameerasw.essentials.utils.ShizukuUtils
2427
import com.sameerasw.essentials.utils.HapticUtil
2528
import com.sameerasw.essentials.viewmodels.MainViewModel
2629

2730
@OptIn(ExperimentalMaterial3Api::class)
2831
class MainActivity : ComponentActivity() {
2932
val viewModel: MainViewModel by viewModels()
33+
private var isAppReady = false
3034

3135
override fun onCreate(savedInstanceState: Bundle?) {
3236
super.onCreate(savedInstanceState)
37+
38+
// Install and configure the splash screen
39+
val splashScreen = installSplashScreen()
40+
41+
// Keep splash screen visible while app is loading
42+
splashScreen.setKeepOnScreenCondition { !isAppReady }
43+
44+
// Customize the exit animation - scale up and fade out
45+
splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
46+
val splashScreenView = splashScreenViewProvider.view
47+
val splashIcon = splashScreenViewProvider.iconView
48+
49+
// Scale down animation
50+
val scaleUp = ObjectAnimator.ofFloat(splashIcon, "scaleX", 1f, 0.5f).apply {
51+
interpolator = AnticipateInterpolator()
52+
duration = 750
53+
}
54+
55+
val scaleUpY = ObjectAnimator.ofFloat(splashIcon, "scaleY", 1f, 0.5f).apply {
56+
interpolator = AnticipateInterpolator()
57+
duration = 750
58+
}
59+
60+
// rotate
61+
val rotate360 = ObjectAnimator.ofFloat(splashIcon, "rotation", 0f,-90f).apply {
62+
interpolator = AnticipateInterpolator()
63+
duration = 750
64+
}
65+
66+
// Fade out animation
67+
val fadeOut = ObjectAnimator.ofFloat(splashScreenView, "alpha", 1f, 0f).apply {
68+
interpolator = AnticipateInterpolator()
69+
duration = 750
70+
}
71+
fadeOut.doOnEnd {
72+
splashScreenViewProvider.remove()
73+
}
74+
75+
fadeOut.start()
76+
scaleUp.start()
77+
scaleUpY.start()
78+
rotate360.start()
79+
}
80+
3381
enableEdgeToEdge()
34-
// Initialize Shizuku
35-
ShizukuUtils.initialize()
82+
83+
Log.d("MainActivity", "onCreate with action: ${intent?.action}")
84+
85+
// Check if this is a QS tile long-press intent for the SoundModeTileService
86+
if (intent?.action == "android.service.quicksettings.action.QS_TILE_PREFERENCES") {
87+
val componentName = intent?.getParcelableExtra<android.content.ComponentName>("android.intent.extra.COMPONENT_NAME")
88+
Log.d("MainActivity", "QS_TILE_PREFERENCES received, component: ${componentName?.className}")
89+
if (componentName?.className == "com.sameerasw.essentials.services.SoundModeTileService") {
90+
Log.d("MainActivity", "Launching volume panel")
91+
// Launch the volume settings panel
92+
val volumeIntent = Intent("android.settings.panel.action.VOLUME")
93+
startActivity(volumeIntent)
94+
finish()
95+
return
96+
}
97+
}
98+
3699
// Initialize HapticUtil with saved preferences
37100
HapticUtil.initialize(this)
38101
// initialize permission registry
@@ -72,6 +135,11 @@ class MainActivity : ComponentActivity() {
72135
onSearchHandled = { searchRequested = false }
73136
)
74137
}
138+
139+
// Mark app as ready after composing (happens very quickly)
140+
LaunchedEffect(Unit) {
141+
isAppReady = true
142+
}
75143
}
76144
}
77145
}
@@ -80,4 +148,23 @@ class MainActivity : ComponentActivity() {
80148
super.onResume()
81149
viewModel.check(this)
82150
}
151+
152+
override fun onNewIntent(intent: Intent) {
153+
super.onNewIntent(intent)
154+
Log.d("MainActivity", "onNewIntent with action: ${intent.action}")
155+
156+
// Check if this is a QS tile long-press intent for the SoundModeTileService
157+
if (intent.action == "android.service.quicksettings.action.QS_TILE_PREFERENCES") {
158+
val componentName = intent.getParcelableExtra<android.content.ComponentName>("android.intent.extra.COMPONENT_NAME")
159+
Log.d("MainActivity", "QS_TILE_PREFERENCES received in onNewIntent, component: ${componentName?.className}")
160+
if (componentName?.className == "com.sameerasw.essentials.services.SoundModeTileService") {
161+
Log.d("MainActivity", "Launching volume panel from onNewIntent")
162+
// Launch the volume settings panel
163+
val volumeIntent = Intent("android.settings.panel.action.VOLUME")
164+
startActivity(volumeIntent)
165+
finish()
166+
return
167+
}
168+
}
169+
}
83170
}

app/src/main/java/com/sameerasw/essentials/PermissionRegistry.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,31 @@ object PermissionRegistry {
1313

1414
// Register existing dependencies
1515
fun initPermissionRegistry() {
16-
// Key for accessibility (use unique string)
16+
// Accessibility permission
1717
PermissionRegistry.register("ACCESSIBILITY", "Screen off widget")
18-
// Key for write secure settings
18+
PermissionRegistry.register("ACCESSIBILITY", "Edge lighting")
19+
20+
// Write secure settings permission
1921
PermissionRegistry.register("WRITE_SECURE_SETTINGS", "Statusbar icons")
2022
PermissionRegistry.register("WRITE_SECURE_SETTINGS", "Sound Mode")
21-
// Key for Shizuku (maps power saving)
23+
24+
// Shizuku permission
2225
PermissionRegistry.register("SHIZUKU", "Maps power saving mode")
23-
// Key for notification listener permission
26+
PermissionRegistry.register("SHIZUKU", "Automatic write secure settings")
27+
28+
// Notification listener permission
2429
PermissionRegistry.register("NOTIFICATION_LISTENER", "Maps power saving mode")
25-
// Key for draw over other apps permission (Edge lighting overlay)
30+
PermissionRegistry.register("NOTIFICATION_LISTENER", "Edge lighting")
31+
32+
// Draw over other apps permission
2633
PermissionRegistry.register("DRAW_OVER_OTHER_APPS", "Edge lighting")
27-
// add other registrations here if needed in future
34+
35+
// Post notifications permission
36+
PermissionRegistry.register("POST_NOTIFICATIONS", "Caffeinate show notification")
37+
38+
// Read phone state permission
39+
PermissionRegistry.register("READ_PHONE_STATE", "Smart data")
40+
41+
// Default browser permission
42+
PermissionRegistry.register("DEFAULT_BROWSER", "Link picker - open with")
2843
}

0 commit comments

Comments
 (0)