Skip to content

Commit cba8cb5

Browse files
authored
Merge pull request #175 from sashalukin/main
Merge adaptive improvements and navigation 3
2 parents f2beff1 + 747e446 commit cba8cb5

54 files changed

Lines changed: 3933 additions & 941 deletions

Some content is hidden

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

app/build.gradle.kts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ plugins {
2626
alias(libs.plugins.secrets)
2727
alias(libs.plugins.download)
2828
alias(libs.plugins.google.gms.google.services)
29+
alias(libs.plugins.parcelize)
2930
}
3031

3132
kotlin {
@@ -38,7 +39,7 @@ secrets {
3839

3940
android {
4041
namespace = "com.google.android.samples.socialite"
41-
compileSdk = 35
42+
compileSdk = 36
4243

4344
defaultConfig {
4445
applicationId = "com.google.android.samples.socialite"
@@ -89,6 +90,10 @@ apply {
8990
}
9091

9192
dependencies {
93+
implementation(libs.adaptive)
94+
implementation(libs.adaptive.layout)
95+
implementation(libs.adaptive.navigation)
96+
9297
implementation(libs.core.ktx)
9398
implementation(libs.camera.extensions)
9499
implementation(libs.profileinstaller)
@@ -121,14 +126,13 @@ dependencies {
121126
implementation(libs.compose.ui.text.google.fonts)
122127
implementation(libs.compose.ui.tooling.preview)
123128
implementation(libs.compose.material3)
124-
implementation(libs.compose.material3.adaptive)
129+
implementation(libs.compose.material3.adaptive.navigation.suite)
125130
implementation(libs.compose.material.icons)
126131
androidTestImplementation(libs.compose.ui.test)
127132
debugImplementation(libs.compose.ui.tooling)
128133
debugImplementation(libs.compose.ui.test.manisfest)
129134

130135
implementation(libs.activity.compose)
131-
implementation(libs.navigation.compose)
132136
implementation(libs.kotlinx.serialization.json)
133137

134138
implementation(libs.accompanist.painter)
@@ -138,7 +142,6 @@ dependencies {
138142

139143
implementation(libs.lifecycle.ktx)
140144
implementation(libs.lifecycle.compose)
141-
implementation(libs.lifecycle.runtime.compose)
142145

143146
ksp(libs.room.compiler)
144147
implementation(libs.room.ktx)
@@ -162,6 +165,7 @@ dependencies {
162165
implementation(libs.media3.exoplayer)
163166
implementation(libs.media3.transformer)
164167
implementation(libs.media3.ui)
168+
implementation(libs.media3.ui.compose)
165169

166170
implementation(libs.hilt.android)
167171
implementation(libs.hilt.navigation.compose)
@@ -180,4 +184,9 @@ dependencies {
180184
implementation(platform(libs.firebase.bom))
181185
implementation(libs.firebase.ai)
182186
implementation(libs.datastore)
187+
188+
implementation(libs.adaptive.navigation3)
189+
implementation(libs.navigation3.runtime)
190+
implementation(libs.navigation3.ui)
191+
implementation(libs.lifecycle.viewmodel.navigation3)
183192
}

app/src/main/AndroidManifest.xml

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<!--
1+
<?xml version="1.0" encoding="utf-8"?><!--
32
~ Copyright (C) 2023 The Android Open Source Project
43
~
54
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,12 +40,12 @@
4140
android:name=".SocialApp"
4241
android:allowBackup="true"
4342
android:dataExtractionRules="@xml/data_extraction_rules"
43+
android:enableOnBackInvokedCallback="true"
4444
android:fullBackupContent="@xml/backup_rules"
4545
android:icon="@mipmap/ic_launcher"
4646
android:label="@string/app_name"
4747
android:roundIcon="@mipmap/ic_launcher_round"
4848
android:supportsRtl="true"
49-
android:enableOnBackInvokedCallback="true"
5049
android:theme="@style/Theme.Social"
5150
tools:targetApi="34">
5251

@@ -68,11 +67,11 @@
6867

6968
<activity
7069
android:name=".MainActivity"
70+
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
7171
android:exported="true"
72+
android:supportsPictureInPicture="true"
7273
android:theme="@style/Theme.Social.SplashScreen"
73-
android:windowSoftInputMode="adjustResize"
74-
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize"
75-
android:supportsPictureInPicture="true">
74+
android:windowSoftInputMode="adjustResize">
7675

7776

7877
<!-- This activity is the one that's shown in the launcher. -->
@@ -110,6 +109,10 @@
110109
<meta-data
111110
android:name="android.app.shortcuts"
112111
android:resource="@xml/sharing_shortcuts" />
112+
113+
<property
114+
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
115+
android:value="true" />
113116
</activity>
114117
<!--
115118
This Activity is the expanded Bubble. For that, this Activity has to have several
@@ -139,20 +142,38 @@
139142
</activity>
140143

141144

142-
<activity android:name=".widget.SociaLiteAppWidgetConfigActivity"
143-
android:taskAffinity=""
144-
android:exported="true">
145+
<activity
146+
android:name=".widget.SociaLiteAppWidgetConfigActivity"
147+
android:exported="true"
148+
android:taskAffinity="">
145149
<intent-filter>
146-
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
150+
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
147151
</intent-filter>
148152
</activity>
149153

150154

151155
<provider
152156
android:name="com.google.android.samples.socialite.AssetFileProvider"
153157
android:authorities="com.google.android.samples.socialite"
154-
android:exported="true"
155-
tools:ignore="ExportedContentProvider" />
158+
android:exported="false"
159+
android:grantUriPermissions="true"
160+
tools:ignore="ExportedContentProvider">
161+
<grant-uri-permission android:pathPattern="/photo/.*" />
162+
<grant-uri-permission android:pathPattern="/video/.*" />
163+
<grant-uri-permission android:pathPattern="/icon/.*" />
164+
</provider>
165+
166+
<provider
167+
android:name="androidx.core.content.FileProvider"
168+
android:authorities="com.google.android.samples.socialite.file_provider"
169+
android:exported="false"
170+
android:grantUriPermissions="true">
171+
172+
<meta-data
173+
android:name="android.support.FILE_PROVIDER_PATHS"
174+
android:resource="@xml/shared_paths" />
175+
176+
</provider>
156177

157178
<receiver android:name="com.google.android.samples.socialite.ReplyReceiver" />
158179

@@ -164,11 +185,11 @@
164185
android:exported="false"
165186
tools:ignore="MissingClass">
166187
<intent-filter>
167-
<action
168-
android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
188+
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
169189
</intent-filter>
170190
<meta-data
171-
android:name="photopicker_activity:0:required" android:value="" />
191+
android:name="photopicker_activity:0:required"
192+
android:value="" />
172193
</service>
173194

174195
</application>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.samples.socialite
18+
19+
import android.app.Activity
20+
import android.content.Intent
21+
import android.os.Build
22+
import androidx.core.content.pm.ShortcutManagerCompat
23+
import com.google.android.samples.socialite.model.extractChatId
24+
import com.google.android.samples.socialite.ui.navigation.Pane
25+
26+
sealed interface AppArgs {
27+
fun toPane(): Pane
28+
29+
data class ShortcutParams(val shortcutId: String, val text: String) : AppArgs {
30+
override fun toPane(): Pane {
31+
val chatId = extractChatId(shortcutId)
32+
return Pane.ChatThread(chatId, text)
33+
}
34+
35+
companion object {
36+
fun tryFrom(intent: Intent): ShortcutParams? {
37+
if (intent.action != Intent.ACTION_SEND) return null
38+
39+
val shortcutId = intent.getStringExtra(
40+
ShortcutManagerCompat.EXTRA_SHORTCUT_ID,
41+
)
42+
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
43+
44+
return if (shortcutId != null && text != null) {
45+
ShortcutParams(shortcutId, text)
46+
} else {
47+
null
48+
}
49+
}
50+
}
51+
}
52+
data class LaunchParams(val chatId: Long) : AppArgs {
53+
override fun toPane() = Pane.ChatThread(chatId, null)
54+
55+
companion object {
56+
const val CHAT_ID_KEY = "chatId"
57+
const val INTENT_KEY = "AppArgs.LaunchParams"
58+
const val REQUEST_CODE = 54321
59+
60+
fun tryFrom(intent: Intent): LaunchParams? {
61+
val chatId = intent.getLongExtra(CHAT_ID_KEY, -1)
62+
return if (chatId != -1L) {
63+
LaunchParams(chatId)
64+
} else {
65+
null
66+
}
67+
}
68+
}
69+
}
70+
}
71+
72+
internal fun Activity.tryCreateIntentFrom(params: AppArgs.LaunchParams): Intent? {
73+
return Intent(Intent.ACTION_VIEW).apply {
74+
component = componentName
75+
putExtra(AppArgs.LaunchParams.CHAT_ID_KEY, params.chatId)
76+
flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
77+
Intent.FLAG_ACTIVITY_NEW_TASK or
78+
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
79+
Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
80+
} else {
81+
Intent.FLAG_ACTIVITY_NEW_TASK or
82+
Intent.FLAG_ACTIVITY_MULTIPLE_TASK
83+
}
84+
}
85+
}

app/src/main/java/com/google/android/samples/socialite/AssetFileProvider.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import android.content.ContentProvider
1919
import android.content.ContentValues
2020
import android.content.res.AssetFileDescriptor
2121
import android.database.Cursor
22+
import android.database.MatrixCursor
2223
import android.net.Uri
24+
import android.provider.OpenableColumns
2325
import android.webkit.MimeTypeMap
2426
import com.google.android.samples.socialite.model.Contact
2527

@@ -29,12 +31,14 @@ class AssetFileProvider : ContentProvider() {
2931
return true
3032
}
3133

32-
override fun getType(uri: Uri): String? {
34+
override fun getType(uri: Uri): String {
3335
val segments = uri.pathSegments
34-
return when (segments[0]) {
36+
val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
37+
val mimeType = when (segments[0]) {
3538
"icon" -> MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg")
36-
else -> "application/octet-stream"
37-
}
39+
else -> MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
40+
} ?: "application/octet-stream"
41+
return mimeType
3842
}
3943

4044
override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? {
@@ -46,10 +50,12 @@ class AssetFileProvider : ContentProvider() {
4650
context?.resources?.assets?.openFd(contact.icon)
4751
}
4852
}
53+
4954
"photo", "video" -> {
5055
val filename = segments[1]
5156
context?.resources?.assets?.openFd(filename)
5257
}
58+
5359
else -> null
5460
}
5561
}
@@ -61,7 +67,20 @@ class AssetFileProvider : ContentProvider() {
6167
selectionArgs: Array<out String>?,
6268
sortOrder: String?,
6369
): Cursor? {
64-
throw UnsupportedOperationException("No query")
70+
val segments = uri.pathSegments
71+
return when (segments[0]) {
72+
"icon", "video", "photo" -> {
73+
val columns = arrayOf(
74+
OpenableColumns.DISPLAY_NAME,
75+
)
76+
77+
return MatrixCursor(columns).apply {
78+
addRow(arrayOf(segments.last()))
79+
}
80+
}
81+
82+
else -> null
83+
}
6584
}
6685

6786
override fun insert(uri: Uri, values: ContentValues?): Uri? {

0 commit comments

Comments
 (0)