Update animations

This commit is contained in:
Tommaso Berlose 2021-01-09 20:26:23 +01:00
parent 889783bb4e
commit e9effbe799
11 changed files with 146 additions and 111 deletions

Binary file not shown.

View File

@ -22,8 +22,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 30 targetSdkVersion 30
versionCode 115 versionCode 116
versionName "2.1.1" versionName "2.2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])

View File

@ -14,6 +14,7 @@ import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -28,6 +29,7 @@ import androidx.navigation.Navigation
import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.card.MaterialCardView
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
@ -48,7 +50,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment() { class MainFragment : Fragment() {
companion object { companion object {
fun newInstance() = MainFragment() fun newInstance() = MainFragment()
@ -71,21 +73,6 @@ class MainFragment : Fragment() {
): View { ): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java) viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentAppMainBinding.inflate(inflater) binding = FragmentAppMainBinding.inflate(inflater)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
subscribeUi(viewModel)
if (binding.preview.height == 0) {
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
}
}
// Warnings // Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) { if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
@ -125,6 +112,10 @@ class MainFragment : Fragment() {
requireContext() requireContext()
) else 0 ) else 0
} }
subscribeUi(viewModel)
return binding.root
} }
private fun subscribeUi(viewModel: MainViewModel) { private fun subscribeUi(viewModel: MainViewModel) {
@ -171,6 +162,10 @@ class MainFragment : Fragment() {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f binding.toolbar.cardElevation = if (it > 0) 24f else 0f
} }
viewModel.showPreview.observe(viewLifecycleOwner) {
updatePreviewVisibility()
}
viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) { viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) {
updateClock() updateClock()
} }
@ -236,10 +231,6 @@ class MainFragment : Fragment() {
} }
} }
} }
} else {
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = 0
}
} }
} }
@ -270,28 +261,57 @@ class MainFragment : Fragment() {
} }
private fun updateClockVisibility(showClock: Boolean) { private fun updateClockVisibility(showClock: Boolean) {
binding.preview.animation?.cancel() binding.widgetDetail.timeContainer.clearAnimation()
binding.widgetDetail.time.clearAnimation()
val clockInitialHeight = binding.widgetDetail.timeContainer.measuredHeight.toFloat() updatePreviewVisibility()
ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f
).apply {
this.duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.preview.layoutParams = binding.preview.layoutParams.apply { if (showClock) {
height = (PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + 100.toPixel(requireContext()) * animatedValue).toInt() binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
} height = RelativeLayout.LayoutParams.WRAP_CONTENT
binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
height = (clockInitialHeight * animatedValue).toInt()
}
binding.widgetDetail.timeContainer.translationY = (clockInitialHeight * animatedValue - clockInitialHeight)
binding.widgetDetail.timeContainer.alpha = animatedValue
} }
}.start() binding.widgetDetail.timeContainer.measure(0, 0)
}
if ((Preferences.showClock && binding.widgetDetail.time.alpha != 1f) || (!Preferences.showClock && binding.widgetDetail.time.alpha != 0f)) {
val initialHeight = binding.widgetDetail.timeContainer.measuredHeight
ValueAnimator.ofFloat(
if (showClock) 0f else 1f,
if (showClock) 1f else 0f
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.alpha = animatedValue
}
}.start()
}
}
private fun updatePreviewVisibility() {
binding.preview.clearAnimation()
if (binding.preview.layoutParams.height != (if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)) {
ValueAnimator.ofInt(
binding.preview.height,
(if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams
layoutParams.height = animatedValue
binding.preview.layoutParams = layoutParams
}
}.start()
}
} }
override fun onResume() { override fun onResume() {

View File

@ -95,7 +95,7 @@ class SettingsFragment : Fragment() {
private fun subscribeUi( private fun subscribeUi(
viewModel: MainViewModel, viewModel: MainViewModel,
) { ) {
viewModel.darkThemePreference.observe(viewLifecycleOwner, Observer { viewModel.darkThemePreference.observe(viewLifecycleOwner) {
AppCompatDelegate.setDefaultNightMode(it) AppCompatDelegate.setDefaultNightMode(it)
maintainScrollPosition { maintainScrollPosition {
binding.theme.text = when (it) { binding.theme.text = when (it) {
@ -106,29 +106,29 @@ class SettingsFragment : Fragment() {
else -> "" else -> ""
} }
} }
}) }
viewModel.installedIntegrations.observe(viewLifecycleOwner, Observer { viewModel.installedIntegrations.observe(viewLifecycleOwner) {
binding.integrationsCountLabel.text = binding.integrationsCountLabel.text =
getString(R.string.label_count_installed_integrations).format( getString(R.string.label_count_installed_integrations).format(
it) it)
}) }
viewModel.showPreview.observe(viewLifecycleOwner, Observer { viewModel.showPreview.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.showWidgetPreviewLabel.text = binding.showWidgetPreviewLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible) if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} }
}) }
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer { viewModel.showWallpaper.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.showWallpaperLabel.text = binding.showWallpaperLabel.text =
if (it && activity?.checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == true) getString( if (it && requireActivity().checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) getString(
R.string.settings_visible R.string.settings_visible
) else getString(R.string.settings_not_visible) ) else getString(R.string.settings_not_visible)
} }
}) }
} }
private fun setupListener() { private fun setupListener() {
@ -180,19 +180,19 @@ class SettingsFragment : Fragment() {
} }
binding.actionTranslate.setOnClickListener { binding.actionTranslate.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml") requireActivity().openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml")
} }
binding.actionWebsite.setOnClickListener { binding.actionWebsite.setOnClickListener {
activity?.openURI("http://tommasoberlose.com/") requireActivity().openURI("http://tommasoberlose.com/")
} }
binding.actionFeedback.setOnClickListener { binding.actionFeedback.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/issues") requireActivity().openURI("https://github.com/tommasoberlose/another-widget/issues")
} }
binding.actionPrivacyPolicy.setOnClickListener { binding.actionPrivacyPolicy.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/privacy-policy.md") requireActivity().openURI("https://github.com/tommasoberlose/another-widget/blob/master/privacy-policy.md")
} }
binding.actionHelpDev.setOnClickListener { binding.actionHelpDev.setOnClickListener {
@ -224,6 +224,11 @@ class SettingsFragment : Fragment() {
} }
} }
override fun onResume() {
super.onResume()
binding.showWallpaperToggle.setCheckedNoEvent(Preferences.showWallpaper && requireActivity().checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE))
}
private fun requirePermission() { private fun requirePermission() {
Dexter.withContext(requireContext()) Dexter.withContext(requireContext())
.withPermissions( .withPermissions(
@ -234,7 +239,7 @@ class SettingsFragment : Fragment() {
if (report.areAllPermissionsGranted()) { if (report.areAllPermissionsGranted()) {
Preferences.showWallpaper = true Preferences.showWallpaper = true
} else { } else {
binding.showWallpaperToggle.isChecked = false binding.showWallpaperToggle.setCheckedNoEvent(false)
} }
} }
} }

View File

@ -18,6 +18,7 @@ import android.view.animation.LayoutAnimationController
import android.widget.ImageView import android.widget.ImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -44,11 +45,11 @@ import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.*
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel import kotlinx.coroutines.Dispatchers
import com.tommasoberlose.anotherwidget.utils.expand
import com.tommasoberlose.anotherwidget.utils.reveal
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
@ -91,6 +92,7 @@ class GlanceTabFragment : Fragment() {
// List // List
binding.providersList.hasFixedSize() binding.providersList.hasFixedSize()
binding.providersList.isNestedScrollingEnabled = false
val mLayoutManager = LinearLayoutManager(context) val mLayoutManager = LinearLayoutManager(context)
binding.providersList.layoutManager = mLayoutManager binding.providersList.layoutManager = mLayoutManager
@ -172,7 +174,7 @@ class GlanceTabFragment : Fragment() {
)) ))
) View.VISIBLE else View.GONE ) View.VISIBLE else View.GONE
) )
isVisible = !(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong( isVisible = (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext() requireContext()
)) ))
} }
@ -343,12 +345,12 @@ class GlanceTabFragment : Fragment() {
GlanceProviderHelper.saveGlanceProviderOrder( GlanceProviderHelper.saveGlanceProviderOrder(
list list
) )
adapter.updateData(listOf("header") + list.mapNotNull { adapter.updateData(list.mapNotNull {
GlanceProviderHelper.getGlanceProviderById( GlanceProviderHelper.getGlanceProviderById(
requireContext(), requireContext(),
it it
) )
} + listOf("footer")) })
} }
override fun onChildDraw( override fun onChildDraw(
@ -412,15 +414,22 @@ class GlanceTabFragment : Fragment() {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY viewModel.fragmentScrollY.value = binding.scrollView.scrollY
} }
adapter.updateData(emptyList<Constants.GlanceProviderId>()) lifecycleScope.launch(Dispatchers.IO) {
lifecycleScope.launchWhenResumed { delay(500)
delay(800)
val l = list.mapNotNull { GlanceProviderHelper.getGlanceProviderById( val l = list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(
requireContext(), requireContext(),
it it
) } ) }
adapter.updateData(l) withContext(Dispatchers.Main) {
binding.listContainer.expand() binding.loader.animate().scaleX(0f).scaleY(0f).alpha(0f).start()
adapter.updateData(l)
val controller =
AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_fall_down)
binding.providersList.layoutAnimation = controller
adapter.notifyDataSetChanged()
binding.providersList.scheduleLayoutAnimation()
}
} }
} }
@ -495,7 +504,7 @@ class GlanceTabFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
adapter.notifyItemRangeChanged(1, adapter.data?.size ?: 0) adapter.notifyItemRangeChanged(0, adapter.data?.size ?: 0)
if (dialog != null) { if (dialog != null) {
dialog?.show() dialog?.show()
} }

