Skip to content

Commit 4f416c6

Browse files
authored
Remove blue line and add shadow to Input Field (#6408)
Task/Issue URL: https://app.asana.com/1/137249556945/project/488551667048375/task/1210707531805574?focus=true ### Description - Removes the blue outline and adds a shadow (matching the browser omnibar) to the Input Field. ### Steps to test this PR - [x] Tap the browser omnibar - [x] Verify that the Input Field has a shadow and no blue line
1 parent 138307e commit 4f416c6

File tree

7 files changed

+77
-164
lines changed

7 files changed

+77
-164
lines changed

app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import com.duckduckgo.app.browser.databinding.IncludeOmnibarToolbarMockupBinding
5858
import com.duckduckgo.app.browser.databinding.IncludeSingleOmnibarToolbarMockupBinding
5959
import com.duckduckgo.app.browser.defaultbrowsing.prompts.ui.DefaultBrowserBottomSheetDialog
6060
import com.duckduckgo.app.browser.defaultbrowsing.prompts.ui.DefaultBrowserBottomSheetDialog.EventListener
61-
import com.duckduckgo.app.browser.omnibar.extensions.addBottomShadow
6261
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.BOTTOM
6362
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.TOP
6463
import com.duckduckgo.app.browser.omnibar.model.OmnibarType
@@ -99,6 +98,7 @@ import com.duckduckgo.browser.api.ui.BrowserScreens.BookmarksScreenNoParams
9998
import com.duckduckgo.browser.api.ui.BrowserScreens.SettingsScreenNoParams
10099
import com.duckduckgo.common.ui.DuckDuckGoActivity
101100
import com.duckduckgo.common.ui.tabs.SwipingTabsFeatureProvider
101+
import com.duckduckgo.common.ui.view.addBottomShadow
102102
import com.duckduckgo.common.ui.view.dialog.TextAlertDialogBuilder
103103
import com.duckduckgo.common.ui.view.gone
104104
import com.duckduckgo.common.ui.view.isFullScreen

app/src/main/java/com/duckduckgo/app/browser/omnibar/experiments/SingleOmnibarLayout.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import com.duckduckgo.app.browser.omnibar.Omnibar.ViewMode
3737
import com.duckduckgo.app.browser.omnibar.OmnibarItemPressedListener
3838
import com.duckduckgo.app.browser.omnibar.OmnibarLayout
3939
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.ViewState
40-
import com.duckduckgo.app.browser.omnibar.extensions.addBottomShadow
4140
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition
41+
import com.duckduckgo.common.ui.view.addBottomShadow
4242
import com.duckduckgo.common.ui.view.gone
4343
import com.duckduckgo.common.ui.view.hide
4444
import com.duckduckgo.common.ui.view.show

app/src/main/java/com/duckduckgo/app/browser/omnibar/extensions/ViewExtensions.kt

Lines changed: 0 additions & 74 deletions
This file was deleted.

common/common-ui/src/main/java/com/duckduckgo/common/ui/view/ViewExtension.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ package com.duckduckgo.common.ui.view
1818

1919
import android.content.Context
2020
import android.content.res.Resources
21+
import android.graphics.Outline
2122
import android.view.View
2223
import android.view.ViewGroup
24+
import android.view.ViewOutlineProvider
2325
import android.view.inputmethod.InputMethodManager
2426
import android.widget.CompoundButton
27+
import androidx.annotation.ColorInt
28+
import androidx.annotation.RequiresApi
2529
import androidx.annotation.StringRes
30+
import androidx.core.content.ContextCompat
2631
import androidx.core.view.children
32+
import com.google.android.material.card.MaterialCardView
2733
import com.google.android.material.slider.Slider
2834
import com.google.android.material.snackbar.BaseTransientBottomBar.Duration
2935
import com.google.android.material.snackbar.Snackbar
@@ -247,3 +253,51 @@ private inline fun <reified T : ViewGroup.LayoutParams> updateLayoutParam(
247253
block(params)
248254
view.layoutParams = params
249255
}
256+
257+
/**
258+
* Adds a custom shadow below a view with specified size in dp and rounded corners.
259+
*
260+
* @param shadowSizeDp The size of the shadow in dp
261+
* @param offsetYDp Optional vertical offset in dp to position shadow below the view
262+
* @param insetDp Optional horizontal inset in dp to prevent shadow from being cut off
263+
* @param shadowColor Optional shadow color (Android P and above only)
264+
*/
265+
@RequiresApi(28)
266+
fun View.addBottomShadow(
267+
shadowSizeDp: Float,
268+
offsetYDp: Float = 2f,
269+
insetDp: Float = 0f,
270+
@ColorInt shadowColor: Int,
271+
) {
272+
val shadowSize = shadowSizeDp.toPx(context)
273+
val offsetY = offsetYDp.toPx(context)
274+
val inset = insetDp.toPx(context).toInt()
275+
276+
// Get corner radius if view is a card
277+
val cornerRadius = when (this) {
278+
is MaterialCardView -> this.radius
279+
else -> 0f
280+
}
281+
282+
outlineProvider = object : ViewOutlineProvider() {
283+
override fun getOutline(view: View, outline: Outline) {
284+
// Create outline with rounded corners that match the view
285+
outline.setRoundRect(
286+
-inset,
287+
0,
288+
view.width + inset,
289+
view.height,
290+
cornerRadius,
291+
)
292+
// Make the shadow appear only below the view
293+
outline.offset(0, offsetY.toInt())
294+
}
295+
}
296+
297+
// Set custom shadow color if specified (Android P and above)
298+
outlineSpotShadowColor = ContextCompat.getColor(context, android.R.color.transparent)
299+
outlineAmbientShadowColor = shadowColor
300+
301+
clipToOutline = false
302+
elevation = shadowSize
303+
}

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/InputScreenFragment.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ class InputScreenFragment : DuckDuckGoFragment(R.layout.fragment_input_screen) {
220220

221221
private fun exitInterstitial() {
222222
hideKeyboard(binding.inputModeWidget.inputField)
223-
binding.inputModeWidget.animateOmnibarFocusedState(false)
224223
requireActivity().supportFinishAfterTransition()
225224
}
226225

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/view/InputModeWidget.kt

Lines changed: 19 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package com.duckduckgo.duckchat.impl.inputscreen.ui.view
1818

19-
import android.animation.ValueAnimator
2019
import android.content.Context
20+
import android.os.Build
2121
import android.text.InputType
2222
import android.transition.ChangeBounds
2323
import android.transition.Fade
@@ -26,21 +26,15 @@ import android.util.AttributeSet
2626
import android.view.LayoutInflater
2727
import android.view.View
2828
import android.view.ViewGroup
29-
import android.view.animation.DecelerateInterpolator
3029
import android.view.inputmethod.EditorInfo
3130
import android.view.inputmethod.InputMethodManager
3231
import android.widget.EditText
3332
import androidx.annotation.IdRes
3433
import androidx.constraintlayout.widget.ConstraintLayout
35-
import androidx.core.animation.addListener
3634
import androidx.core.view.isVisible
37-
import androidx.core.view.marginBottom
38-
import androidx.core.view.marginEnd
39-
import androidx.core.view.marginStart
40-
import androidx.core.view.marginTop
41-
import androidx.core.view.updateLayoutParams
4235
import androidx.core.widget.doOnTextChanged
4336
import com.duckduckgo.anvil.annotations.InjectWith
37+
import com.duckduckgo.common.ui.view.addBottomShadow
4438
import com.duckduckgo.di.scopes.ActivityScope
4539
import com.duckduckgo.duckchat.impl.R
4640
import com.duckduckgo.mobile.android.R as CommonR
@@ -54,27 +48,11 @@ class InputModeWidget @JvmOverloads constructor(
5448
defStyle: Int = 0,
5549
) : ConstraintLayout(context, attrs, defStyle) {
5650

57-
private val omnibarCardMarginHorizontal by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarCardMarginHorizontal) }
58-
private val omnibarCardMarginTop by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarCardMarginTop) }
59-
private val omnibarCardMarginBottom by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarCardMarginBottom) }
60-
private val omnibarCardFocusedMarginHorizontal by lazy {
61-
resources.getDimensionPixelSize(
62-
CommonR.dimen.experimentalOmnibarCardFocusedMarginHorizontal,
63-
)
64-
}
65-
private val omnibarCardFocusedMarginTop by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarCardFocusedMarginTop) }
66-
private val omnibarCardFocusedMarginBottom by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarCardFocusedMarginBottom) }
67-
68-
private val omnibarOutlineWidth by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarOutlineWidth) }
69-
private val omnibarOutlineFocusedWidth by lazy { resources.getDimensionPixelSize(CommonR.dimen.experimentalOmnibarOutlineFocusedWidth) }
70-
71-
private val omnibarCard: MaterialCardView by lazy { findViewById(R.id.inputModeWidgetCard) }
72-
private val omnibarContent: View by lazy { findViewById(R.id.inputModeWidgetCardContent) }
73-
7451
val inputField: EditText
75-
val inputFieldClearText: View
76-
val inputModeWidgetBack: View
77-
val inputModeSwitch: TabLayout
52+
private val inputFieldClearText: View
53+
private val inputModeWidgetBack: View
54+
private val inputModeSwitch: TabLayout
55+
private val inputModeWidgetCard: MaterialCardView
7856

