Skip to content

Commit eaeb280

Browse files
committed
Allow SyncBarcodeView consumers to define minimum scanning area
1 parent ed5c509 commit eaeb280

File tree

6 files changed

+282
-6
lines changed

6 files changed

+282
-6
lines changed

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/SyncFeature.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,7 @@ interface SyncFeature {
6565

6666
@Toggle.DefaultValue(DefaultFeatureValue.TRUE)
6767
fun canOverrideThemeSyncSetup(): Toggle
68+
69+
@Toggle.DefaultValue(DefaultFeatureValue.TRUE)
70+
fun useNewActivityConnectSyncLayout(): Toggle
6871
}

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncConnectActivity.kt

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,23 @@ package com.duckduckgo.sync.impl.ui
1919
import android.content.Context
2020
import android.content.Intent
2121
import android.os.Bundle
22+
import android.view.View
23+
import android.widget.ImageView
2224
import androidx.activity.addCallback
2325
import androidx.lifecycle.Lifecycle
2426
import androidx.lifecycle.flowWithLifecycle
2527
import androidx.lifecycle.lifecycleScope
2628
import com.duckduckgo.anvil.annotations.InjectWith
2729
import com.duckduckgo.common.ui.DuckDuckGoActivity
30+
import com.duckduckgo.common.ui.view.button.DaxButtonGhost
2831
import com.duckduckgo.common.ui.view.dialog.TextAlertDialogBuilder
2932
import com.duckduckgo.common.ui.view.show
30-
import com.duckduckgo.common.ui.viewbinding.viewBinding
3133
import com.duckduckgo.di.scopes.ActivityScope
34+
import com.duckduckgo.mobile.android.databinding.IncludeDefaultToolbarBinding
3235
import com.duckduckgo.sync.impl.R
36+
import com.duckduckgo.sync.impl.SyncFeature
3337
import com.duckduckgo.sync.impl.databinding.ActivityConnectSyncBinding
38+
import com.duckduckgo.sync.impl.databinding.ActivityConnectSyncNewBinding
3439
import com.duckduckgo.sync.impl.ui.EnterCodeActivity.Companion.Code.CONNECT_CODE
3540
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command
3641
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.FinishWithError
@@ -39,15 +44,21 @@ import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ReadTextCode
3944
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ShowError
4045
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ShowMessage
4146
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.ViewState
47+
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeView
4248
import com.duckduckgo.sync.impl.ui.setup.EnterCodeContract
4349
import com.duckduckgo.sync.impl.ui.setup.EnterCodeContract.EnterCodeContractOutput
4450
import com.google.android.material.snackbar.Snackbar
4551
import kotlinx.coroutines.flow.launchIn
4652
import kotlinx.coroutines.flow.onEach
53+
import javax.inject.Inject
4754

