Skip to content

Commit

Permalink
Merge pull request #204 from terrakok/dev
Browse files Browse the repository at this point in the history
Huge refactoring of the authorization flow to make the app as single activity application.
  • Loading branch information
Hiebeler authored Feb 10, 2025
2 parents 3bb7063 + 2752848 commit 79d9d17
Show file tree
Hide file tree
Showing 55 changed files with 1,826 additions and 1,737 deletions.
64 changes: 33 additions & 31 deletions app/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,24 @@
android:theme="@style/Theme.Pixelix"
tools:ignore="Instantiatable"
tools:targetApi="31">


<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>

<activity
android:name=".MainActivity"
android:name=".AppActivity"
android:exported="true"
android:theme="@style/Theme.Pixelix"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down Expand Up @@ -51,7 +63,16 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />

<data
android:host="callback"
android:scheme="pixelix-android-auth" />
</intent-filter>
</activity>

<activity-alias
Expand All @@ -60,7 +81,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -73,7 +94,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_05"
android:roundIcon="@mipmap/ic_launcher_05_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -86,7 +107,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_06"
android:roundIcon="@mipmap/ic_launcher_06_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -99,7 +120,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_07"
android:roundIcon="@mipmap/ic_launcher_07_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -112,7 +133,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_08"
android:roundIcon="@mipmap/ic_launcher_08_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -125,7 +146,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_09"
android:roundIcon="@mipmap/ic_launcher_09_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -138,7 +159,7 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_01"
android:roundIcon="@mipmap/ic_launcher_01_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -151,32 +172,13 @@
android:enabled="false"
android:icon="@mipmap/ic_launcher_03"
android:roundIcon="@mipmap/ic_launcher_03_round"
android:targetActivity=".MainActivity">
android:targetActivity=".AppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

<activity
android:name=".LoginActivity"
android:exported="true"
android:screenOrientation="sensorPortrait"
android:theme="@style/Theme.Pixelix"
android:windowSoftInputMode="adjustResize"
tools:ignore="LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />

<data
android:host="callback"
android:scheme="pixelix-android-auth" />
</intent-filter>
</activity>

<receiver
android:name=".widget.notifications.NotificationWidgetReceiver"
android:label="Notifications"
Expand Down
157 changes: 157 additions & 0 deletions app/src/androidMain/kotlin/com/daniebeler/pfpixelix/AppActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.daniebeler.pfpixelix

import android.content.ContentResolver
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.window.DialogWindowProvider
import co.touchlab.kermit.Logger
import com.daniebeler.pfpixelix.utils.LocalKmpContext
import com.daniebeler.pfpixelix.utils.Navigate
import kotlinx.serialization.json.Json
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream

class AppActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
CompositionLocalProvider(
LocalKmpContext provides this
) {
App(MyApplication.appComponent) { finish() }
}
}
if (savedInstanceState == null) {
handleNewIntent(intent)
}
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleNewIntent(intent)
}

private fun handleNewIntent(intent: Intent) {
when (intent.action) {
Intent.ACTION_VIEW -> {
intent.dataString?.let { onExternalUrl(it) }
}
Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE -> {
val imageUris = handleSharePhotoIntent(intent, contentResolver, cacheDir)
if (imageUris.isNotEmpty()) {
imageUris.forEach { uri ->
try {
contentResolver.takePersistableUriPermission(
uri, Intent.FLAG_GRANT_READ_URI_PERMISSION
)
} catch (e: SecurityException) {
e.printStackTrace() // Handle permission denial gracefully
}
}
onExternalFileShare(imageUris)
}
}
}
}

private fun onExternalUrl(url: String) {
val systemUrlHandler = MyApplication.appComponent.systemUrlHandler
systemUrlHandler.onRedirect(url)
}

private fun onExternalFileShare(uris: List<Uri>) {
val systemFileShare = MyApplication.appComponent.systemFileShare
systemFileShare.share(uris)
}
}

@Composable
actual fun SetUpEdgeToEdgeDialog() {
val parentView = LocalView.current.parent as View
val window = (parentView as DialogWindowProvider).window

window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)

window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.attributes.fitInsetsTypes = 0
window.attributes.fitInsetsSides = 0
}
}

private fun saveUriToCache(uri: Uri, contentResolver: ContentResolver, cacheDir: File): Uri? {
try {
val inputStream: InputStream? = contentResolver.openInputStream(uri)
inputStream?.use { input ->
val file = File(cacheDir, "shared_image_${System.currentTimeMillis()}.jpg")
FileOutputStream(file).use { output ->
input.copyTo(output)
}
return Uri.fromFile(file) // Return the new cached URI
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}

private fun handleSharePhotoIntent(
intent: Intent, contentResolver: ContentResolver, cacheDir: File
): List<Uri> {
val action = intent.action
val type = intent.type
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

var imageUris: List<Uri> = emptyList()
when {
Intent.ACTION_SEND == action && type != null -> {
if (type.startsWith("image/") || type.startsWith("video/")) {
val singleUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(
Intent.EXTRA_STREAM, Uri::class.java
)
} else {
@Suppress("DEPRECATION") intent.getParcelableExtra(
Intent.EXTRA_STREAM
) as? Uri
}
singleUri?.let { uri ->
val cachedUri = saveUriToCache(uri, contentResolver, cacheDir)
imageUris =
cachedUri?.let { listOf(it) } ?: emptyList() // Wrap single image in a list
}
}
}

Intent.ACTION_SEND_MULTIPLE == action && type != null -> {
val receivedUris = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableArrayListExtra(
Intent.EXTRA_STREAM, Uri::class.java
)
} else {
@Suppress("DEPRECATION") intent.getParcelableArrayListExtra(
Intent.EXTRA_STREAM
)
}
imageUris = receivedUris?.mapNotNull {
saveUriToCache(
it, contentResolver, cacheDir
)
} ?: emptyList()
}
}
return imageUris
}
Loading

0 comments on commit 79d9d17

Please sign in to comment.