7957
var onBack: (() -> Unit)? = null
8058
var onSearchSent: ((String) -> Unit)? = null
@@ -97,7 +75,6 @@ class InputModeWidget @JvmOverloads constructor(
9775

9876
@IdRes
9977
private var contentId: Int = View.NO_ID
100-
private var focusAnimator: ValueAnimator? = null
10178
private var originalText: String? = null
10279
private var hasTextChangedFromOriginal = false
10380

@@ -108,11 +85,13 @@ class InputModeWidget @JvmOverloads constructor(
10885
inputFieldClearText = findViewById(R.id.inputFieldClearText)
10986
inputModeWidgetBack = findViewById(R.id.InputModeWidgetBack)
11087
inputModeSwitch = findViewById(R.id.inputModeSwitch)
88+
inputModeWidgetCard = findViewById(R.id.inputModeWidgetCard)
11189

11290
configureClickListeners()
11391
configureInputBehavior()
11492
configureTabBehavior()
11593
applyModeSpecificInputBehaviour(isSearchTab = true)
94+
configureShadow()
11695
}
11796

11897
fun provideInitialText(text: String) {
@@ -135,10 +114,6 @@ class InputModeWidget @JvmOverloads constructor(
135114
setHorizontallyScrolling(false)
136115
setRawInputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)
137116

138-
setOnFocusChangeListener { _, hasFocus ->
139-
animateOmnibarFocusedState(hasFocus)
140-
}
141-
142117
setOnEditorActionListener { _, actionId, _ ->
143118
if (actionId == EditorInfo.IME_ACTION_GO) {
144119
submitMessage()
@@ -251,58 +226,6 @@ class InputModeWidget @JvmOverloads constructor(
251226
contentId = id
252227
}
253228

254-
fun animateOmnibarFocusedState(focused: Boolean) {
255-
focusAnimator?.cancel()
256-
257-
val startTop = omnibarCard.marginTop
258-
val startBottom = omnibarCard.marginBottom
259-
val startStart = omnibarCard.marginStart
260-
val startEnd = omnibarCard.marginEnd
261-
val startStroke = omnibarCard.strokeWidth
262-
263-
val endTop = if (focused) omnibarCardFocusedMarginTop else omnibarCardMarginTop
264-
val endBottom = if (focused) omnibarCardFocusedMarginBottom else omnibarCardMarginBottom
265-
val endStart = if (focused) omnibarCardFocusedMarginHorizontal else omnibarCardMarginHorizontal
266-
val endEnd = if (focused) omnibarCardFocusedMarginHorizontal else omnibarCardMarginHorizontal
267-
val endStroke = if (focused) omnibarOutlineFocusedWidth else omnibarOutlineWidth
268-
269-
ValueAnimator.ofFloat(0f, 1f).apply {
270-
duration = DEFAULT_ANIMATION_DURATION
271-
interpolator = DecelerateInterpolator()
272-
addUpdateListener { valueAnimator ->
273-
val fraction = valueAnimator.animatedFraction
274-
(omnibarCard.layoutParams as MarginLayoutParams).apply {
275-
leftMargin = (startStart + (endStart - startStart) * fraction).toInt()
276-
topMargin = (startTop + (endTop - startTop) * fraction).toInt()
277-
rightMargin = (startEnd + (endEnd - startEnd) * fraction).toInt()
278-
bottomMargin = (startBottom + (endBottom - startBottom) * fraction).toInt()
279-
}.also { omnibarCard.layoutParams = it }
280-
omnibarCard.strokeWidth = (startStroke + (endStroke - startStroke) * fraction).toInt()
281-
}
282-
addListener(
283-
onStart = { lockContentDimensions() },
284-
onEnd = { if (!focused) unlockContentDimensions() },
285-
onCancel = { removeAllListeners() },
286-
)
287-
start()
288-
focusAnimator = this
289-
}
290-
}
291-
292-
private fun lockContentDimensions() {
293-
omnibarContent.updateLayoutParams {
294-
width = omnibarContent.measuredWidth
295-
height = ViewGroup.LayoutParams.WRAP_CONTENT
296-
}
297-
}
298-
299-
private fun unlockContentDimensions() {
300-
omnibarContent.updateLayoutParams {
301-
width = ViewGroup.LayoutParams.MATCH_PARENT
302-
height = ViewGroup.LayoutParams.WRAP_CONTENT
303-
}
304-
}
305-
306229
fun printNewLine() {
307230
val currentText = inputField.text.toString()
308231
val selectionStart = inputField.selectionStart
@@ -317,8 +240,18 @@ class InputModeWidget @JvmOverloads constructor(
317240
return text.ifBlank { null }
318241
}
319242

243+
private fun configureShadow() {
244+
if (Build.VERSION.SDK_INT >= 28) {
245+
inputModeWidgetCard.addBottomShadow(
246+
shadowSizeDp = 12f,
247+
offsetYDp = 3f,
248+
insetDp = 3f,
249+
shadowColor = context.getColor(CommonR.color.background_omnibar_shadow),
250+
)
251+
}
252+
}
253+
320254
companion object {
321-
private const val DEFAULT_ANIMATION_DURATION = 300L
322255
private const val FADE_DURATION = 150L
323256
private const val MAX_LINES = 8
324257
private const val SEARCH_MIN_LINES = 1

duckchat/duckchat-impl/src/main/res/layout/fragment_input_screen.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
xmlns:app="http://schemas.android.com/apk/res-auto"
2020
android:id="@+id/root"
2121
android:layout_width="match_parent"
22-
android:layout_height="match_parent">
22+
android:layout_height="match_parent"
23+
android:clipChildren="false">
2324

2425
<com.duckduckgo.duckchat.impl.inputscreen.ui.view.InputModeWidget
2526
android:id="@+id/inputModeWidget"

0 commit comments

Comments
 (0)