4855
@InjectWith(ActivityScope::class)
4956
class SyncConnectActivity : DuckDuckGoActivity() {
50-
private val binding: ActivityConnectSyncBinding by viewBinding()
57+
58+
@Inject
59+
lateinit var syncFeature: SyncFeature
60+
61+
private lateinit var binding: ConnectSyncBinding
5162
private val viewModel: SyncConnectViewModel by bindViewModel()
5263

5364
private val enterCodeLauncher = registerForActivityResult(
@@ -60,6 +71,15 @@ class SyncConnectActivity : DuckDuckGoActivity() {
6071

6172
override fun onCreate(savedInstanceState: Bundle?) {
6273
super.onCreate(savedInstanceState)
74+
75+
binding = if (syncFeature.useNewActivityConnectSyncLayout().isEnabled()) {
76+
val viewBinding = ActivityConnectSyncNewBinding.inflate(layoutInflater)
77+
ConnectSyncBinding.NewBinding(viewBinding)
78+
} else {
79+
val viewBinding = ActivityConnectSyncBinding.inflate(layoutInflater)
80+
ConnectSyncBinding.OldBinding(viewBinding)
81+
}
82+
6383
setContentView(binding.root)
6484
setupToolbar(binding.includeToolbar.toolbar)
6585

@@ -166,3 +186,27 @@ class SyncConnectActivity : DuckDuckGoActivity() {
166186
private const val SOURCE_INTENT_KEY = "source"
167187
}
168188
}
189+
190+
private sealed interface ConnectSyncBinding {
191+
val root: View
192+
val includeToolbar: IncludeDefaultToolbarBinding
193+
val qrCodeReader: SyncBarcodeView
194+
val qrCodeImageView: ImageView
195+
val copyCodeButton: DaxButtonGhost
196+
197+
data class OldBinding(private val binding: ActivityConnectSyncBinding) : ConnectSyncBinding {
198+
override val root: View get() = binding.root
199+
override val includeToolbar: IncludeDefaultToolbarBinding get() = binding.includeToolbar
200+
override val qrCodeReader: SyncBarcodeView get() = binding.qrCodeReader
201+
override val qrCodeImageView: ImageView get() = binding.qrCodeImageView
202+
override val copyCodeButton: DaxButtonGhost get() = binding.copyCodeButton
203+
}
204+
205+
data class NewBinding(private val binding: ActivityConnectSyncNewBinding) : ConnectSyncBinding {
206+
override val root: View get() = binding.root
207+
override val includeToolbar: IncludeDefaultToolbarBinding get() = binding.includeToolbar
208+
override val qrCodeReader: SyncBarcodeView get() = binding.qrCodeReader
209+
override val qrCodeImageView: ImageView get() = binding.qrCodeImageView
210+
override val copyCodeButton: DaxButtonGhost get() = binding.copyCodeButton
211+
}
212+
}

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeView.kt

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ import android.view.View
3030
import android.widget.FrameLayout
3131
import androidx.core.app.ActivityCompat
3232
import androidx.core.content.ContextCompat
33+
import androidx.core.view.doOnNextLayout
3334
import androidx.lifecycle.ViewModelProvider
3435
import androidx.lifecycle.findViewTreeLifecycleOwner
3536
import androidx.lifecycle.findViewTreeViewModelStoreOwner
3637
import androidx.lifecycle.lifecycleScope
3738
import com.duckduckgo.anvil.annotations.InjectWith
39+
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
3840
import com.duckduckgo.common.ui.viewbinding.viewBinding
3941
import com.duckduckgo.common.utils.ConflatedJob
4042
import com.duckduckgo.common.utils.DispatcherProvider
4143
import com.duckduckgo.di.scopes.ViewScope
4244
import com.duckduckgo.sync.impl.R
45+
import com.duckduckgo.sync.impl.SyncFeature
4346
import com.duckduckgo.sync.impl.databinding.ViewSquareDecoratedBarcodeBinding
4447
import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.Command
4548
import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.Command.CheckCameraAvailable
@@ -48,7 +51,6 @@ import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.Comman
4851
import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.Command.RequestPermissions
4952
import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.ViewState
5053
import dagger.android.support.AndroidSupportInjection
51-
import kotlinx.coroutines.cancel
5254
import kotlinx.coroutines.flow.launchIn
5355
import kotlinx.coroutines.flow.onEach
5456
import javax.inject.Inject
@@ -66,12 +68,30 @@ constructor(
6668
) :
6769
FrameLayout(context, attrs, defStyleAttr) {
6870

71+
private val minScanningAreaHeight: Int
72+
73+
init {
74+
context.obtainStyledAttributes(attrs, R.styleable.SyncBarcodeView).apply {
75+
try {
76+
minScanningAreaHeight = getDimensionPixelSize(R.styleable.SyncBarcodeView_minScanningAreaHeight, MIN_SCANNING_AREA_HEIGHT_NOT_SET)
77+
} finally {
78+
recycle()
79+
}
80+
}
81+
}
82+
6983
@Inject
7084
lateinit var viewModelFactory: SquareDecoratedBarcodeViewModel.Factory
7185

7286
@Inject
7387
lateinit var dispatchers: DispatcherProvider
7488

89+
@Inject
90+
lateinit var appBuildConfig: AppBuildConfig
91+
92+
@Inject
93+
lateinit var syncFeature: SyncFeature
94+
7595
private val cameraBlockedDrawable by lazy {
7696
ContextCompat.getDrawable(context, R.drawable.camera_blocked)
7797
}
@@ -93,6 +113,8 @@ constructor(
93113
AndroidSupportInjection.inject(this)
94114
super.onAttachedToWindow()
95115

116+
setupExpandedScanningArea()
117+
96118
findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel)
97119

98120
val scope = findViewTreeLifecycleOwner()?.lifecycleScope!!
@@ -110,6 +132,23 @@ constructor(
110132
}
111133
}
112134

135+
private fun setupExpandedScanningArea() {
136+
if (minScanningAreaHeight == MIN_SCANNING_AREA_HEIGHT_NOT_SET) return
137+
binding.barcodeContainer.doOnNextLayout {
138+
val containerHeight = binding.barcodeContainer.height
139+
140+
if (containerHeight >= minScanningAreaHeight) return@doOnNextLayout
141+
142+
binding.barcodeView.layoutParams = binding.barcodeView.layoutParams.apply {
143+
height = minScanningAreaHeight
144+
}
145+
146+
binding.barcodeView.doOnNextLayout {
147+
binding.barcodeView.translationY = -((minScanningAreaHeight - containerHeight) / 2f)
148+
}
149+
}
150+
}
151+
113152
override fun onDetachedFromWindow() {
114153
conflatedStateJob.cancel()
115154
conflatedCommandJob.cancel()
@@ -217,4 +256,8 @@ constructor(
217256
}
218257
throw IllegalStateException("The ${this.javaClass.simpleName}'s Context is not an Activity.")
219258
}
259+
260+
companion object {
261+
private const val MIN_SCANNING_AREA_HEIGHT_NOT_SET = -1
262+
}
220263
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?xml version="1.0" encoding="utf-8"?><!--
2+
~ Copyright (c) 2023 DuckDuckGo
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+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
17+
xmlns:app="http://schemas.android.com/apk/res-auto"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent"
21+
android:orientation="vertical">
22+
23+
<include
24+
android:id="@+id/includeToolbar"
25+
layout="@layout/include_default_toolbar" />
26+
27+
<androidx.constraintlayout.widget.ConstraintLayout
28+
android:layout_width="match_parent"
29+
android:layout_height="match_parent">
30+
31+
<LinearLayout
32+
android:id="@+id/readerContainer"
33+
android:layout_width="match_parent"
34+
android:layout_height="0dp"
35+
android:background="?attr/daxColorSurface"
36+
android:orientation="vertical"
37+
app:layout_constraintBottom_toTopOf="@+id/qrcodeContainer"
38+
app:layout_constraintTop_toTopOf="parent">
39+
40+
<com.duckduckgo.common.ui.view.text.DaxTextView
41+
android:id="@+id/connect_hint"
42+
android:layout_width="match_parent"
43+
android:layout_height="wrap_content"
44+
android:layout_margin="@dimen/keyline_4"
45+
android:text="@string/connect_screen_scan_qr_hint" />
46+
47+
<com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeView
48+
android:id="@+id/qrCodeReader"
49+
android:layout_width="match_parent"
50+
android:layout_height="match_parent"
51+
app:minScanningAreaHeight="@dimen/qrBarcodeSize"/>
52+
</LinearLayout>
53+
54+
<androidx.constraintlayout.widget.ConstraintLayout
55+
android:id="@+id/qrcodeContainer"
56+
android:layout_width="0dp"
57+
android:layout_height="wrap_content"
58+
android:background="?attr/daxColorBackground"
59+
app:layout_constraintBottom_toTopOf="@id/copyCodeDivider"
60+
app:layout_constraintHeight_min="185dp"
61+
app:layout_constraintHorizontal_weight="1"
62+
app:layout_constraintStart_toStartOf="parent"
63+
app:layout_constraintEnd_toEndOf="parent"
64+
android:layout_marginStart="20dp"
65+
android:layout_marginEnd="20dp"
66+
android:layout_marginTop="20dp"
67+
app:layout_constraintVertical_chainStyle="spread">
68+
69+
<ImageView
70+
android:id="@+id/qrCodeImageView"
71+
android:layout_width="@dimen/qrSizeMedium"
72+
android:layout_height="@dimen/qrSizeMedium"
73+
android:layout_gravity="center"
74+
app:layout_constraintStart_toStartOf="parent"
75+
app:layout_constraintBottom_toBottomOf="parent"
76+
tools:ignore="ContentDescription" />
77+
78+
<com.duckduckgo.common.ui.view.text.DaxTextView
79+
android:id="@+id/qrCodeHintTitle"
80+
android:layout_width="0dp"
81+
android:layout_height="wrap_content"
82+
android:layout_marginStart="20dp"
83+
app:layout_constraintWidth_max="300dp"
84+
android:text="@string/connect_screen_connect_qr_hint_title"
85+
app:layout_constraintEnd_toEndOf="parent"
86+
app:layout_constraintStart_toEndOf="@+id/qrCodeImageView"
87+
app:layout_constraintTop_toTopOf="@id/qrCodeImageView"
88+
app:typography="h5" />
89+
90+
<com.duckduckgo.common.ui.view.text.DaxTextView
91+
android:id="@+id/qrCodeHintContent"
92+
android:layout_width="0dp"
93+
android:layout_height="wrap_content"
94+
android:layout_marginStart="20dp"
95+
android:layout_marginTop="@dimen/keyline_2"
96+
app:layout_constraintWidth_max="300dp"
97+
android:text="@string/connect_screen_connect_qr_hint_content"
98+
app:layout_constraintEnd_toEndOf="parent"
99+
app:layout_constraintStart_toEndOf="@+id/qrCodeImageView"
100+
app:layout_constraintTop_toBottomOf="@+id/qrCodeHintTitle"
101+
app:textType="secondary"
102+
app:typography="caption" />
103+
104+
</androidx.constraintlayout.widget.ConstraintLayout>
105+
106+
<com.duckduckgo.common.ui.view.divider.HorizontalDivider
107+
android:id="@+id/copyCodeDivider"
108+
android:layout_width="match_parent"
109+
android:layout_height="wrap_content"
110+
app:layout_constraintEnd_toEndOf="parent"
111+
app:layout_constraintStart_toStartOf="parent"
112+
app:layout_constraintVertical_bias="1"
113+
android:layout_marginBottom="10dp"
114+
app:layout_constraintBottom_toTopOf="@id/cantScanLabel"
115+
/>
116+
117+
<com.duckduckgo.common.ui.view.text.DaxTextView
118+
android:id="@+id/cantScanLabel"
119+
android:layout_width="wrap_content"
120+
android:layout_height="0dp"
121+
android:layout_marginBottom="@dimen/keyline_4"
122+
android:layout_marginStart="@dimen/keyline_1"
123+
android:text="@string/login_screen_cant_scan_label"
124+
app:layout_constraintHorizontal_chainStyle="packed"
125+
app:layout_constraintBottom_toBottomOf="parent"
126+
app:layout_constraintEnd_toStartOf="@id/copyCodeButton"
127+
app:layout_constraintStart_toStartOf="parent"
128+
app:textType="secondary" />
129+
130+
131+
<com.duckduckgo.common.ui.view.button.DaxButtonGhost
132+
android:id="@+id/copyCodeButton"
133+
android:layout_width="wrap_content"
134+
android:layout_height="wrap_content"
135+
android:text="@string/login_screen_cant_scan_button_text"
136+
app:daxButtonSize="large"
137+
app:icon="@drawable/ic_copy_24"
138+
app:layout_constraintBottom_toBottomOf="@id/cantScanLabel"
139+
app:layout_constraintEnd_toEndOf="parent"
140+
app:layout_constraintStart_toEndOf="@id/cantScanLabel"
141+
app:layout_constraintTop_toTopOf="@id/cantScanLabel" />
142+
143+
<androidx.fragment.app.FragmentContainerView
144+
android:id="@+id/fragment_container_view"
145+
android:layout_width="0dp"
146+
app:layout_constraintTop_toTopOf="parent"
147+
app:layout_constraintStart_toStartOf="parent"
148+
app:layout_constraintEnd_toEndOf="parent"
149+
app:layout_constraintBottom_toBottomOf="parent"
150+
android:layout_height="0dp"/>
151+
152+
</androidx.constraintlayout.widget.ConstraintLayout>
153+
</LinearLayout>

sync/sync-impl/src/main/res/layout/view_square_decorated_barcode.xml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@
1818
xmlns:app="http://schemas.android.com/apk/res-auto"
1919
xmlns:tools="http://schemas.android.com/tools">
2020

21-
<com.journeyapps.barcodescanner.BarcodeView
22-
android:id="@+id/barcodeView"
21+
<FrameLayout
22+
android:id="@+id/barcodeContainer"
2323
android:layout_width="match_parent"
24-
android:layout_height="match_parent" />
24+
android:layout_height="match_parent"
25+
android:clipChildren="true"
26+
android:clipToPadding="true">
27+
28+
<com.journeyapps.barcodescanner.BarcodeView
29+
android:id="@+id/barcodeView"
30+
android:layout_width="match_parent"
31+
android:layout_height="match_parent" />
32+
33+
</FrameLayout>
2534

2635
<com.duckduckgo.common.ui.view.text.DaxTextView
2736
android:id="@+id/barcodeHint"

0 commit comments

Comments
 (0)