Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/upstream' into trunk
Browse files Browse the repository at this point in the history
  • Loading branch information
Goooler committed Mar 16, 2024
2 parents e5a4d84 + e0727ec commit fc07992
Show file tree
Hide file tree
Showing 18 changed files with 377 additions and 33 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ allprojects {
}

plugins.withId('com.google.protobuf') {
def protocVersion = '3.25.3'
def protocVersion = '4.26.0'
protobuf {
// Configure the protoc executable
protoc {
Expand Down Expand Up @@ -342,6 +342,7 @@ dependencies {
implementation "androidx.compose.ui:ui"
implementation "androidx.compose.ui:ui-util"
debugImplementation "androidx.compose.ui:ui-tooling"
implementation "androidx.compose.ui:ui-tooling-preview"
implementation "androidx.compose.ui:ui-text-google-fonts"
implementation "androidx.compose.foundation:foundation"
implementation "androidx.compose.material:material-icons-extended"
Expand Down
2 changes: 2 additions & 0 deletions lawnchair/res/values/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
<bool name="config_default_smartspace_show_date">true</bool>
<bool name="config_default_smartspace_show_time">false</bool>
<bool name="config_default_perform_wide_search">true</bool>
<bool name="config_default_live_information_enabled">true</bool>
<bool name="config_default_live_information_show_announcements">true</bool>
<bool name="config_default_enable_dot_pagination">true</bool>
<bool name="config_default_enable_material_u_popup">true</bool>

Expand Down
7 changes: 0 additions & 7 deletions lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,6 @@ class AllAppsSearchInput(context: Context, attrs: AttributeSet?) :
enableDebugMenu.set(!enableDebugMenu.get())
launcher.stateManager.goToState(LauncherState.NORMAL)
}
// Make sure to empty
// if user used backspace instead of clear action btn
if (input.text.isEmpty() || input.text.isBlank()) {
input.reset()
resetSearch()
clearSearchResult()
}
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.view.descendants
import app.lawnchair.font.FontManager
import app.lawnchair.util.recursiveChildren
import com.android.launcher3.R
import com.android.launcher3.icons.ShadowGenerator
import com.android.launcher3.testing.shared.ResourceUtils
Expand All @@ -38,7 +38,7 @@ class ThemedSmartSpaceHostView(context: Context) : SmartSpaceHostView(context) {

private fun overrideStyles(parent: ViewGroup) {
val images = mutableListOf<ImageView>()
for (child in parent.recursiveChildren) {
for (child in parent.descendants) {
when (child) {
is TextView -> overrideTextView(child)
is ImageView -> images.add(child)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import android.widget.Space
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import app.lawnchair.launcherNullable
import app.lawnchair.preferences.PreferenceManager
import app.lawnchair.util.RecentHelper
import app.lawnchair.util.isDefaultLauncher
Expand All @@ -29,7 +28,7 @@ class LawnchairOverviewActionsView @JvmOverloads constructor(
) : OverviewActionsView<TaskOverlayFactoryImpl.OverlayUICallbacks>(context, attrs, defStyleAttr) {

private val prefs = PreferenceManager.getInstance(context)
private val launcher: Launcher? = context.launcherNullable
private val launcher: Launcher? = if (context.isDefaultLauncher()) Launcher.getLauncher(context) else null
private lateinit var container: LinearLayout
private lateinit var screenshotAction: Button
private lateinit var shareAction: Button
Expand Down
4 changes: 2 additions & 2 deletions lawnchair/src/app/lawnchair/qsb/LawnQsbLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import android.widget.FrameLayout
import android.widget.ImageView
import androidx.core.view.ViewCompat
import androidx.core.view.children
import androidx.core.view.descendants
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import app.lawnchair.HeadlessWidgetsManager
Expand All @@ -26,7 +27,6 @@ import app.lawnchair.qsb.providers.GoogleGo
import app.lawnchair.qsb.providers.PixelSearch
import app.lawnchair.qsb.providers.QsbSearchProvider
import app.lawnchair.util.pendingIntent
import app.lawnchair.util.recursiveChildren
import app.lawnchair.util.repeatOnAttached
import app.lawnchair.util.viewAttachedScope
import com.android.launcher3.BaseActivity
Expand Down Expand Up @@ -144,7 +144,7 @@ class LawnQsbLayout(context: Context, attrs: AttributeSet?) : FrameLayout(contex
MeasureSpec.makeMeasureSpec(1000, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
)
searchPendingIntent = view.recursiveChildren
searchPendingIntent = view.descendants
.filter { it.pendingIntent != null }
.sortedByDescending { it.measuredWidth * it.measuredHeight }
.firstOrNull()
Expand Down
4 changes: 2 additions & 2 deletions lawnchair/src/app/lawnchair/qsb/providers/Google.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import android.appwidget.AppWidgetHostView
import android.content.Context
import android.content.Intent
import android.view.View
import androidx.core.view.descendants
import app.lawnchair.HeadlessWidgetsManager
import app.lawnchair.qsb.ThemingMethod
import app.lawnchair.util.pendingIntent
import app.lawnchair.util.recursiveChildren
import com.android.launcher3.Launcher
import com.android.launcher3.R
import com.android.launcher3.qsb.QsbContainerView
Expand Down Expand Up @@ -57,7 +57,7 @@ data object Google : QsbSearchProvider(
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY),
)
return view.recursiveChildren
return view.descendants
.filter { it.pendingIntent != null }
.sortedByDescending { it.measuredWidth * it.measuredHeight }
.firstOrNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.descendants
import app.lawnchair.BlankActivity
import app.lawnchair.HeadlessWidgetsManager
import app.lawnchair.smartspace.model.SmartspaceAction
import app.lawnchair.smartspace.model.SmartspaceScores
import app.lawnchair.smartspace.model.SmartspaceTarget
import app.lawnchair.util.Temperature
import app.lawnchair.util.pendingIntent
import app.lawnchair.util.recursiveChildren
import com.android.launcher3.R
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
Expand Down Expand Up @@ -57,9 +57,9 @@ class SmartspaceWidgetReader(context: Context) : SmartspaceDataSource(
}

private fun extractWidgetLayout(appWidgetHostView: ViewGroup): List<SmartspaceTarget> {
val children = appWidgetHostView.recursiveChildren
val texts = children.filterIsInstance<TextView>().filter { !it.text.isNullOrEmpty() }.toList()
val images = children.filterIsInstance<ImageView>().toList().filter { it.drawable != null && it.drawable is BitmapDrawable }
val descendants = appWidgetHostView.descendants
val texts = descendants.filterIsInstance<TextView>().filter { !it.text.isNullOrEmpty() }.toList()
val images = descendants.filterIsInstance<ImageView>().toList().filter { it.drawable != null && it.drawable is BitmapDrawable }
var weatherIconView: ImageView? = null
var cardIconView: ImageView? = null
var title: TextView? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package app.lawnchair.ui.preferences.components

import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Launch
import androidx.compose.material.icons.rounded.NewReleases
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import app.lawnchair.preferences2.asState
import app.lawnchair.ui.preferences.components.layout.ExpandAndShrink
import app.lawnchair.ui.preferences.components.layout.PreferenceTemplate
import app.lawnchair.ui.preferences.data.liveinfo.liveInformationManager
import app.lawnchair.ui.preferences.data.liveinfo.model.Announcement
import app.lawnchair.ui.util.addIf
import com.android.launcher3.BuildConfig
import kotlinx.collections.immutable.ImmutableList

@Composable
fun AnnouncementPreference() {
val liveInformationManager = liveInformationManager()

val enabled by liveInformationManager.enabled.asState()
val showAnnouncements by liveInformationManager.showAnnouncements.asState()
val liveInformation by liveInformationManager.liveInformation.asState()

if (enabled && showAnnouncements) {
AnnouncementPreference(
announcements = liveInformation.announcementsImmutable,
)
}
}

@Composable
fun AnnouncementPreference(
announcements: ImmutableList<Announcement>,
) {
Column {
announcements.forEach { announcement ->
AnnouncementItem(announcement)
Spacer(modifier = Modifier.height(16.dp))
}
}
}

@Composable
private fun AnnouncementItem(
announcement: Announcement,
) {
var show by remember { mutableStateOf(true) }

ExpandAndShrink(
visible = show && announcement.active &&
announcement.text.isNotBlank() &&
(!announcement.test || BuildConfig.DEBUG),
) {
AnnouncementItemContent(
text = announcement.text,
url = announcement.url,
onClose = { show = false },
)
}
}

@Composable
private fun AnnouncementItemContent(
text: String,
url: String?,
onClose: () -> Unit,
) {
Surface(
modifier = Modifier
.padding(16.dp, 0.dp, 16.dp, 0.dp),
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.surfaceVariant,
) {
AnnouncementPreferenceItemContent(text = text, url = url, onClose = onClose)
}
}

@Composable
private fun AnnouncementPreferenceItemContent(
text: String,
url: String?,
onClose: (() -> Unit)?,
) {
val context = LocalContext.current
val hasLink = !url.isNullOrBlank()

PreferenceTemplate(
modifier = Modifier
.fillMaxWidth()
.addIf(hasLink) {
clickable {
val webpage = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, webpage)
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
}
}
},
title = {},
description = {
Text(
modifier = Modifier.fillMaxWidth(),
text = text,
color = MaterialTheme.colorScheme.primary,
textAlign = TextAlign.Center,
)
},
startWidget = {
Icon(
imageVector = Icons.Rounded.NewReleases,
tint = MaterialTheme.colorScheme.primary,
contentDescription = null,
)
},
endWidget = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
) {
if (hasLink) {
Icon(
imageVector = Icons.Rounded.Launch,
tint = MaterialTheme.colorScheme.primary,
contentDescription = null,
)
}

Spacer(modifier = Modifier.width(8.dp))

if (onClose != null) {
IconButton(
onClick = onClose,
modifier = Modifier.size(16.dp).offset(x = (8).dp, y = (-16).dp),
) {
Icon(
imageVector = Icons.Rounded.Close,
tint = MaterialTheme.colorScheme.surfaceTint,
contentDescription = null,
)
}
}
}
},
)
}

@Preview
@Composable
private fun InfoPreferenceWithoutLinkPreview() {
AnnouncementPreferenceItemContent(
text = "Very important announcement ",
url = "",
onClose = null,
)
}

@Preview
@Composable
private fun InfoPreferenceWithLinkPreview() {
AnnouncementPreferenceItemContent(
text = "Very important announcement with a very important link",
url = "https://lawnchair.app/",
onClose = null,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package app.lawnchair.ui.preferences.data.liveinfo

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import app.lawnchair.ui.preferences.data.liveinfo.model.LiveInformation
import com.android.launcher3.R
import com.android.launcher3.util.MainThreadInitializedObject
import com.patrykmichalik.opto.core.PreferenceManager
import kotlinx.serialization.json.Json

class LiveInformationManager private constructor(context: Context) : PreferenceManager {

companion object {
private val Context.preferencesDataStore by preferencesDataStore(
name = "live-information",
)

@JvmField
val INSTANCE = MainThreadInitializedObject(::LiveInformationManager)

@JvmStatic
fun getInstance(context: Context) = INSTANCE.get(context)!!
}

override val preferencesDataStore = context.preferencesDataStore

val enabled = preference(
key = booleanPreferencesKey(name = "enabled"),
defaultValue = context.resources.getBoolean(R.bool.config_default_live_information_enabled),
)

val showAnnouncements = preference(
key = booleanPreferencesKey(name = "show_announcements"),
defaultValue = context.resources.getBoolean(R.bool.config_default_live_information_show_announcements),
)

val liveInformation = preference(
key = stringPreferencesKey(name = "live_information"),
defaultValue = LiveInformation.default,
parse = { Json.decodeFromString<LiveInformation>(it) },
save = { Json.encodeToString(LiveInformation.serializer(), it) },
)
}

@Composable
fun liveInformationManager() = LiveInformationManager.getInstance(LocalContext.current)
Loading

0 comments on commit fc07992

Please sign in to comment.