View File

@ -94,6 +94,14 @@ class PreferencesFragment : Fragment() {
viewModel.showWeather.observe(viewLifecycleOwner) { viewModel.showWeather.observe(viewLifecycleOwner) {
checkWeatherProviderConfig() checkWeatherProviderConfig()
} }
viewModel.weatherProviderError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
} }
private fun setupListener() { private fun setupListener() {
@ -175,18 +183,10 @@ class PreferencesFragment : Fragment() {
} }
private fun checkWeatherProviderConfig() { private fun checkWeatherProviderConfig() {
if (Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" && !binding.weatherProviderError.isVisible) { binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.expand()
} else {
binding.weatherProviderError.collapse()
}
binding.weatherProviderError.text = Preferences.weatherProviderError binding.weatherProviderError.text = Preferences.weatherProviderError
if (Preferences.showWeather && Preferences.weatherProviderLocationError != "" && !binding.weatherProviderError.isVisible) { binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.expand()
} else {
binding.weatherProviderLocationError.collapse()
}
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
} }

View File

@ -149,18 +149,10 @@ class WeatherFragment : Fragment() {
} }
private fun checkWeatherProviderConfig() { private fun checkWeatherProviderConfig() {
if (Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" && !binding.locationPermissionAlert.isVisible) { binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.expand()
} else {
binding.weatherProviderError.collapse()
}
binding.weatherProviderError.text = Preferences.weatherProviderError binding.weatherProviderError.text = Preferences.weatherProviderError
if (Preferences.showWeather && Preferences.weatherProviderLocationError != "" && !binding.locationPermissionAlert.isVisible) { binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.expand()
} else {
binding.weatherProviderLocationError.collapse()
}
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
} }

