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"
minSdkVersion 23
targetSdkVersion 30
versionCode 115
versionName "2.1.1"
versionCode 116
versionName "2.2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
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.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat
@ -28,6 +29,7 @@ import androidx.navigation.Navigation
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.NavHostFragment
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.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
@ -48,7 +50,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment() {
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
@ -71,21 +73,6 @@ class MainFragment : Fragment() {
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
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
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
@ -125,6 +112,10 @@ class MainFragment : Fragment() {
requireContext()
) else 0
}
subscribeUi(viewModel)
return binding.root
}
private fun subscribeUi(viewModel: MainViewModel) {
@ -171,6 +162,10 @@ class MainFragment : Fragment() {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f
}
viewModel.showPreview.observe(viewLifecycleOwner) {
updatePreviewVisibility()
}
viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) {
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) {
binding.preview.animation?.cancel()
binding.widgetDetail.timeContainer.clearAnimation()
binding.widgetDetail.time.clearAnimation()
val clockInitialHeight = binding.widgetDetail.timeContainer.measuredHeight.toFloat()
ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f
).apply {
this.duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
updatePreviewVisibility()
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = (PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + 100.toPixel(requireContext()) * animatedValue).toInt()
}
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
if (showClock) {
binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
}.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() {

View File

@ -95,7 +95,7 @@ class SettingsFragment : Fragment() {
private fun subscribeUi(
viewModel: MainViewModel,
) {
viewModel.darkThemePreference.observe(viewLifecycleOwner, Observer {
viewModel.darkThemePreference.observe(viewLifecycleOwner) {
AppCompatDelegate.setDefaultNightMode(it)
maintainScrollPosition {
binding.theme.text = when (it) {
@ -106,29 +106,29 @@ class SettingsFragment : Fragment() {
else -> ""
}
}
})
}
viewModel.installedIntegrations.observe(viewLifecycleOwner, Observer {
viewModel.installedIntegrations.observe(viewLifecycleOwner) {
binding.integrationsCountLabel.text =
getString(R.string.label_count_installed_integrations).format(
it)
})
}
viewModel.showPreview.observe(viewLifecycleOwner, Observer {
viewModel.showPreview.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showWidgetPreviewLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
}
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer {
viewModel.showWallpaper.observe(viewLifecycleOwner) {
maintainScrollPosition {
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
) else getString(R.string.settings_not_visible)
}
})
}
}
private fun setupListener() {
@ -180,19 +180,19 @@ class SettingsFragment : Fragment() {
}
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 {
activity?.openURI("http://tommasoberlose.com/")
requireActivity().openURI("http://tommasoberlose.com/")
}
binding.actionFeedback.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/issues")
requireActivity().openURI("https://github.com/tommasoberlose/another-widget/issues")
}
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 {
@ -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() {
Dexter.withContext(requireContext())
.withPermissions(
@ -234,7 +239,7 @@ class SettingsFragment : Fragment() {
if (report.areAllPermissionsGranted()) {
Preferences.showWallpaper = true
} 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 androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
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.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.expand
import com.tommasoberlose.anotherwidget.utils.reveal
import com.tommasoberlose.anotherwidget.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter
@ -91,6 +92,7 @@ class GlanceTabFragment : Fragment() {
// List
binding.providersList.hasFixedSize()
binding.providersList.isNestedScrollingEnabled = false
val mLayoutManager = LinearLayoutManager(context)
binding.providersList.layoutManager = mLayoutManager
@ -172,7 +174,7 @@ class GlanceTabFragment : Fragment() {
))
) View.VISIBLE else View.GONE
)
isVisible = !(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong(
isVisible = (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext()
))
}
@ -343,12 +345,12 @@ class GlanceTabFragment : Fragment() {
GlanceProviderHelper.saveGlanceProviderOrder(
list
)
adapter.updateData(listOf("header") + list.mapNotNull {
adapter.updateData(list.mapNotNull {
GlanceProviderHelper.getGlanceProviderById(
requireContext(),
it
)
} + listOf("footer"))
})
}
override fun onChildDraw(
@ -412,15 +414,22 @@ class GlanceTabFragment : Fragment() {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
adapter.updateData(emptyList<Constants.GlanceProviderId>())
lifecycleScope.launchWhenResumed {
delay(800)
lifecycleScope.launch(Dispatchers.IO) {
delay(500)
val l = list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(
requireContext(),
it
) }
adapter.updateData(l)
binding.listContainer.expand()
withContext(Dispatchers.Main) {
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() {
super.onResume()
adapter.notifyItemRangeChanged(1, adapter.data?.size ?: 0)
adapter.notifyItemRangeChanged(0, adapter.data?.size ?: 0)
if (dialog != null) {
dialog?.show()
}

View File

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

View File

@ -149,18 +149,10 @@ class WeatherFragment : Fragment() {
}
private fun checkWeatherProviderConfig() {
if (Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" && !binding.locationPermissionAlert.isVisible) {
binding.weatherProviderError.expand()
} else {
binding.weatherProviderError.collapse()
}
binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.text = Preferences.weatherProviderError
if (Preferences.showWeather && Preferences.weatherProviderLocationError != "" && !binding.locationPermissionAlert.isVisible) {
binding.weatherProviderLocationError.expand()
} else {
binding.weatherProviderLocationError.collapse()
}
binding.weatherProviderLocationError.isVisible = Preferences.showWeather && 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::customLocationAdd)) { value = true }
addSource(Preferences.asLiveData(Preferences::showPreview)) { value = true }
addSource(Preferences.asLiveData(Preferences::enabledGlanceProviderOrder)) { value = true }
addSource(Preferences.asLiveData(Preferences::customNotes)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextAlarm)) { value = true }

View File

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

View File

@ -43,22 +43,34 @@
android:text="@string/settings_sort_glance_providers_subtitle"
android:textAppearance="@style/AnotherWidget.Settings.SubHeader"/>
</LinearLayout>
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha="0"
android:id="@+id/list_container"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_height="wrap_content"
android:minHeight="64dp">
<ProgressBar
android:layout_width="32dp"
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_height="0dp"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:id="@+id/providers_list" />
</LinearLayout>
android:layout_height="wrap_content"
android:id="@+id/list_container">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/layout_animation_fall_down"
android:clipChildren="false"
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"
android:layout_height="wrap_content"
android:id="@+id/footer"

View File

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