From a706a1a8672c52f18ce918d65578f13d5e2a235e Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Fri, 1 May 2020 15:51:46 +0100 Subject: [PATCH 01/19] WIP --- .../app/browser/BrowserTabFragment.kt | 42 ++- .../app/browser/BrowserTabViewModel.kt | 22 +- .../browser/BrowserTrackersAnimatorHelper.kt | 326 ++++++++++++++---- .../res/drawable/background_tracker_logo.xml | 24 ++ .../main/res/drawable/other_tracker_bg.xml | 5 +- .../main/res/drawable/privacygrade_icon_a.xml | 31 +- .../main/res/drawable/privacygrade_icon_b.xml | 31 +- .../res/drawable/privacygrade_icon_b_plus.xml | 17 +- .../main/res/drawable/privacygrade_icon_c.xml | 31 +- .../res/drawable/privacygrade_icon_c_plus.xml | 17 +- .../main/res/drawable/privacygrade_icon_d.xml | 31 +- .../drawable/privacygrade_icon_loading.xml | 28 ++ .../res/layout/include_omnibar_toolbar.xml | 35 +- 13 files changed, 504 insertions(+), 136 deletions(-) create mode 100644 app/src/main/res/drawable/background_tracker_logo.xml create mode 100644 app/src/main/res/drawable/privacygrade_icon_loading.xml diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 8d8c01ed1f53..840bced9a410 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -184,7 +184,7 @@ import javax.inject.Inject import kotlin.concurrent.thread import kotlin.coroutines.CoroutineContext -class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogListener { +class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogListener, TrackersAnimatorListener { private val supervisorJob = SupervisorJob() @@ -271,7 +271,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi viewModel } - private val animatorHelper by lazy { BrowserTrackersAnimatorHelper() } + private val animatorHelper by lazy { BrowserTrackersAnimatorHelper(privacyGradeButton) } private val smoothProgressAnimator by lazy { SmoothProgressAnimator(pageLoadingIndicator) } @@ -351,6 +351,8 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi decorateWithFeatures() + animatorHelper.setListener(this) + if (savedInstanceState == null) { viewModel.onViewReady() messageFromPreviousTab?.let { @@ -781,12 +783,16 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi browserActivity?.launchPrivacyDashboard() } - viewModel.privacyGrade.observe(viewLifecycleOwner, Observer { - Timber.d("Observed grade: $it") - it?.let { privacyGrade -> - val drawable = context?.getDrawable(privacyGrade.icon()) ?: return@let + viewModel.privacyGradeState.observe(viewLifecycleOwner, Observer { + Timber.d("MARCOS Observed grade: $it") + if (it.canShowPrivacyGrade) { + val drawable = context?.getDrawable(it.privacyGrade.icon()) + privacyGradeButton?.setImageDrawable(drawable) + privacyGradeButton?.isEnabled = it.privacyGrade != PrivacyGrade.UNKNOWN + } else { + val drawable = context?.getDrawable(R.drawable.privacygrade_icon_loading) privacyGradeButton?.setImageDrawable(drawable) - privacyGradeButton?.isEnabled = privacyGrade != PrivacyGrade.UNKNOWN + privacyGradeButton?.isEnabled = false } }) } @@ -1072,6 +1078,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi } override fun onDestroy() { + animatorHelper.removeListener() supervisorJob.cancel() popupMenu.dismiss() destroyWebView() @@ -1211,7 +1218,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi } private fun finishTrackerAnimation() { - animatorHelper.finishTrackerAnimation(omnibarViews(), networksContainer) + animatorHelper.finishTrackerAnimation(omnibarViews(), animationContainer) } private fun showHideTipsDialog(cta: Cta) { @@ -1248,7 +1255,12 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi .show() } - fun omnibarViews(): List = listOf(clearTextButton, omnibarTextInput, privacyGradeButton, searchIcon) + fun omnibarViews(): List = listOf(clearTextButton, omnibarTextInput, searchIcon) + + override fun onAnimationFinished() { + Timber.d("MARCOS animation finished") + viewModel.updatePrivacyGrade() + } companion object { private const val TAB_ID_ARG = "TAB_ID_ARG" @@ -1586,6 +1598,10 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi if (viewState.progress == MAX_PROGRESS) { createTrackersAnimation() + animatorHelper.stopPulseAnimation() + } else { + animatorHelper.pulseAnimation(privacyGradeButton) + viewModel.stopShowPrivacyGrade() } } } @@ -1598,15 +1614,17 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi delay(TRACKERS_SECONDARY_DELAY) if (lastSeenOmnibarViewState?.isEditing != true) { val site = viewModel.siteLiveData.value + val grade = viewModel.privacyGradeState.value?.privacyGrade val events = site?.orderedTrackingEntities() activity?.let { activity -> animatorHelper.startTrackersAnimation( lastSeenCtaViewState?.cta, activity, - networksContainer, + animationContainer, omnibarViews(), - events + events, + grade ) } } @@ -1615,7 +1633,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi fun cancelAllAnimations() { animatorHelper.cancelAnimations() - networksContainer.alpha = 0f + //animationContainer.alpha = 0f clearTextButton.alpha = 1f omnibarTextInput.alpha = 1f } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index e4e0735dd60c..29a9e6fe50ba 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -161,6 +161,11 @@ class BrowserTabViewModel( val searchResults: AutoCompleteResult = AutoCompleteResult("", emptyList()) ) + data class PrivacyGradeState( + val privacyGrade: PrivacyGrade = PrivacyGrade.UNKNOWN, + val canShowPrivacyGrade: Boolean = false + ) + sealed class Command { object Refresh : Command() class Navigate(val url: String) : Command() @@ -209,11 +214,11 @@ class BrowserTabViewModel( val findInPageViewState: MutableLiveData = MutableLiveData() val ctaViewState: MutableLiveData = MutableLiveData() var siteLiveData: MutableLiveData = MutableLiveData() + val privacyGradeState: MutableLiveData = MutableLiveData() var skipHome = false val tabs: LiveData> = tabRepository.liveTabs val survey: LiveData = ctaViewModel.surveyLiveData - val privacyGrade: MutableLiveData = MutableLiveData() val command: SingleLiveEvent = SingleLiveEvent() val url: String? @@ -621,16 +626,16 @@ class BrowserTabViewModel( } private fun onSiteChanged() { + Timber.d("MARCOS On Site Changed") httpsUpgraded = false viewModelScope.launch { - val improvedGrade = withContext(dispatchers.io()) { site?.calculateGrades()?.improvedGrade } withContext(dispatchers.main()) { siteLiveData.value = site - privacyGrade.value = improvedGrade + privacyGradeState.value = currentPrivacyGradeState().copy(privacyGrade = improvedGrade ?: PrivacyGrade.UNKNOWN) } withContext(dispatchers.io()) { @@ -639,6 +644,15 @@ class BrowserTabViewModel( } } + fun updatePrivacyGrade() { + Timber.d("MARCOS update privacy grade") + privacyGradeState.value = currentPrivacyGradeState().copy(canShowPrivacyGrade = true) + } + + fun stopShowPrivacyGrade() { + privacyGradeState.value = currentPrivacyGradeState().copy(canShowPrivacyGrade = false) + } + override fun showFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { command.value = ShowFileChooser(filePathCallback, fileChooserParams) } @@ -650,6 +664,7 @@ class BrowserTabViewModel( private fun currentOmnibarViewState(): OmnibarViewState = omnibarViewState.value!! private fun currentLoadingViewState(): LoadingViewState = loadingViewState.value!! private fun currentCtaViewState(): CtaViewState = ctaViewState.value!! + private fun currentPrivacyGradeState(): PrivacyGradeState = privacyGradeState.value!! fun onOmnibarInputStateChanged(query: String, hasFocus: Boolean, hasQueryChanged: Boolean) { @@ -827,6 +842,7 @@ class BrowserTabViewModel( omnibarViewState.value = OmnibarViewState() findInPageViewState.value = FindInPageViewState() ctaViewState.value = CtaViewState() + privacyGradeState.value = PrivacyGradeState() } fun onShareSelected() { diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt index 71505cbd01a3..613060370c05 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt @@ -18,31 +18,59 @@ package com.duckduckgo.app.browser import android.animation.AnimatorSet import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder import android.app.Activity import android.view.Gravity import android.view.View +import android.view.ViewGroup +import android.view.animation.LinearInterpolator import android.widget.FrameLayout +import android.widget.ImageButton import android.widget.ImageView import androidx.appcompat.widget.AppCompatTextView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.animation.addListener +import androidx.core.content.ContextCompat import androidx.core.view.children import androidx.core.widget.TextViewCompat +import androidx.transition.Fade +import androidx.transition.Slide +import androidx.transition.TransitionManager +import androidx.transition.TransitionSet import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.duckduckgo.app.cta.ui.Cta import com.duckduckgo.app.cta.ui.DaxDialogCta +import com.duckduckgo.app.global.view.toPx +import com.duckduckgo.app.privacy.model.PrivacyGrade import com.duckduckgo.app.privacy.renderer.TrackersRenderer import com.duckduckgo.app.trackerdetection.model.Entity +import timber.log.Timber +import kotlin.math.log -class BrowserTrackersAnimatorHelper { + +interface TrackersAnimatorListener { + fun onAnimationFinished() +} + +class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { private var trackersAnimation: AnimatorSet = AnimatorSet() + private var pulseAnimation: AnimatorSet = AnimatorSet() + private var listener: TrackersAnimatorListener? = null - fun startTrackersAnimation(cta: Cta?, activity: Activity, container: ConstraintLayout, omnibarViews: List, entities: List?) { - if (entities.isNullOrEmpty()) return + fun startTrackersAnimation(cta: Cta?, activity: Activity, container: ViewGroup, omnibarViews: List, entities: List?, grade: PrivacyGrade?) { + if (entities.isNullOrEmpty()) { + listener?.onAnimationFinished() + return + } val logoViews: List = getLogosViewListInContainer(activity, container, entities) - if (logoViews.isEmpty()) return + if (logoViews.isEmpty()) { + listener?.onAnimationFinished() + return + } + + val drawable = ContextCompat.getDrawable(activity, R.drawable.privacygrade_icon_loading) + privacyGradeButton.setImageDrawable(drawable) if (!trackersAnimation.isRunning) { trackersAnimation = if (cta is DaxDialogCta.DaxTrackersBlockedCta) { @@ -50,57 +78,91 @@ class BrowserTrackersAnimatorHelper { start() } } else { - createFullTrackersAnimation(container, omnibarViews, logoViews).apply { + createFullTrackersAnimation(container, omnibarViews, logoViews, grade).apply { start() } } } } + fun removeListener() { + listener = null + } + + fun setListener(animatorListener: TrackersAnimatorListener) { + listener = animatorListener + } + fun cancelAnimations() { if (trackersAnimation.isRunning) { trackersAnimation.end() } + stopPulseAnimation() } - fun finishTrackerAnimation(fadeInViews: List, container: ConstraintLayout) { + fun areAnimationsRunning() = trackersAnimation.isRunning + + fun finishTrackerAnimation(fadeInViews: List, container: ViewGroup) { val animateOmnibarIn = animateOmnibarIn(fadeInViews) val animateLogosOut = animateFadeOut(container) trackersAnimation = AnimatorSet().apply { play(animateLogosOut).before(animateOmnibarIn) start() + addListener( + onEnd = { + Timber.d("MARCOS DO ON END") + container.removeViews(1, container.children.count() - 1) + listener?.onAnimationFinished() + } + ) } } - private fun getLogosViewListInContainer(activity: Activity, container: ConstraintLayout, entities: List): List { + private fun getLogosViewListInContainer(activity: Activity, container: ViewGroup, entities: List): List { container.removeAllViews() - container.alpha = 0f + //container.alpha = 0f val logos = createResourcesIdList(activity, entities) - return createLogosViewList(activity, container, logos) + val logosList = createLogosViewList(activity, container, logos) +// logosList.reversed().map { +// it.bringToFront() +// } + logosList.first().bringToFront() + return logosList } private fun createLogosViewList( activity: Activity, - container: ConstraintLayout, + container: ViewGroup, resourcesId: List ): List { - return resourcesId.map { - return@map if (it.resId == R.drawable.other_tracker_bg) { - val frameLayout = createTrackerTextLogo(activity, it) + return resourcesId.mapIndexed { index, it -> + return@mapIndexed if (it.resId == R.drawable.other_tracker_bg) { + val frameLayout = createTrackerTextLogo(activity, it, index) container.addView(frameLayout) frameLayout } else { - val imageView = createTrackerImageLogo(activity, it) + val imageView = createTrackerImageLogo(activity, it, index) container.addView(imageView) imageView } } } - private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo): FrameLayout { + private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo, index: Int): FrameLayout { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + params.gravity = Gravity.CENTER + val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + if (index == 0) { + frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) + } else { + frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) + } val frameLayout = FrameLayout(activity) + frameLayout.alpha = 0f + frameLayout.visibility = View.INVISIBLE frameLayout.id = View.generateViewId() + frameLayout.layoutParams = frameLayoutParams + frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) val animationView = ImageView(activity) val animatedDrawable = AnimatedVectorDrawableCompat.create(activity, R.drawable.network_cross_anim) @@ -120,14 +182,33 @@ class BrowserTrackersAnimatorHelper { return frameLayout } - private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo): ImageView { + private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo, index: Int): FrameLayout { + val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + if (index == 0) { + frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) + } else { + frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) + } + params.gravity = Gravity.CENTER + val frameLayout = FrameLayout(activity) + frameLayout.alpha = 0f + frameLayout.visibility = View.INVISIBLE + frameLayout.id = View.generateViewId() + frameLayout.layoutParams = frameLayoutParams + frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) + val imageView = ImageView(activity) imageView.scaleType = ImageView.ScaleType.CENTER_CROP val animatedDrawable = AnimatedVectorDrawableCompat.create(activity, R.drawable.network_cross_anim) imageView.setImageDrawable(animatedDrawable) imageView.setBackgroundResource(trackerLogo.resId) imageView.id = View.generateViewId() - return imageView + imageView.layoutParams = params + + frameLayout.addView(imageView) + + return frameLayout } private fun createResourcesIdList(activity: Activity, entities: List): List { @@ -171,67 +252,97 @@ class BrowserTrackersAnimatorHelper { } private fun createFullTrackersAnimation( - container: ConstraintLayout, + container: ViewGroup, omnibarViews: List, - logoViews: List + logoViews: List, + grade: PrivacyGrade? ): AnimatorSet { return AnimatorSet().apply { play(createPartialTrackersAnimation(container, omnibarViews, logoViews)) - play(animateFadeOut(container)) + play(animateOmnibarMoveToLeft(logoViews)) .after(TRACKER_LOGOS_DELAY_ON_SCREEN) .before(animateOmnibarIn(omnibarViews)) + addListener( + onEnd = { + Timber.d("MARCOS DO ON END") + container.removeAllViews() + listener?.onAnimationFinished() + } + ) } } +// private fun createFullTrackersAnimation( +// container: ViewGroup, +// omnibarViews: List, +// logoViews: List +// ): AnimatorSet { +// return AnimatorSet().apply { +// play(createPartialTrackersAnimation(container, omnibarViews, logoViews)) +// play(animateFadeOut(container)) +// .after(TRACKER_LOGOS_DELAY_ON_SCREEN) +// .before(animateOmnibarIn(omnibarViews)) +// addListener( +// onEnd = { +// Timber.d("MARCOS DO ON END") +// container.removeViews(1, container.children.count() - 1) +// } +// ) +// } +// } + private fun createPartialTrackersAnimation( - container: ConstraintLayout, + container: ViewGroup, omnibarViews: List, logoViews: List ): AnimatorSet { val fadeOmnibarOut = animateOmnibarOut(omnibarViews) - val fadeLogosIn = animateFadeIn(container) - - applyConstraintSet(container, logoViews) + //val fadeLogosIn = animateOmnibarMoveToRight(logoViews) + val fadeLogosIn = animateOmnibarMoveToRight(logoViews) + animateMoveToRight(container, logoViews) + //animateMoveToRight(container, logoViews) + //applyConstraintSet(container, logoViews) animateBlockedLogos(logoViews) return AnimatorSet().apply { - play(fadeLogosIn).after(fadeOmnibarOut) + //play(fadeLogosIn).after(fadeOmnibarOut) + play(fadeOmnibarOut) } } - private fun applyConstraintSet(container: ConstraintLayout, views: List) { - val constraints = ConstraintSet() - constraints.clone(container) - - views.mapIndexed { index, view -> - constraints.connect(view.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) - constraints.connect(view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) - if (index == 0) { - constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) - } else { - constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 10) - } - if (index == views.size - 1) { - constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) - } - } - - val viewIds = views.map { it.id }.toIntArray() - - if (viewIds.size > 1) { - constraints.createHorizontalChain( - ConstraintSet.PARENT_ID, - ConstraintSet.LEFT, - ConstraintSet.PARENT_ID, - ConstraintSet.RIGHT, - viewIds, - null, - ConstraintSet.CHAIN_SPREAD - ) - } - - constraints.applyTo(container) - } +// private fun applyConstraintSet(container: ConstraintLayout, views: List) { +// val constraints = ConstraintSet() +// constraints.clone(container) +// +// views.mapIndexed { index, view -> +// constraints.connect(view.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) +// constraints.connect(view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) +// if (index == 0) { +// constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) +// } else { +// constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 10) +// } +// if (index == views.size - 1) { +// constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) +// } +// } +// +// val viewIds = views.map { it.id }.toIntArray() +// +// if (viewIds.size > 1) { +// constraints.createHorizontalChain( +// ConstraintSet.PARENT_ID, +// ConstraintSet.LEFT, +// ConstraintSet.PARENT_ID, +// ConstraintSet.RIGHT, +// viewIds, +// null, +// ConstraintSet.CHAIN_SPREAD +// ) +// } +// +// constraints.applyTo(container) +// } private fun animateOmnibarOut(views: List): AnimatorSet { val animators = views.map { @@ -251,22 +362,111 @@ class BrowserTrackersAnimatorHelper { } } - private fun animateFadeOut(view: View): ObjectAnimator { - return ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply { + private fun animateOmnibarMoveToRight(views: List): AnimatorSet { +// val animators = views.map { +// animateMoveToRight2(it) +// } + val animators2 = views.map { + animateFadeIn(it) + } + return AnimatorSet().apply { + playTogether(animators2) + } + } + + private fun animateOmnibarMoveToLeft(views: List): AnimatorSet { + val animators = views.map { + animateMoveToLeft(it) + } + val animators2 = views.map { + animateFadeOut(it) + } + return AnimatorSet().apply { + playTogether(animators + animators2) + } + } + + private fun animateMoveToRight2(view: View): ObjectAnimator { + return ObjectAnimator.ofFloat(view, "x", view.left.toFloat(), view.left.toFloat()).apply { duration = DEFAULT_ANIMATION_DURATION } } + private fun animateMoveToLeft(view: View): ObjectAnimator { + return ObjectAnimator.ofFloat(view, "x", 0f).apply { + duration = DEFAULT_ANIMATION_DURATION + } + } + + private fun animateMoveToRight(viewGroup: ViewGroup, views: List) { + val transitionSet = TransitionSet() + val transitionSlide = Slide(Gravity.START) + transitionSlide.duration = DEFAULT_ANIMATION_DURATION + transitionSlide.interpolator = LinearInterpolator() + + val transitionFadeIn = Fade(Fade.IN) + transitionFadeIn.duration = DEFAULT_ANIMATION_DURATION + transitionFadeIn.interpolator = LinearInterpolator() + + transitionSet.ordering = TransitionSet.ORDERING_TOGETHER + + transitionSet + .addTransition(transitionFadeIn) + .addTransition(transitionSlide) + + TransitionManager.beginDelayedTransition(viewGroup, transitionSet) + views.map { + it.visibility = View.VISIBLE + it.alpha = 1f + } + + } + + fun pulseAnimation(view: View) { + if (!pulseAnimation.isRunning) { + val scaleDown = ObjectAnimator.ofPropertyValuesHolder( + view, + PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f), + PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f) + ) + scaleDown.repeatCount = ObjectAnimator.INFINITE + scaleDown.duration = 750 + + pulseAnimation = AnimatorSet().apply { + play(scaleDown) + start() + } + } + } + + fun stopPulseAnimation() { + if (pulseAnimation.isRunning) { + pulseAnimation.cancel() + } + } + + private fun animateFadeOut(view: View, durationInMs: Long = DEFAULT_ANIMATION_DURATION): ObjectAnimator { + return ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply { + duration = durationInMs + } + } + private fun animateFadeIn(view: View): ObjectAnimator { return ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply { duration = DEFAULT_ANIMATION_DURATION } } + private fun animateExpand(view: View): ObjectAnimator { + return ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply { + duration = DEFAULT_ANIMATION_DURATION + } + } + companion object { private const val TRACKER_LOGOS_DELAY_ON_SCREEN = 2400L private const val DEFAULT_ANIMATION_DURATION = 150L - private const val MAX_LOGOS_SHOWN = 3 + private const val MAX_LOGOS_SHOWN = 4 } } diff --git a/app/src/main/res/drawable/background_tracker_logo.xml b/app/src/main/res/drawable/background_tracker_logo.xml new file mode 100644 index 000000000000..b3641d2f8edb --- /dev/null +++ b/app/src/main/res/drawable/background_tracker_logo.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/other_tracker_bg.xml b/app/src/main/res/drawable/other_tracker_bg.xml index feb8e3a29aa9..95bc254a0a89 100644 --- a/app/src/main/res/drawable/other_tracker_bg.xml +++ b/app/src/main/res/drawable/other_tracker_bg.xml @@ -17,7 +17,8 @@ + + android:height="26dp" + android:width="26dp" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/privacygrade_icon_a.xml b/app/src/main/res/drawable/privacygrade_icon_a.xml index effb7aa4582f..302817601a52 100644 --- a/app/src/main/res/drawable/privacygrade_icon_a.xml +++ b/app/src/main/res/drawable/privacygrade_icon_a.xml @@ -1,9 +1,28 @@ + + + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM15.758,15.4L12.224,6.229L9.776,6.229L6.242,15.4L8.456,15.4L9.034,13.846L12.966,13.846L13.544,15.4L15.757,15.4L15.758,15.4ZM12.43,12.128L9.57,12.128L11,8.18L12.43,12.127L12.43,12.128Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_b.xml b/app/src/main/res/drawable/privacygrade_icon_b.xml index 816a91c1640d..fb3e65a62c19 100644 --- a/app/src/main/res/drawable/privacygrade_icon_b.xml +++ b/app/src/main/res/drawable/privacygrade_icon_b.xml @@ -1,9 +1,28 @@ + + + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM12.815,15.771C14.575,15.771 15.483,14.671 15.483,13.282C15.483,12.142 14.713,11.192 13.723,11.042C14.589,10.862 15.303,10.079 15.303,8.938C15.303,7.714 14.41,6.6 12.663,6.6L7.839,6.6L7.839,15.771L12.816,15.771L12.815,15.771ZM12.238,10.244L9.79,10.244L9.79,8.319L12.238,8.319C12.898,8.319 13.31,8.717 13.31,9.281C13.31,9.872 12.898,10.244 12.238,10.244ZM12.32,14.053L9.79,14.053L9.79,11.963L12.32,11.963C13.076,11.963 13.489,12.43 13.489,13.008C13.489,13.668 13.049,14.053 12.32,14.053Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_b_plus.xml b/app/src/main/res/drawable/privacygrade_icon_b_plus.xml index c30b3fdb1258..96b7c2476268 100644 --- a/app/src/main/res/drawable/privacygrade_icon_b_plus.xml +++ b/app/src/main/res/drawable/privacygrade_icon_b_plus.xml @@ -1,5 +1,5 @@ + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM15.446,10.77L13.612,10.77L13.612,12.604L15.446,12.604L15.446,14.438L17.279,14.438L17.279,12.604L19.113,12.604L19.113,10.771L17.279,10.771L17.279,8.937L15.446,8.937L15.446,10.771L15.446,10.77ZM9.322,15.813C11.082,15.813 11.99,14.713 11.99,13.323C11.99,12.183 11.22,11.233 10.23,11.083C11.096,10.903 11.811,10.12 11.811,8.979C11.811,7.755 10.918,6.641 9.171,6.641L4.345,6.641L4.345,15.813L9.323,15.813L9.322,15.813ZM8.745,10.285L6.298,10.285L6.298,8.36L8.745,8.36C9.405,8.36 9.818,8.759 9.818,9.323C9.818,9.913 9.405,10.285 8.745,10.285L8.745,10.285ZM8.828,14.094L6.298,14.094L6.298,12.004L8.828,12.004C9.584,12.004 9.996,12.471 9.996,13.049C9.996,13.709 9.556,14.094 8.828,14.094Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_c.xml b/app/src/main/res/drawable/privacygrade_icon_c.xml index 77e0e6bb098a..f172ba5c327c 100644 --- a/app/src/main/res/drawable/privacygrade_icon_c.xml +++ b/app/src/main/res/drawable/privacygrade_icon_c.xml @@ -1,9 +1,28 @@ + + + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM11.495,15.538C13.503,15.538 14.671,14.424 15.317,13.283L13.64,12.471C13.255,13.214 12.43,13.805 11.495,13.805C9.818,13.805 8.608,12.526 8.608,10.794C8.608,9.061 9.818,7.783 11.495,7.783C12.43,7.783 13.255,8.373 13.64,9.116L15.317,8.291C14.671,7.136 13.502,6.05 11.495,6.05C8.731,6.05 6.6,7.961 6.6,10.794C6.6,13.613 8.731,15.538 11.495,15.538L11.495,15.538Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_c_plus.xml b/app/src/main/res/drawable/privacygrade_icon_c_plus.xml index f84e9ec8a82a..fa437a4f8faf 100644 --- a/app/src/main/res/drawable/privacygrade_icon_c_plus.xml +++ b/app/src/main/res/drawable/privacygrade_icon_c_plus.xml @@ -1,5 +1,5 @@ + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M15.446,10.358L13.613,10.358L13.613,12.192L15.446,12.192L15.446,14.025L17.279,14.025L17.279,12.192L19.113,12.192L19.113,10.358L17.279,10.358L17.279,8.525L15.446,8.525L15.446,10.358ZM11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM8.8,15.565C10.807,15.565 11.976,14.451 12.622,13.31L10.945,12.499C10.56,13.241 9.735,13.832 8.8,13.832C7.122,13.832 5.912,12.554 5.912,10.822C5.912,9.089 7.122,7.81 8.8,7.81C9.735,7.81 10.56,8.401 10.945,9.144L12.623,8.319C11.976,7.164 10.807,6.077 8.8,6.077C6.036,6.077 3.905,7.989 3.905,10.821C3.905,13.64 6.036,15.565 8.8,15.565Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_d.xml b/app/src/main/res/drawable/privacygrade_icon_d.xml index 64fd2f12f441..6f95bf96e628 100644 --- a/app/src/main/res/drawable/privacygrade_icon_d.xml +++ b/app/src/main/res/drawable/privacygrade_icon_d.xml @@ -1,9 +1,28 @@ + + + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + android:pathData="M11,22C4.925,22 0,17.075 0,11C0,4.925 4.925,0 11,0C17.075,0 22,4.925 22,11C22,17.075 17.075,22 11,22ZM11.33,15.771C14.19,15.771 16.184,13.956 16.184,11.179C16.184,8.429 14.19,6.6 11.316,6.6L7.7,6.6L7.7,15.771L11.33,15.771ZM11.316,14.052L9.652,14.052L9.652,8.32L11.33,8.32C13.2,8.32 14.19,9.571 14.19,11.18C14.19,12.747 13.131,14.053 11.316,14.053L11.316,14.052Z" + android:strokeWidth="1" + android:fillColor="?attr/toolbarIconColor" + android:fillType="evenOdd" + android:strokeColor="#00000000"/> diff --git a/app/src/main/res/drawable/privacygrade_icon_loading.xml b/app/src/main/res/drawable/privacygrade_icon_loading.xml new file mode 100644 index 000000000000..4a492d2d6909 --- /dev/null +++ b/app/src/main/res/drawable/privacygrade_icon_loading.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/app/src/main/res/layout/include_omnibar_toolbar.xml b/app/src/main/res/layout/include_omnibar_toolbar.xml index cd26d56e7ade..62ab3e85fee2 100644 --- a/app/src/main/res/layout/include_omnibar_toolbar.xml +++ b/app/src/main/res/layout/include_omnibar_toolbar.xml @@ -57,13 +57,6 @@ android:paddingStart="6dp" android:paddingEnd="6dp"> - - + + + + android:src="@drawable/privacygrade_icon_loading" /> + android:src="@drawable/ic_loupe_24dp" /> From 69669cdfad2f71d760625e692db479ef761589e4 Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Mon, 11 May 2020 13:12:37 +0100 Subject: [PATCH 02/19] Amend animation --- .../app/browser/BrowserTabFragment.kt | 13 +- .../browser/BrowserTrackersAnimatorHelper.kt | 135 +++++++++++------- .../res/layout/include_omnibar_toolbar.xml | 22 +-- 3 files changed, 97 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 4a0a3048a2b7..336dd359fae9 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -156,16 +156,7 @@ import kotlinx.android.synthetic.main.include_new_browser_tab.ctaContainer import kotlinx.android.synthetic.main.include_new_browser_tab.ctaTopContainer import kotlinx.android.synthetic.main.include_new_browser_tab.ddgLogo import kotlinx.android.synthetic.main.include_new_browser_tab.newTabLayout -import kotlinx.android.synthetic.main.include_omnibar_toolbar.appBarLayout -import kotlinx.android.synthetic.main.include_omnibar_toolbar.browserMenu -import kotlinx.android.synthetic.main.include_omnibar_toolbar.clearTextButton -import kotlinx.android.synthetic.main.include_omnibar_toolbar.networksContainer -import kotlinx.android.synthetic.main.include_omnibar_toolbar.omnibarTextInput -import kotlinx.android.synthetic.main.include_omnibar_toolbar.pageLoadingIndicator -import kotlinx.android.synthetic.main.include_omnibar_toolbar.privacyGradeButton -import kotlinx.android.synthetic.main.include_omnibar_toolbar.searchIcon -import kotlinx.android.synthetic.main.include_omnibar_toolbar.toolbar -import kotlinx.android.synthetic.main.include_omnibar_toolbar.toolbarContainer +import kotlinx.android.synthetic.main.include_omnibar_toolbar.* import kotlinx.android.synthetic.main.include_omnibar_toolbar.view.browserMenu import kotlinx.android.synthetic.main.include_omnibar_toolbar.view.fireIconMenu import kotlinx.android.synthetic.main.include_omnibar_toolbar.view.privacyGradeButton @@ -1656,7 +1647,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi fun cancelAllAnimations() { animatorHelper.cancelAnimations() - //animationContainer.alpha = 0f + animationContainer.alpha = 0f clearTextButton.alpha = 1f omnibarTextInput.alpha = 1f } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt index 613060370c05..230c3f0ca1e4 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt @@ -28,6 +28,8 @@ import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView import androidx.appcompat.widget.AppCompatTextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.view.children @@ -57,7 +59,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { private var pulseAnimation: AnimatorSet = AnimatorSet() private var listener: TrackersAnimatorListener? = null - fun startTrackersAnimation(cta: Cta?, activity: Activity, container: ViewGroup, omnibarViews: List, entities: List?, grade: PrivacyGrade?) { + fun startTrackersAnimation(cta: Cta?, activity: Activity, container: ConstraintLayout, omnibarViews: List, entities: List?, grade: PrivacyGrade?) { if (entities.isNullOrEmpty()) { listener?.onAnimationFinished() return @@ -111,7 +113,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { addListener( onEnd = { Timber.d("MARCOS DO ON END") - container.removeViews(1, container.children.count() - 1) +// container.removeViews(1, container.children.count() - 1) listener?.onAnimationFinished() } ) @@ -120,14 +122,9 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { private fun getLogosViewListInContainer(activity: Activity, container: ViewGroup, entities: List): List { container.removeAllViews() - //container.alpha = 0f + container.alpha = 0f val logos = createResourcesIdList(activity, entities) - val logosList = createLogosViewList(activity, container, logos) -// logosList.reversed().map { -// it.bringToFront() -// } - logosList.first().bringToFront() - return logosList + return createLogosViewList(activity, container, logos) } private fun createLogosViewList( @@ -152,11 +149,12 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) params.gravity = Gravity.CENTER val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - if (index == 0) { - frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) - } else { - frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) - } +// frameLayoutParams.gravity = Gravity.RIGHT +// if (index == 0) { +// frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) +// } else { +// frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) +// } val frameLayout = FrameLayout(activity) frameLayout.alpha = 0f frameLayout.visibility = View.INVISIBLE @@ -185,11 +183,12 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo, index: Int): FrameLayout { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - if (index == 0) { - frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) - } else { - frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) - } +// frameLayoutParams.gravity = Gravity.RIGHT +// if (index == 0) { +// frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) +// } else { +// frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) +// } params.gravity = Gravity.CENTER val frameLayout = FrameLayout(activity) frameLayout.alpha = 0f @@ -252,7 +251,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } private fun createFullTrackersAnimation( - container: ViewGroup, + container: ConstraintLayout, omnibarViews: List, logoViews: List, grade: PrivacyGrade? @@ -265,7 +264,8 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { addListener( onEnd = { Timber.d("MARCOS DO ON END") - container.removeAllViews() + //container.removeAllViews() + container.alpha = 0f listener?.onAnimationFinished() } ) @@ -292,16 +292,16 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { // } private fun createPartialTrackersAnimation( - container: ViewGroup, + container: ConstraintLayout, omnibarViews: List, logoViews: List ): AnimatorSet { + applyConstraintSet(container, logoViews) val fadeOmnibarOut = animateOmnibarOut(omnibarViews) //val fadeLogosIn = animateOmnibarMoveToRight(logoViews) val fadeLogosIn = animateOmnibarMoveToRight(logoViews) animateMoveToRight(container, logoViews) //animateMoveToRight(container, logoViews) - //applyConstraintSet(container, logoViews) animateBlockedLogos(logoViews) return AnimatorSet().apply { @@ -310,38 +310,68 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } -// private fun applyConstraintSet(container: ConstraintLayout, views: List) { -// val constraints = ConstraintSet() -// constraints.clone(container) -// -// views.mapIndexed { index, view -> -// constraints.connect(view.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) -// constraints.connect(view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) -// if (index == 0) { -// constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) + private fun applyConstraintSet(container: ConstraintLayout, views: List) { + val constraints = ConstraintSet() + constraints.clone(container) + + views.mapIndexed { index, view -> + constraints.connect(view.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) + constraints.connect(view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) + if (index == 0) { + constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 25.toPx()) + } else { + constraints.setTranslationX(view.id, (-7.toPx() * index).toFloat()) + constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 0) + } + if (index == views.size - 1) { + constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) + } + } + + views.reversed().map { + it.bringToFront() + } + + val viewIds = views.map { it.id }.toIntArray() + + if (viewIds.size > 1) { + constraints.createHorizontalChain( + ConstraintSet.PARENT_ID, + ConstraintSet.LEFT, + ConstraintSet.PARENT_ID, + ConstraintSet.RIGHT, + viewIds, + null, + ConstraintSet.CHAIN_SPREAD + ) + } + + constraints.applyTo(container) + } + +// var previousItem: View? = null +// var i = 1 +// views.map { +// val isLastItem = views.indexOf(it) == views.size - 1 +// constraints.connect(it.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) +// constraints.connect(it.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) +// if (previousItem == null) { +// constraints.connect(it.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) +// } else { +// val margin = if (it is TextView) { +// if (views.size == 2) 10 else 0 // } else { -// constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 10) -// } -// if (index == views.size - 1) { -// constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) +// 0 // } +// val margin2 = if (it is TextView) (10f * (2 - views.size)) else (-15f * views.indexOf(it)) +// constraints.setTranslationX(it.id, margin2) +// constraints.connect(it.id, ConstraintSet.START, (previousItem as View).id, ConstraintSet.END, margin) // } -// -// val viewIds = views.map { it.id }.toIntArray() -// -// if (viewIds.size > 1) { -// constraints.createHorizontalChain( -// ConstraintSet.PARENT_ID, -// ConstraintSet.LEFT, -// ConstraintSet.PARENT_ID, -// ConstraintSet.RIGHT, -// viewIds, -// null, -// ConstraintSet.CHAIN_SPREAD -// ) +// if (isLastItem) { +// constraints.connect(it.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) // } -// -// constraints.applyTo(container) +// previousItem = it +// i++ // } private fun animateOmnibarOut(views: List): AnimatorSet { @@ -415,6 +445,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { .addTransition(transitionSlide) TransitionManager.beginDelayedTransition(viewGroup, transitionSet) + viewGroup.alpha = 1f views.map { it.visibility = View.VISIBLE it.alpha = 1f @@ -441,7 +472,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { fun stopPulseAnimation() { if (pulseAnimation.isRunning) { - pulseAnimation.cancel() + pulseAnimation.end() } } diff --git a/app/src/main/res/layout/include_omnibar_toolbar.xml b/app/src/main/res/layout/include_omnibar_toolbar.xml index 0eb295390b98..f637edec915b 100644 --- a/app/src/main/res/layout/include_omnibar_toolbar.xml +++ b/app/src/main/res/layout/include_omnibar_toolbar.xml @@ -57,6 +57,18 @@ android:paddingStart="6dp" android:paddingEnd="6dp"> + + + - - - Date: Mon, 11 May 2020 13:45:57 +0100 Subject: [PATCH 03/19] Add stacked logo --- .../browser/BrowserTrackersAnimatorHelper.kt | 133 ++++++++---------- .../app/global/view/ViewExtension.kt | 1 + 2 files changed, 57 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt index 230c3f0ca1e4..7b1dc2f8843e 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt @@ -133,28 +133,31 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { resourcesId: List ): List { return resourcesId.mapIndexed { index, it -> - return@mapIndexed if (it.resId == R.drawable.other_tracker_bg) { - val frameLayout = createTrackerTextLogo(activity, it, index) - container.addView(frameLayout) - frameLayout - } else { - val imageView = createTrackerImageLogo(activity, it, index) - container.addView(imageView) - imageView + return@mapIndexed when (it) { + is TrackerLogo.ImageLogo -> { + val imageView = createTrackerImageLogo(activity, it, index) + container.addView(imageView) + imageView + } + is TrackerLogo.LetterLogo -> { + val frameLayout = createTrackerTextLogo(activity, it, index) + container.addView(frameLayout) + frameLayout + } + is TrackerLogo.StackedLogo -> { + val imageView = createTrackerStackedLogo(activity, it, index) + container.addView(imageView) + imageView + } } } } - private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo, index: Int): FrameLayout { + private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo.LetterLogo, index: Int): FrameLayout { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) params.gravity = Gravity.CENTER val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) -// frameLayoutParams.gravity = Gravity.RIGHT -// if (index == 0) { -// frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) -// } else { -// frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) -// } + val frameLayout = FrameLayout(activity) frameLayout.alpha = 0f frameLayout.visibility = View.INVISIBLE @@ -180,15 +183,33 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { return frameLayout } - private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo, index: Int): FrameLayout { + private fun createTrackerStackedLogo(activity: Activity, trackerLogo: TrackerLogo.StackedLogo, index: Int): FrameLayout { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) -// frameLayoutParams.gravity = Gravity.RIGHT -// if (index == 0) { -// frameLayoutParams.setMargins(25.toPx(), 0, 0, 0) -// } else { -// frameLayoutParams.setMargins((-4).toPx(), 0, 0, 0) -// } + + params.gravity = Gravity.CENTER + val frameLayout = FrameLayout(activity) + frameLayout.alpha = 0f + frameLayout.visibility = View.INVISIBLE + frameLayout.id = View.generateViewId() + frameLayout.layoutParams = frameLayoutParams + frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) + + val imageView = ImageView(activity) + imageView.scaleType = ImageView.ScaleType.CENTER_CROP + imageView.setBackgroundResource(trackerLogo.resId) + imageView.id = View.generateViewId() + imageView.layoutParams = params + + frameLayout.addView(imageView) + + return frameLayout + } + + private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo.ImageLogo, index: Int): FrameLayout { + val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + params.gravity = Gravity.CENTER val frameLayout = FrameLayout(activity) frameLayout.alpha = 0f @@ -218,9 +239,9 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { .map { val res = TrackersRenderer().networkLogoIcon(activity, it.name) if (res == null) { - TrackerLogo(R.drawable.other_tracker_bg, it.displayName.take(1)) + TrackerLogo.LetterLogo(it.displayName.take(1)) } else { - TrackerLogo(res) + TrackerLogo.ImageLogo(res) } } .toMutableList() @@ -230,7 +251,7 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } else { resourcesList.take(MAX_LOGOS_SHOWN - 1) .toMutableList() - .apply { add(TrackerLogo(R.drawable.ic_more_trackers)) } + .apply { add(TrackerLogo.StackedLogo()) } } } @@ -263,8 +284,6 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { .before(animateOmnibarIn(omnibarViews)) addListener( onEnd = { - Timber.d("MARCOS DO ON END") - //container.removeAllViews() container.alpha = 0f listener?.onAnimationFinished() } @@ -272,25 +291,6 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } -// private fun createFullTrackersAnimation( -// container: ViewGroup, -// omnibarViews: List, -// logoViews: List -// ): AnimatorSet { -// return AnimatorSet().apply { -// play(createPartialTrackersAnimation(container, omnibarViews, logoViews)) -// play(animateFadeOut(container)) -// .after(TRACKER_LOGOS_DELAY_ON_SCREEN) -// .before(animateOmnibarIn(omnibarViews)) -// addListener( -// onEnd = { -// Timber.d("MARCOS DO ON END") -// container.removeViews(1, container.children.count() - 1) -// } -// ) -// } -// } - private fun createPartialTrackersAnimation( container: ConstraintLayout, omnibarViews: List, @@ -298,14 +298,10 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { ): AnimatorSet { applyConstraintSet(container, logoViews) val fadeOmnibarOut = animateOmnibarOut(omnibarViews) - //val fadeLogosIn = animateOmnibarMoveToRight(logoViews) - val fadeLogosIn = animateOmnibarMoveToRight(logoViews) animateMoveToRight(container, logoViews) - //animateMoveToRight(container, logoViews) animateBlockedLogos(logoViews) return AnimatorSet().apply { - //play(fadeLogosIn).after(fadeOmnibarOut) play(fadeOmnibarOut) } } @@ -324,6 +320,9 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 0) } if (index == views.size - 1) { + if (views.size == 4) { + constraints.setTranslationX(view.id, (-11.5f.toPx() * index).toFloat()) + } constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) } } @@ -349,31 +348,6 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { constraints.applyTo(container) } -// var previousItem: View? = null -// var i = 1 -// views.map { -// val isLastItem = views.indexOf(it) == views.size - 1 -// constraints.connect(it.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) -// constraints.connect(it.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) -// if (previousItem == null) { -// constraints.connect(it.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) -// } else { -// val margin = if (it is TextView) { -// if (views.size == 2) 10 else 0 -// } else { -// 0 -// } -// val margin2 = if (it is TextView) (10f * (2 - views.size)) else (-15f * views.indexOf(it)) -// constraints.setTranslationX(it.id, margin2) -// constraints.connect(it.id, ConstraintSet.START, (previousItem as View).id, ConstraintSet.END, margin) -// } -// if (isLastItem) { -// constraints.connect(it.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) -// } -// previousItem = it -// i++ -// } - private fun animateOmnibarOut(views: List): AnimatorSet { val animators = views.map { animateFadeOut(it) @@ -450,7 +424,6 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { it.visibility = View.VISIBLE it.alpha = 1f } - } fun pulseAnimation(view: View) { @@ -501,4 +474,10 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } -data class TrackerLogo(val resId: Int, val trackerLetter: String = "") \ No newline at end of file +sealed class TrackerLogo(val resId: Int) { + class ImageLogo(resId: Int) : TrackerLogo(resId) + + class LetterLogo(val trackerLetter: String = "", resId: Int = R.drawable.other_tracker_bg) : TrackerLogo(resId) + + class StackedLogo(resId: Int = R.drawable.other_tracker_bg) : TrackerLogo(resId) +} diff --git a/app/src/main/java/com/duckduckgo/app/global/view/ViewExtension.kt b/app/src/main/java/com/duckduckgo/app/global/view/ViewExtension.kt index 278f617a4dc7..368eb7e473ad 100644 --- a/app/src/main/java/com/duckduckgo/app/global/view/ViewExtension.kt +++ b/app/src/main/java/com/duckduckgo/app/global/view/ViewExtension.kt @@ -81,3 +81,4 @@ fun View.hideKeyboard(): Boolean { fun Int.toDp(): Int = (this / Resources.getSystem().displayMetrics.density).toInt() fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).toInt() +fun Float.toPx(): Float = (this * Resources.getSystem().displayMetrics.density) From 89e849e3aa5d31c80d2aa9c00023bac359c351bd Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Mon, 11 May 2020 15:57:54 +0100 Subject: [PATCH 04/19] Clean up code --- .../app/browser/BrowserTabFragment.kt | 8 +- .../browser/BrowserTrackersAnimatorHelper.kt | 288 ++++++++---------- 2 files changed, 132 insertions(+), 164 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 336dd359fae9..a360d16368ca 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -796,7 +796,6 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi } viewModel.privacyGradeState.observe(viewLifecycleOwner, Observer { - Timber.d("MARCOS Observed grade: $it") if (it.canShowPrivacyGrade) { val drawable = context?.getDrawable(it.privacyGrade.icon()) privacyGradeButton?.setImageDrawable(drawable) @@ -1270,7 +1269,6 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi fun omnibarViews(): List = listOf(clearTextButton, omnibarTextInput, searchIcon) override fun onAnimationFinished() { - Timber.d("MARCOS animation finished") viewModel.updatePrivacyGrade() } @@ -1614,7 +1612,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi createTrackersAnimation() animatorHelper.stopPulseAnimation() } else { - animatorHelper.pulseAnimation(privacyGradeButton) + animatorHelper.startPulseAnimation(privacyGradeButton) viewModel.stopShowPrivacyGrade() } } @@ -1628,7 +1626,6 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi delay(TRACKERS_SECONDARY_DELAY) if (lastSeenOmnibarViewState?.isEditing != true) { val site = viewModel.siteLiveData.value - val grade = viewModel.privacyGradeState.value?.privacyGrade val events = site?.orderedTrackingEntities() activity?.let { activity -> @@ -1637,8 +1634,7 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi activity, animationContainer, omnibarViews(), - events, - grade + events ) } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt index 7b1dc2f8843e..10ae7b79933f 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt @@ -20,10 +20,10 @@ import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.PropertyValuesHolder import android.app.Activity +import android.content.Context import android.view.Gravity import android.view.View import android.view.ViewGroup -import android.view.animation.LinearInterpolator import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView @@ -34,32 +34,30 @@ import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.view.children import androidx.core.widget.TextViewCompat -import androidx.transition.Fade -import androidx.transition.Slide -import androidx.transition.TransitionManager -import androidx.transition.TransitionSet import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.duckduckgo.app.cta.ui.Cta import com.duckduckgo.app.cta.ui.DaxDialogCta import com.duckduckgo.app.global.view.toPx -import com.duckduckgo.app.privacy.model.PrivacyGrade import com.duckduckgo.app.privacy.renderer.TrackersRenderer import com.duckduckgo.app.trackerdetection.model.Entity -import timber.log.Timber -import kotlin.math.log - interface TrackersAnimatorListener { fun onAnimationFinished() } -class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { +class BrowserTrackersAnimatorHelper(private val privacyGradeButton: ImageButton) { private var trackersAnimation: AnimatorSet = AnimatorSet() private var pulseAnimation: AnimatorSet = AnimatorSet() private var listener: TrackersAnimatorListener? = null - fun startTrackersAnimation(cta: Cta?, activity: Activity, container: ConstraintLayout, omnibarViews: List, entities: List?, grade: PrivacyGrade?) { + fun startTrackersAnimation( + cta: Cta?, + activity: Activity, + container: ConstraintLayout, + omnibarViews: List, + entities: List? + ) { if (entities.isNullOrEmpty()) { listener?.onAnimationFinished() return @@ -80,13 +78,36 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { start() } } else { - createFullTrackersAnimation(container, omnibarViews, logoViews, grade).apply { + createFullTrackersAnimation(container, omnibarViews, logoViews).apply { start() } } } } + fun startPulseAnimation(view: View) { + if (!pulseAnimation.isRunning) { + val scaleDown = ObjectAnimator.ofPropertyValuesHolder( + view, + PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f), + PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f) + ) + scaleDown.repeatCount = ObjectAnimator.INFINITE + scaleDown.duration = PULSE_ANIMATION_DURATION + + pulseAnimation = AnimatorSet().apply { + play(scaleDown) + start() + } + } + } + + fun stopPulseAnimation() { + if (pulseAnimation.isRunning) { + pulseAnimation.end() + } + } + fun removeListener() { listener = null } @@ -96,27 +117,17 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } fun cancelAnimations() { - if (trackersAnimation.isRunning) { - trackersAnimation.end() - } + stopTrackersAnimation() stopPulseAnimation() } - fun areAnimationsRunning() = trackersAnimation.isRunning - fun finishTrackerAnimation(fadeInViews: List, container: ViewGroup) { val animateOmnibarIn = animateOmnibarIn(fadeInViews) - val animateLogosOut = animateFadeOut(container) + val animateContainerOut = animateFadeOut(container) trackersAnimation = AnimatorSet().apply { - play(animateLogosOut).before(animateOmnibarIn) + play(animateContainerOut).before(animateOmnibarIn) start() - addListener( - onEnd = { - Timber.d("MARCOS DO ON END") -// container.removeViews(1, container.children.count() - 1) - listener?.onAnimationFinished() - } - ) + addListener(onEnd = { listener?.onAnimationFinished() }) } } @@ -132,20 +143,20 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { container: ViewGroup, resourcesId: List ): List { - return resourcesId.mapIndexed { index, it -> - return@mapIndexed when (it) { + return resourcesId.map { + return@map when (it) { is TrackerLogo.ImageLogo -> { - val imageView = createTrackerImageLogo(activity, it, index) + val imageView = createTrackerImageLogo(activity, it) container.addView(imageView) imageView } is TrackerLogo.LetterLogo -> { - val frameLayout = createTrackerTextLogo(activity, it, index) + val frameLayout = createTrackerTextLogo(activity, it) container.addView(frameLayout) frameLayout } is TrackerLogo.StackedLogo -> { - val imageView = createTrackerStackedLogo(activity, it, index) + val imageView = createTrackerStackedLogo(activity, it) container.addView(imageView) imageView } @@ -153,79 +164,81 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } - private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo.LetterLogo, index: Int): FrameLayout { + private fun getParams(): FrameLayout.LayoutParams { val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) params.gravity = Gravity.CENTER - val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) + return params + } + + private fun createAnimatedDrawable(context: Context): AnimatedVectorDrawableCompat? { + return AnimatedVectorDrawableCompat.create(context, R.drawable.network_cross_anim) + } - val frameLayout = FrameLayout(activity) + private fun createFrameLayoutContainer(context: Context): FrameLayout { + val frameLayoutParams = getParams() + val frameLayout = FrameLayout(context) frameLayout.alpha = 0f - frameLayout.visibility = View.INVISIBLE frameLayout.id = View.generateViewId() frameLayout.layoutParams = frameLayoutParams frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) + return frameLayout + } + + private fun createImageView(context: Context, resId: Int): ImageView { + val frameLayoutParams = getParams() + val imageView = ImageView(context) + imageView.scaleType = ImageView.ScaleType.CENTER_CROP + imageView.setBackgroundResource(resId) + imageView.id = View.generateViewId() + imageView.layoutParams = frameLayoutParams + return imageView + } + + private fun createTextView(context: Context): AppCompatTextView { + val frameLayoutParams = getParams() + + val textView = AppCompatTextView(context) + textView.gravity = Gravity.CENTER + TextViewCompat.setTextAppearance(textView, R.style.UnknownTrackerText) + textView.layoutParams = frameLayoutParams + + return textView + } + + private fun createTrackerTextLogo(activity: Activity, trackerLogo: TrackerLogo.LetterLogo): FrameLayout { + val frameLayoutParams = getParams() + val animatedDrawable = createAnimatedDrawable(activity) val animationView = ImageView(activity) - val animatedDrawable = AnimatedVectorDrawableCompat.create(activity, R.drawable.network_cross_anim) animationView.setImageDrawable(animatedDrawable) - animationView.layoutParams = params + animationView.layoutParams = frameLayoutParams - val textView = AppCompatTextView(activity) - textView.gravity = Gravity.CENTER + val textView = createTextView(activity) textView.setBackgroundResource(trackerLogo.resId) - TextViewCompat.setTextAppearance(textView, R.style.UnknownTrackerText) textView.text = trackerLogo.trackerLetter - textView.layoutParams = params + val frameLayout = createFrameLayoutContainer(activity) frameLayout.addView(textView) frameLayout.addView(animationView) return frameLayout } - private fun createTrackerStackedLogo(activity: Activity, trackerLogo: TrackerLogo.StackedLogo, index: Int): FrameLayout { - val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - - params.gravity = Gravity.CENTER - val frameLayout = FrameLayout(activity) - frameLayout.alpha = 0f - frameLayout.visibility = View.INVISIBLE - frameLayout.id = View.generateViewId() - frameLayout.layoutParams = frameLayoutParams - frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) - - val imageView = ImageView(activity) - imageView.scaleType = ImageView.ScaleType.CENTER_CROP - imageView.setBackgroundResource(trackerLogo.resId) - imageView.id = View.generateViewId() - imageView.layoutParams = params + private fun createTrackerStackedLogo(activity: Activity, trackerLogo: TrackerLogo.StackedLogo): FrameLayout { + val imageView = createImageView(activity, trackerLogo.resId) + val frameLayout = createFrameLayoutContainer(activity) frameLayout.addView(imageView) return frameLayout } - private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo.ImageLogo, index: Int): FrameLayout { - val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - val frameLayoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT) - - params.gravity = Gravity.CENTER - val frameLayout = FrameLayout(activity) - frameLayout.alpha = 0f - frameLayout.visibility = View.INVISIBLE - frameLayout.id = View.generateViewId() - frameLayout.layoutParams = frameLayoutParams - frameLayout.setBackgroundResource(R.drawable.background_tracker_logo) - - val imageView = ImageView(activity) - imageView.scaleType = ImageView.ScaleType.CENTER_CROP - val animatedDrawable = AnimatedVectorDrawableCompat.create(activity, R.drawable.network_cross_anim) + private fun createTrackerImageLogo(activity: Activity, trackerLogo: TrackerLogo.ImageLogo): FrameLayout { + val imageView = createImageView(activity, trackerLogo.resId) + val animatedDrawable = createAnimatedDrawable(activity) imageView.setImageDrawable(animatedDrawable) - imageView.setBackgroundResource(trackerLogo.resId) - imageView.id = View.generateViewId() - imageView.layoutParams = params + val frameLayout = createFrameLayoutContainer(activity) frameLayout.addView(imageView) return frameLayout @@ -274,12 +287,11 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { private fun createFullTrackersAnimation( container: ConstraintLayout, omnibarViews: List, - logoViews: List, - grade: PrivacyGrade? + logoViews: List ): AnimatorSet { return AnimatorSet().apply { play(createPartialTrackersAnimation(container, omnibarViews, logoViews)) - play(animateOmnibarMoveToLeft(logoViews)) + play(animateLogosSlideOut(logoViews)) .after(TRACKER_LOGOS_DELAY_ON_SCREEN) .before(animateOmnibarIn(omnibarViews)) addListener( @@ -297,12 +309,13 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { logoViews: List ): AnimatorSet { applyConstraintSet(container, logoViews) + container.alpha = 1f val fadeOmnibarOut = animateOmnibarOut(omnibarViews) - animateMoveToRight(container, logoViews) + val slideInLogos = animateLogosSlideIn(logoViews) animateBlockedLogos(logoViews) return AnimatorSet().apply { - play(fadeOmnibarOut) + play(slideInLogos).after(fadeOmnibarOut) } } @@ -314,22 +327,20 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { constraints.connect(view.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) constraints.connect(view.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) if (index == 0) { - constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 25.toPx()) + constraints.connect(view.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, FIRST_LOGO_MARGIN.toPx()) } else { - constraints.setTranslationX(view.id, (-7.toPx() * index).toFloat()) + constraints.setTranslationX(view.id, (NORMAL_LOGO_MARGIN.toPx() * index)) constraints.connect(view.id, ConstraintSet.START, views[index - 1].id, ConstraintSet.END, 0) } if (index == views.size - 1) { - if (views.size == 4) { - constraints.setTranslationX(view.id, (-11.5f.toPx() * index).toFloat()) + if (views.size == MAX_LOGOS_SHOWN) { + constraints.setTranslationX(view.id, (STACKED_LOGO_MARGIN.toPx() * index)) } constraints.connect(view.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) } } - views.reversed().map { - it.bringToFront() - } + views.reversed().map { it.bringToFront() } val viewIds = views.map { it.id }.toIntArray() @@ -348,6 +359,14 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { constraints.applyTo(container) } + private fun calculateMarginInPx(position: Int): Int = START_MARGIN_IN_DP.toPx() + (position * LOGO_SIZE_IN_DP.toPx()) + + private fun stopTrackersAnimation() { + if (trackersAnimation.isRunning) { + trackersAnimation.end() + } + } + private fun animateOmnibarOut(views: List): AnimatorSet { val animators = views.map { animateFadeOut(it) @@ -366,89 +385,44 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } - private fun animateOmnibarMoveToRight(views: List): AnimatorSet { -// val animators = views.map { -// animateMoveToRight2(it) -// } - val animators2 = views.map { + private fun animateLogosSlideIn(views: List): AnimatorSet { + val initialMargin = (views.first().layoutParams as ConstraintLayout.LayoutParams).marginStart.toFloat() + val slideInAnimators = views.mapIndexed { index, it -> + val margin = calculateMarginInPx(index) + initialMargin + animateSlideIn(it, margin) + } + val fadeInAnimators = views.map { animateFadeIn(it) } return AnimatorSet().apply { - playTogether(animators2) + playTogether(slideInAnimators + fadeInAnimators) } } - private fun animateOmnibarMoveToLeft(views: List): AnimatorSet { - val animators = views.map { - animateMoveToLeft(it) + private fun animateLogosSlideOut(views: List): AnimatorSet { + val slideOutAnimators = views.map { + animateSlideOut(it) } - val animators2 = views.map { + val fadeOutAnimators = views.map { animateFadeOut(it) } return AnimatorSet().apply { - playTogether(animators + animators2) + playTogether(slideOutAnimators + fadeOutAnimators) } } - private fun animateMoveToRight2(view: View): ObjectAnimator { - return ObjectAnimator.ofFloat(view, "x", view.left.toFloat(), view.left.toFloat()).apply { + private fun animateSlideIn(view: View, margin: Float): ObjectAnimator { + return ObjectAnimator.ofFloat(view, "x", 0f, margin + view.translationX).apply { duration = DEFAULT_ANIMATION_DURATION } } - private fun animateMoveToLeft(view: View): ObjectAnimator { + private fun animateSlideOut(view: View): ObjectAnimator { return ObjectAnimator.ofFloat(view, "x", 0f).apply { duration = DEFAULT_ANIMATION_DURATION } } - private fun animateMoveToRight(viewGroup: ViewGroup, views: List) { - val transitionSet = TransitionSet() - val transitionSlide = Slide(Gravity.START) - transitionSlide.duration = DEFAULT_ANIMATION_DURATION - transitionSlide.interpolator = LinearInterpolator() - - val transitionFadeIn = Fade(Fade.IN) - transitionFadeIn.duration = DEFAULT_ANIMATION_DURATION - transitionFadeIn.interpolator = LinearInterpolator() - - transitionSet.ordering = TransitionSet.ORDERING_TOGETHER - - transitionSet - .addTransition(transitionFadeIn) - .addTransition(transitionSlide) - - TransitionManager.beginDelayedTransition(viewGroup, transitionSet) - viewGroup.alpha = 1f - views.map { - it.visibility = View.VISIBLE - it.alpha = 1f - } - } - - fun pulseAnimation(view: View) { - if (!pulseAnimation.isRunning) { - val scaleDown = ObjectAnimator.ofPropertyValuesHolder( - view, - PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f), - PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f) - ) - scaleDown.repeatCount = ObjectAnimator.INFINITE - scaleDown.duration = 750 - - pulseAnimation = AnimatorSet().apply { - play(scaleDown) - start() - } - } - } - - fun stopPulseAnimation() { - if (pulseAnimation.isRunning) { - pulseAnimation.end() - } - } - private fun animateFadeOut(view: View, durationInMs: Long = DEFAULT_ANIMATION_DURATION): ObjectAnimator { return ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply { duration = durationInMs @@ -461,23 +435,21 @@ class BrowserTrackersAnimatorHelper(val privacyGradeButton: ImageButton) { } } - private fun animateExpand(view: View): ObjectAnimator { - return ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply { - duration = DEFAULT_ANIMATION_DURATION - } - } - companion object { private const val TRACKER_LOGOS_DELAY_ON_SCREEN = 2400L - private const val DEFAULT_ANIMATION_DURATION = 150L + private const val DEFAULT_ANIMATION_DURATION = 250L + private const val PULSE_ANIMATION_DURATION = 750L private const val MAX_LOGOS_SHOWN = 4 + private const val LOGO_SIZE_IN_DP = 26 + private const val START_MARGIN_IN_DP = 10 + private const val STACKED_LOGO_MARGIN = -11.5f + private const val NORMAL_LOGO_MARGIN = -7f + private const val FIRST_LOGO_MARGIN = 25 } } sealed class TrackerLogo(val resId: Int) { class ImageLogo(resId: Int) : TrackerLogo(resId) - class LetterLogo(val trackerLetter: String = "", resId: Int = R.drawable.other_tracker_bg) : TrackerLogo(resId) - class StackedLogo(resId: Int = R.drawable.other_tracker_bg) : TrackerLogo(resId) } From 2df2c782402a2ffc227d153b258290516e5a3cab Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Tue, 12 May 2020 15:23:53 +0100 Subject: [PATCH 05/19] Amend animation --- .../app/browser/BrowserTabViewModel.kt | 2 -- .../browser/BrowserTrackersAnimatorHelper.kt | 8 +++---- .../app/privacy/ui/TrackerNetworksAdapter.kt | 2 +- .../main/res/drawable/network_cross_anim.xml | 10 ++++---- .../other_tracker_privacy_dashboard_bg.xml | 23 +++++++++++++++++++ .../layout/item_tracker_network_header.xml | 2 +- app/src/main/res/values/styles.xml | 2 +- 7 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 app/src/main/res/drawable/other_tracker_privacy_dashboard_bg.xml diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 29a9e6fe50ba..d01da0179c1f 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -626,7 +626,6 @@ class BrowserTabViewModel( } private fun onSiteChanged() { - Timber.d("MARCOS On Site Changed") httpsUpgraded = false viewModelScope.launch { val improvedGrade = withContext(dispatchers.io()) { @@ -645,7 +644,6 @@ class BrowserTabViewModel( } fun updatePrivacyGrade() { - Timber.d("MARCOS update privacy grade") privacyGradeState.value = currentPrivacyGradeState().copy(canShowPrivacyGrade = true) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt index 10ae7b79933f..9c96974f79ec 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTrackersAnimatorHelper.kt @@ -89,8 +89,8 @@ class BrowserTrackersAnimatorHelper(private val privacyGradeButton: ImageButton) if (!pulseAnimation.isRunning) { val scaleDown = ObjectAnimator.ofPropertyValuesHolder( view, - PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f), - PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f) + PropertyValuesHolder.ofFloat("scaleX", 1f, 0.9f, 1f), + PropertyValuesHolder.ofFloat("scaleY", 1f, 0.9f, 1f) ) scaleDown.repeatCount = ObjectAnimator.INFINITE scaleDown.duration = PULSE_ANIMATION_DURATION @@ -437,8 +437,8 @@ class BrowserTrackersAnimatorHelper(private val privacyGradeButton: ImageButton) companion object { private const val TRACKER_LOGOS_DELAY_ON_SCREEN = 2400L - private const val DEFAULT_ANIMATION_DURATION = 250L - private const val PULSE_ANIMATION_DURATION = 750L + private const val DEFAULT_ANIMATION_DURATION = 150L + private const val PULSE_ANIMATION_DURATION = 1500L private const val MAX_LOGOS_SHOWN = 4 private const val LOGO_SIZE_IN_DP = 26 private const val START_MARGIN_IN_DP = 10 diff --git a/app/src/main/java/com/duckduckgo/app/privacy/ui/TrackerNetworksAdapter.kt b/app/src/main/java/com/duckduckgo/app/privacy/ui/TrackerNetworksAdapter.kt index df9418dc3020..ca462fb18e1a 100644 --- a/app/src/main/java/com/duckduckgo/app/privacy/ui/TrackerNetworksAdapter.kt +++ b/app/src/main/java/com/duckduckgo/app/privacy/ui/TrackerNetworksAdapter.kt @@ -104,7 +104,7 @@ class TrackerNetworksAdapter : RecyclerView.Adapter() { holder.icon.show() holder.unknownIcon.gone() } else { - val drawable = Theming.getThemedDrawable(holder.icon.context, R.drawable.other_tracker_bg, DuckDuckGoTheme.LIGHT) + val drawable = Theming.getThemedDrawable(holder.icon.context, R.drawable.other_tracker_privacy_dashboard_bg, DuckDuckGoTheme.LIGHT) holder.unknownIcon.text = viewElement.networkDisplayName.take(1) holder.unknownIcon.background = drawable holder.unknownIcon.show() diff --git a/app/src/main/res/drawable/network_cross_anim.xml b/app/src/main/res/drawable/network_cross_anim.xml index 4f7aa1b641fd..68ccb701e41d 100644 --- a/app/src/main/res/drawable/network_cross_anim.xml +++ b/app/src/main/res/drawable/network_cross_anim.xml @@ -50,7 +50,7 @@ android:duration="20" android:interpolator="@android:interpolator/linear" android:propertyName="strokeWidth" - android:startOffset="800" + android:startOffset="1000" android:valueFrom="0" android:valueTo="2" android:valueType="floatType" /> @@ -58,7 +58,7 @@ android:duration="200" android:interpolator="@android:interpolator/linear" android:propertyName="pathData" - android:startOffset="800" + android:startOffset="1000" android:valueFrom="M 10 10 L 10 10" android:valueTo="M 2 2 L 20 20" android:valueType="pathType" /> @@ -80,7 +80,7 @@ android:duration="20" android:interpolator="@android:interpolator/linear" android:propertyName="strokeWidth" - android:startOffset="800" + android:startOffset="1000" android:valueFrom="0" android:valueTo="6" android:valueType="floatType" /> @@ -88,7 +88,7 @@ android:duration="200" android:interpolator="@android:interpolator/linear" android:propertyName="pathData" - android:startOffset="800" + android:startOffset="1000" android:valueFrom="M 10 10 L 10 10" android:valueTo="M 2 2 L 20 20" android:valueType="pathType" /> @@ -96,7 +96,7 @@ android:duration="200" android:interpolator="@android:interpolator/linear" android:propertyName="strokeAlpha" - android:startOffset="800" + android:startOffset="1000" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" /> diff --git a/app/src/main/res/drawable/other_tracker_privacy_dashboard_bg.xml b/app/src/main/res/drawable/other_tracker_privacy_dashboard_bg.xml new file mode 100644 index 000000000000..4dfbb3d19c56 --- /dev/null +++ b/app/src/main/res/drawable/other_tracker_privacy_dashboard_bg.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_tracker_network_header.xml b/app/src/main/res/layout/item_tracker_network_header.xml index 04b62d2c2a9f..1eadea74a064 100644 --- a/app/src/main/res/layout/item_tracker_network_header.xml +++ b/app/src/main/res/layout/item_tracker_network_header.xml @@ -57,7 +57,7 @@ android:id="@+id/unknownIcon" android:layout_width="24dp" android:layout_height="24dp" - android:background="@drawable/other_tracker_bg" + android:background="@drawable/other_tracker_privacy_dashboard_bg" android:gravity="center" android:importantForAccessibility="no" android:textAppearance="@style/UnknownTrackerText" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 02035dad9e79..d00777c48ae7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -303,7 +303,7 @@ center bold ?attr/omnibarLogoLetterTextColor - 12sp + 13sp