View File

@ -154,9 +154,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationAdd)) { value = true } addSource(Preferences.asLiveData(Preferences::customLocationAdd)) { value = true }
addSource(Preferences.asLiveData(Preferences::showPreview)) { value = true }
addSource(Preferences.asLiveData(Preferences::enabledGlanceProviderOrder)) { value = true } addSource(Preferences.asLiveData(Preferences::enabledGlanceProviderOrder)) { value = true }
addSource(Preferences.asLiveData(Preferences::customNotes)) { value = true } addSource(Preferences.asLiveData(Preferences::customNotes)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextAlarm)) { value = true } addSource(Preferences.asLiveData(Preferences::showNextAlarm)) { value = true }

View File

@ -275,28 +275,28 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:duplicateParentState="true" android:duplicateParentState="true"
android:textSize="14sp" android:textSize="14sp"
android:paddingStart="64dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
android:visibility="gone" android:visibility="gone"
android:id="@+id/weather_provider_error" android:id="@+id/weather_provider_error"
android:textColor="@color/errorColorText" android:textColor="@color/errorColorText"
android:letterSpacing="0" android:letterSpacing="0"
android:paddingTop="8dp"
android:fontFamily="@font/google_sans_bold" android:fontFamily="@font/google_sans_bold"
android:textStyle="bold" android:textStyle="bold"
android:text="@string/weather_provider_error_missing_key"
android:textAppearance="@style/TextAppearance.MaterialComponents.Button" android:textAppearance="@style/TextAppearance.MaterialComponents.Button"
app:textAllCaps="false" /> app:textAllCaps="false" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:duplicateParentState="true" android:duplicateParentState="true"
android:textSize="14sp" android:textSize="14sp"
android:paddingTop="8dp"
android:id="@+id/weather_provider_location_error" android:id="@+id/weather_provider_location_error"
android:textColor="@color/errorColorText" android:textColor="@color/errorColorText"
android:letterSpacing="0" android:letterSpacing="0"
android:fontFamily="@font/google_sans_bold" android:fontFamily="@font/google_sans_bold"
android:textStyle="bold" android:textStyle="bold"
android:layout_marginStart="64dp" android:visibility="gone"
android:textAppearance="@style/TextAppearance.MaterialComponents.Button" android:textAppearance="@style/TextAppearance.MaterialComponents.Button"
app:textAllCaps="false" /> app:textAllCaps="false" />
</LinearLayout> </LinearLayout>

