diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000000..a39361ea57a
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
index e1477d14631..0e114fd4c13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -338,7 +338,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.preference:preference-ktx:1.2.1"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.2'
implementation 'com.github.ChickenHook:RestrictionBypass:2.2'
implementation 'dev.rikka.tools.refine:runtime:4.4.0'
diff --git a/lawnchair/res/drawable/ic_firefox.xml b/lawnchair/res/drawable/ic_firefox.xml
new file mode 100644
index 00000000000..247d797bcfe
--- /dev/null
+++ b/lawnchair/res/drawable/ic_firefox.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/lawnchair/res/drawable/ic_firefox_tinted.xml b/lawnchair/res/drawable/ic_firefox_tinted.xml
new file mode 100644
index 00000000000..4beb0fdbdac
--- /dev/null
+++ b/lawnchair/res/drawable/ic_firefox_tinted.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/lawnchair/res/drawable/ic_iceraven.xml b/lawnchair/res/drawable/ic_iceraven.xml
new file mode 100644
index 00000000000..7308766f81f
--- /dev/null
+++ b/lawnchair/res/drawable/ic_iceraven.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/lawnchair/res/drawable/ic_iceraven_tinted.xml b/lawnchair/res/drawable/ic_iceraven_tinted.xml
new file mode 100644
index 00000000000..72268352ec1
--- /dev/null
+++ b/lawnchair/res/drawable/ic_iceraven_tinted.xml
@@ -0,0 +1,14 @@
+
+
+
+
\ No newline at end of file
diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml
index a7e27c999eb..a0669d6a6bc 100644
--- a/lawnchair/res/values/strings.xml
+++ b/lawnchair/res/values/strings.xml
@@ -566,6 +566,9 @@
YouTube
Pixel Search
Yandex
+ Firefox
+ Iceraven
+ Mull
%1$s and Lawnchair have a revenue share agreement.\n\nSearching with %1$s helps support Lawnchair.
diff --git a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
index 91c6de9a21c..6f4ea68e133 100644
--- a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
+++ b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt
@@ -334,6 +334,7 @@ class LawnchairLauncher : QuickstepLauncher() {
override fun onDestroy() {
super.onDestroy()
+ // Only actually closes if required, safe to call if not enabled
SmartspacerClient.close()
}
diff --git a/lawnchair/src/app/lawnchair/qsb/providers/Firefox.kt b/lawnchair/src/app/lawnchair/qsb/providers/Firefox.kt
new file mode 100644
index 00000000000..99c610793ad
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/qsb/providers/Firefox.kt
@@ -0,0 +1,25 @@
+package app.lawnchair.qsb.providers
+
+import android.content.Intent
+import app.lawnchair.qsb.ThemingMethod
+import com.android.launcher3.R
+
+data object Firefox : QsbSearchProvider(
+ id = "Firefox",
+ name = R.string.search_provider_firefox,
+ icon = R.drawable.ic_firefox,
+ themedIcon = R.drawable.ic_firefox_tinted,
+ themingMethod = ThemingMethod.TINT,
+ packageName = "org.mozilla.firefox",
+ action = "org.mozilla.fenix.OPEN_TAB",
+ className = "org.mozilla.fenix.IntentReceiverActivity",
+ website = "https://play.google.com/store/apps/details?id=org.mozilla.firefox",
+ type = QsbSearchProviderType.APP,
+ supportVoiceIntent = false,
+) {
+
+ override fun handleCreateVoiceIntent(): Intent =
+ Intent(action)
+ .addFlags(INTENT_FLAGS)
+ .setClassName(packageName, "org.chromium.chrome.browser.VoiceSearchActivity")
+}
diff --git a/lawnchair/src/app/lawnchair/qsb/providers/Iceraven.kt b/lawnchair/src/app/lawnchair/qsb/providers/Iceraven.kt
new file mode 100644
index 00000000000..c51b0e00b43
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/qsb/providers/Iceraven.kt
@@ -0,0 +1,25 @@
+package app.lawnchair.qsb.providers
+
+import android.content.Intent
+import app.lawnchair.qsb.ThemingMethod
+import com.android.launcher3.R
+
+data object Iceraven : QsbSearchProvider(
+ id = "Iceraven",
+ name = R.string.search_provider_iceraven,
+ icon = R.drawable.ic_iceraven,
+ themedIcon = R.drawable.ic_iceraven_tinted,
+ themingMethod = ThemingMethod.TINT,
+ packageName = "io.github.forkmaintainers.iceraven",
+ action = "org.mozilla.fenix.OPEN_TAB",
+ className = "org.mozilla.fenix.IntentReceiverActivity",
+ website = "github.com/fork-maintainers/iceraven-browser/releases/latest",
+ type = QsbSearchProviderType.APP,
+ supportVoiceIntent = true,
+) {
+
+ override fun handleCreateVoiceIntent(): Intent =
+ Intent(action)
+ .addFlags(INTENT_FLAGS)
+ .setClassName(packageName, "org.chromium.chrome.browser.VoiceSearchActivity")
+}
diff --git a/lawnchair/src/app/lawnchair/qsb/providers/Mull.kt b/lawnchair/src/app/lawnchair/qsb/providers/Mull.kt
new file mode 100644
index 00000000000..7842b101d93
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/qsb/providers/Mull.kt
@@ -0,0 +1,25 @@
+package app.lawnchair.qsb.providers
+
+import android.content.Intent
+import app.lawnchair.qsb.ThemingMethod
+import com.android.launcher3.R
+
+data object Mull : QsbSearchProvider(
+ id = "Mull",
+ name = R.string.search_provider_mull,
+ icon = R.drawable.ic_mull,
+ themedIcon = R.drawable.ic_mull_tinted,
+ themingMethod = ThemingMethod.TINT,
+ packageName = "us.spotco.fennec_dos",
+ action = "org.mozilla.fenix.OPEN_TAB",
+ className = "org.mozilla.fenix.IntentReceiverActivity",
+ website = "gitlab.com/divested-mobile/mull-fenix",
+ type = QsbSearchProviderType.APP,
+ supportVoiceIntent = true,
+) {
+
+ override fun handleCreateVoiceIntent(): Intent =
+ Intent(action)
+ .addFlags(INTENT_FLAGS)
+ .setClassName(packageName, "org.chromium.chrome.browser.VoiceSearchActivity")
+}
diff --git a/lawnchair/src/app/lawnchair/qsb/providers/QsbSearchProvider.kt b/lawnchair/src/app/lawnchair/qsb/providers/QsbSearchProvider.kt
index 585b8f5f911..1409194c9cb 100644
--- a/lawnchair/src/app/lawnchair/qsb/providers/QsbSearchProvider.kt
+++ b/lawnchair/src/app/lawnchair/qsb/providers/QsbSearchProvider.kt
@@ -133,7 +133,10 @@ sealed class QsbSearchProvider(
Bing,
Brave,
Yandex,
+ Firefox,
+ Iceraven,
Startpage,
+ Mull,
)
/**
diff --git a/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt b/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
index 0b270645963..b9a769dde07 100644
--- a/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
+++ b/lawnchair/src/app/lawnchair/search/adapter/SearchTargetFactory.kt
@@ -37,6 +37,7 @@ import java.io.IOException
import java.io.InputStream
import java.security.MessageDigest
import java.util.Locale
+import java.util.UUID
import okio.ByteString
class SearchTargetFactory(
@@ -93,7 +94,8 @@ class SearchTargetFactory(
): SearchTargetCompat {
val result = calculation.result
val equation = calculation.equation
- val id = "calculator:$result"
+ val uuid = UUID.randomUUID().toString()
+ val id = "calculator:$uuid"
val action = SearchActionCompat.Builder(id, result)
.setIcon(
Icon.createWithResource(context, R.drawable.calculator)
diff --git a/lawnchair/src/app/lawnchair/search/algorithms/data/Calculation.kt b/lawnchair/src/app/lawnchair/search/algorithms/data/Calculation.kt
index dcd21f8f084..e05585b32f0 100644
--- a/lawnchair/src/app/lawnchair/search/algorithms/data/Calculation.kt
+++ b/lawnchair/src/app/lawnchair/search/algorithms/data/Calculation.kt
@@ -1,6 +1,8 @@
package app.lawnchair.search.algorithms.data
import app.lawnchair.search.algorithms.data.calculator.Expressions
+import java.math.BigDecimal
+import java.math.MathContext
data class Calculation(
val equation: String,
@@ -12,9 +14,17 @@ fun calculateEquationFromString(
query: String,
): Calculation {
return try {
- val result = Expressions()
- .eval(query)
- .toString()
+ val evaluatedValue = Expressions().eval(query)
+ val roundedValue = evaluatedValue.round(MathContext.DECIMAL64)
+ val formattedValue = roundedValue.stripTrailingZeros()
+ val absoluteValue = formattedValue.abs()
+ val threshold = BigDecimal("9999999999999999")
+
+ val result = if (absoluteValue > threshold) {
+ formattedValue.toString()
+ } else {
+ formattedValue.toPlainString()
+ }
Calculation(
equation = query,
diff --git a/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/Expressions.kt b/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/Expressions.kt
index 4f258bb73f1..d4e89ea4f7a 100644
--- a/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/Expressions.kt
+++ b/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/Expressions.kt
@@ -18,8 +18,8 @@ class Expressions {
private val evaluator = Evaluator()
init {
- define("pi", Math.PI)
- define("e", Math.E)
+ define("pi", BigDecimal(Math.PI).setScale(33, RoundingMode.HALF_EVEN))
+ define("e", BigDecimal(Math.E).setScale(33, RoundingMode.HALF_EVEN))
evaluator.addFunction("abs") { arguments ->
if (arguments.size != 1) {
diff --git a/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/internal/Evaluator.kt b/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/internal/Evaluator.kt
index 9eb51e91917..8e9ff478ae6 100644
--- a/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/internal/Evaluator.kt
+++ b/lawnchair/src/app/lawnchair/search/algorithms/data/calculator/internal/Evaluator.kt
@@ -23,7 +23,7 @@ import java.util.Locale
import kotlin.math.pow
internal class Evaluator : ExprVisitor {
- internal var mathContext: MathContext = MathContext.DECIMAL64
+ internal var mathContext: MathContext = MathContext.DECIMAL128
private val variables: LinkedHashMap = linkedMapOf()
private val functions: MutableMap = mutableMapOf()
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SliderPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SliderPreference.kt
index 0b2ae921576..030b0014152 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SliderPreference.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SliderPreference.kt
@@ -138,10 +138,11 @@ fun SliderPreference(
fun getSteps(valueRange: ClosedFloatingPointRange, step: Float): Int {
if (step == 0f) return 0
- val start = valueRange.start
- val end = valueRange.endInclusive
- val steps = ((end - start) / step).toInt()
- require(start + step * steps == end) {
+ val start = valueRange.start.toBigDecimal()
+ val end = valueRange.endInclusive.toBigDecimal()
+ val decimalSteps = (end - start) / step.toBigDecimal()
+ val steps = decimalSteps.toInt()
+ require(decimalSteps.compareTo(steps.toBigDecimal()) == 0) {
"value range must be a multiple of step"
}
return steps - 1
diff --git a/res/drawable/ic_mull.xml b/res/drawable/ic_mull.xml
new file mode 100644
index 00000000000..079ee421235
--- /dev/null
+++ b/res/drawable/ic_mull.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_mull_tinted.xml b/res/drawable/ic_mull_tinted.xml
new file mode 100644
index 00000000000..44768626f3e
--- /dev/null
+++ b/res/drawable/ic_mull_tinted.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+