View File

@ -43,22 +43,34 @@
android:text="@string/settings_sort_glance_providers_subtitle" android:text="@string/settings_sort_glance_providers_subtitle"
android:textAppearance="@style/AnotherWidget.Settings.SubHeader"/> android:textAppearance="@style/AnotherWidget.Settings.SubHeader"/>
</LinearLayout> </LinearLayout>
<LinearLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:alpha="0" android:minHeight="64dp">
android:id="@+id/list_container" <ProgressBar
android:orientation="vertical"> android:layout_width="32dp"
<androidx.recyclerview.widget.RecyclerView android:layout_height="32dp"
android:layout_margin="16dp"
android:layout_centerInParent="true"
android:indeterminateTint="@color/colorAccent"
android:indeterminate="true"
android:id="@+id/loader" />
<RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:layout_weight="1" android:id="@+id/list_container">
android:clipChildren="false" <androidx.recyclerview.widget.RecyclerView
android:clipToPadding="false" android:layout_width="match_parent"
android:paddingTop="8dp" android:layout_height="wrap_content"
android:paddingBottom="8dp" android:layoutAnimation="@anim/layout_animation_fall_down"
android:id="@+id/providers_list" /> android:clipChildren="false"
</LinearLayout> android:clipToPadding="false"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:background="@color/colorPrimary"
android:id="@+id/providers_list" />
</RelativeLayout>
</RelativeLayout>
<LinearLayout android:layout_width="match_parent" <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/footer" android:id="@+id/footer"

View File

@ -80,12 +80,12 @@
android:paddingStart="64dp" android:paddingStart="64dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:visibility="gone"
android:id="@+id/weather_provider_error" android:id="@+id/weather_provider_error"
android:textColor="@color/errorColorText" android:textColor="@color/errorColorText"
android:letterSpacing="0" android:letterSpacing="0"
android:fontFamily="@font/google_sans_bold" android:fontFamily="@font/google_sans_bold"
android:textStyle="bold" android:textStyle="bold"
android:text="@string/weather_provider_error_missing_key"
android:textAppearance="@style/TextAppearance.MaterialComponents.Button" android:textAppearance="@style/TextAppearance.MaterialComponents.Button"
app:textAllCaps="false" /> app:textAllCaps="false" />
<LinearLayout <LinearLayout