From 331d5772afaf876193aed08218e221bd654a53d0 Mon Sep 17 00:00:00 2001 From: Tommaso Berlose Date: Fri, 16 Oct 2020 18:04:30 +0200 Subject: [PATCH] Added the shadow to the icons --- .idea/caches/build_file_checksums.ser | Bin 537 -> 537 bytes app/build.gradle | 2 + .../anotherwidget/helpers/ImageHelper.kt | 84 +++++++++++++ .../ui/activities/WeatherProviderActivity.kt | 8 ++ .../ui/fragments/MainFragment.kt | 1 + .../ui/fragments/WeatherTabFragment.kt | 26 ++-- .../anotherwidget/ui/widgets/MainWidget.kt | 38 +++++- .../anotherwidget/utils/Extensions.kt | 112 +++++++++++------- .../res/layout/fragment_weather_settings.xml | 96 +++++---------- app/src/main/res/layout/the_widget.xml | 77 ++++++++---- 10 files changed, 295 insertions(+), 149 deletions(-) create mode 100644 app/src/main/java/com/tommasoberlose/anotherwidget/helpers/ImageHelper.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index b7727100945c12f0d3beb30d916a3b77b29c6d7e..089332dccfd7287617c167534a9dff21b163bdd5 100644 GIT binary patch delta 33 rcmV++0N($Z1epYom;^C~IPtNZYylCjhWPu{qroh++3BDWxk}l11(p#R delta 33 rcmV++0N($Z1epYom;@&*Je#qcYylDY2~d|6qv-I7ie8PE6XTY6+RqMc diff --git a/app/build.gradle b/app/build.gradle index 9711042..7ff3258 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) + + renderscriptSupportModeEnabled true } buildTypes { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/ImageHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/ImageHelper.kt new file mode 100644 index 0000000..0bf1b61 --- /dev/null +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/ImageHelper.kt @@ -0,0 +1,84 @@ +package com.tommasoberlose.anotherwidget.helpers + +import android.content.Context +import android.graphics.* +import android.renderscript.* +import android.util.TypedValue +import android.widget.ImageView +import androidx.core.graphics.drawable.toBitmap +import com.tommasoberlose.anotherwidget.utils.isDarkTheme +import java.util.prefs.Preferences +import kotlin.math.min + +object ImageHelper { + fun ImageView.applyShadow(originalView: ImageView, factor: Float = 1f) { + clearColorFilter() + val cElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, when (if (context.isDarkTheme()) com.tommasoberlose.anotherwidget.global.Preferences.textShadowDark else com.tommasoberlose.anotherwidget.global.Preferences.textShadow) { + 0 -> 0f * factor + 1 -> 8f * factor + 2 -> 16f * factor + else -> 0f * factor + }, resources.displayMetrics) + + if (originalView.drawable != null) { + val btm = originalView.drawable.toBitmap().copy(Bitmap.Config.ARGB_8888, true) + val comb = Bitmap.createBitmap(btm) + val shadowBitmap = generateShadowBitmap(context, cElevation, btm, factor) + + shadowBitmap?.let { + val canvas = Canvas(comb) + canvas.drawColor(Color.TRANSPARENT) + canvas.save() + val rect = Rect() + val bounds = originalView.drawable.copyBounds() + canvas.getClipBounds(rect) + rect.inset(-2 * getBlurRadius(context, cElevation).toInt(), -2 * getBlurRadius(context, cElevation).toInt()) + canvas.save() + canvas.clipRect(rect) + canvas.drawBitmap(shadowBitmap, 0f, 2f, null) + canvas.restore() + setImageBitmap(comb) + } + } + } + + private fun generateShadowBitmap(context: Context, cElevation: Float, bitmap: Bitmap?, factor: Float): Bitmap? { + val rs: RenderScript = RenderScript.create(context) + val element = Element.U8_4(rs) + val blurScript: ScriptIntrinsicBlur = ScriptIntrinsicBlur.create(rs, element) + val colorMatrixScript: ScriptIntrinsicColorMatrix = ScriptIntrinsicColorMatrix.create(rs) + val allocationIn = Allocation.createFromBitmap(rs, bitmap) + val allocationOut = Allocation.createTyped(rs, allocationIn.type) + + val matrix = Matrix4f(floatArrayOf( + 0f, 0f, 0f, 0f, + 0f, 0f, 0f, 0f, + 0f, 0f, 0f, 0f, + 0f, 0f, 0f, when (if (context.isDarkTheme()) com.tommasoberlose.anotherwidget.global.Preferences.textShadowDark else com.tommasoberlose.anotherwidget.global.Preferences.textShadow) { + 0 -> 0f * factor + 1 -> 0.8f * factor + 2 -> 1f * factor + else -> 0f + })) + + colorMatrixScript.setColorMatrix(matrix) + colorMatrixScript.forEach(allocationIn, allocationOut) + + blurScript.setRadius(getBlurRadius(context, cElevation)) + + blurScript.setInput(allocationOut) + blurScript.forEach(allocationIn) + + allocationIn.copyTo(bitmap) + + allocationIn.destroy() + allocationOut.destroy() + + return bitmap + } + + private fun getBlurRadius(context: Context, customElevation: Float): Float { + val maxElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f, context.resources.displayMetrics) + return min(25f * (customElevation / maxElevation), 25f) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt index 1d67193..199ee5b 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/WeatherProviderActivity.kt @@ -66,6 +66,10 @@ class WeatherProviderActivity : AppCompatActivity() { injector .text(R.id.text, WeatherHelper.getProviderName(this, provider)) .clicked(R.id.item) { + if (Preferences.weatherProvider != provider.value) { + Preferences.weatherProviderError = "-" + Preferences.weatherProviderLocationError = "" + } val oldValue = Preferences.weatherProvider Preferences.weatherProvider = provider.value updateListItem(oldValue) @@ -77,6 +81,10 @@ class WeatherProviderActivity : AppCompatActivity() { } } .clicked(R.id.radioButton) { + if (Preferences.weatherProvider != provider.value) { + Preferences.weatherProviderError = "-" + Preferences.weatherProviderLocationError = "" + } val oldValue = Preferences.weatherProvider Preferences.weatherProvider = provider.value updateListItem(oldValue) diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt index d885c76..8e02583 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt @@ -354,6 +354,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION ) != true) || (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") + || (Preferences.weatherProviderLocationError != "") } else { false } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt index 0ab97d5..a71faf2 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt @@ -42,6 +42,8 @@ import com.tommasoberlose.anotherwidget.ui.activities.WeatherProviderActivity import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission +import com.tommasoberlose.anotherwidget.utils.collapse +import com.tommasoberlose.anotherwidget.utils.expand import kotlinx.android.synthetic.main.fragment_weather_settings.* import kotlinx.android.synthetic.main.fragment_weather_settings.scrollView import kotlinx.coroutines.delay @@ -87,11 +89,6 @@ class WeatherTabFragment : Fragment() { ) { binding.isWeatherVisible = Preferences.showWeather - viewModel.showWeatherWarning.observe(viewLifecycleOwner, Observer { - weather_warning?.isVisible = it - checkLocationPermission() - }) - viewModel.showWeather.observe(viewLifecycleOwner, Observer { maintainScrollPosition { show_weather_label?.text = @@ -100,6 +97,7 @@ class WeatherTabFragment : Fragment() { binding.isWeatherVisible = it } checkLocationPermission() + checkWeatherProviderConfig() }) viewModel.weatherProvider.observe(viewLifecycleOwner, Observer { @@ -174,7 +172,7 @@ class WeatherTabFragment : Fragment() { WeatherReceiver.setUpdates(requireContext()) } else if (Preferences.showWeather && Preferences.customLocationAdd == "") { location_permission_alert?.isVisible = true - background_location_warning.isVisible = false + background_location_warning.isVisible = true location_permission_alert?.setOnClickListener { MaterialBottomSheetDialog(requireContext(), message = getString(R.string.background_location_warning)) .setPositiveButton(getString(android.R.string.ok)) { @@ -188,18 +186,22 @@ class WeatherTabFragment : Fragment() { } private fun checkWeatherProviderConfig() { - weather_provider_error.isVisible = Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" + if (Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" && !location_permission_alert.isVisible) { + weather_provider_error.expand() + } else { + weather_provider_error.collapse() + } weather_provider_error?.text = Preferences.weatherProviderError - weather_provider_location_error.isVisible = Preferences.weatherProviderLocationError != "" + if (Preferences.showWeather && Preferences.weatherProviderLocationError != "" && !location_permission_alert.isVisible) { + weather_provider_location_error.expand() + } else { + weather_provider_location_error.collapse() + } weather_provider_location_error?.text = Preferences.weatherProviderLocationError } private fun setupListener() { - action_hide_weather_warning.setOnClickListener { - Preferences.showWeatherWarning = false - } - action_show_weather.setOnClickListener { Preferences.showWeather = !Preferences.showWeather } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt index 4d0080f..aea3be0 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/MainWidget.kt @@ -29,6 +29,7 @@ import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.helpers.* import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue +import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow import com.tommasoberlose.anotherwidget.receivers.* import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.isDarkTheme @@ -715,7 +716,6 @@ class MainWidget : AppWidgetProvider() { if (Preferences.customNotes.isNotEmpty()) { v.second_row_icon.isVisible = false v.next_event_date.text = Preferences.customNotes - v.next_event_date.gravity v.next_event_date.maxLines = 2 showSomething = true break@loop @@ -804,9 +804,9 @@ class MainWidget : AppWidgetProvider() { } if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) { - listOf(v.second_row_icon) + listOf(v.second_row_icon, v.second_row_icon_shadow) } else { - listOf(v.second_row_icon, v.weather_icon) + listOf(v.second_row_icon, v.weather_icon, v.second_row_icon_shadow) }.forEach { it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme())) it.alpha = @@ -889,6 +889,38 @@ class MainWidget : AppWidgetProvider() { it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) } + // Icons shadow + + listOf( + Pair(v.second_row_icon, v.second_row_icon_shadow), + ).forEach { + if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) { + it.second.isVisible = false + } else { + it.second.isVisible = it.first.isVisible + it.second.scaleX = it.first.scaleX + it.second.scaleY = it.first.scaleY + it.second.applyShadow(it.first) + } + } + + listOf( + Pair(v.action_next, v.action_next_shadow), + Pair(v.action_previous, v.action_previous_shadow), + ).forEach { + if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) { + it.second.isVisible = false + } else { + it.second.isVisible = it.first.isVisible + it.second.scaleX = it.first.scaleX + it.second.scaleY = it.first.scaleY + it.second.applyShadow(it.first, 0.6f) + } + } + + v.action_previous.scaleX = v.action_previous.scaleX * -1 + v.action_previous_shadow.scaleX = v.action_previous_shadow.scaleX * -1 + // Custom Font if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) { val googleSans: Typeface = when (Preferences.customFontVariant) { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt index d6278cf..7f7f9f3 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt @@ -1,12 +1,11 @@ package com.tommasoberlose.anotherwidget.utils +import android.animation.* import android.content.pm.PackageManager import android.view.Gravity import android.view.View import android.view.ViewAnimationUtils import android.widget.Toast -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.app.Activity import android.app.WallpaperManager import android.content.* @@ -23,10 +22,20 @@ import android.content.res.Resources import android.graphics.drawable.Drawable import android.util.DisplayMetrics import android.util.TypedValue +import android.view.ViewPropertyAnimator import android.view.animation.Animation import android.view.animation.Transformation import android.widget.LinearLayout +import android.widget.RelativeLayout +import androidx.core.animation.addListener +import androidx.core.animation.doOnEnd +import androidx.core.animation.doOnStart +import androidx.core.view.isVisible import com.tommasoberlose.anotherwidget.R +import com.tommasoberlose.anotherwidget.global.Preferences +import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment +import kotlinx.android.synthetic.main.fragment_app_main.* +import kotlinx.android.synthetic.main.the_widget_sans.* import java.util.* @@ -68,55 +77,72 @@ fun View.reveal(initialX: Int? = null, initialY: Int? = null) { } -fun View.expand() { - if (visibility != View.VISIBLE) { - measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) - val targetHeight = measuredHeight +fun View.expand(duration: Long = 500L) { + clearAnimation() + try { + val animator = (tag as ValueAnimator) + animator.removeAllListeners() + animator.cancel() + } catch (ex: java.lang.Exception) {} - layoutParams.height = 0 - visibility = View.VISIBLE - val a = object : Animation() { - protected override fun applyTransformation(interpolatedTime: Float, t: Transformation) { - layoutParams.height = if (interpolatedTime == 1f) - LinearLayout.LayoutParams.WRAP_CONTENT - else - (targetHeight * interpolatedTime).toInt() - translationY = 0f - requestLayout() - } - - override fun willChangeBounds(): Boolean { - return true - } - } - - a.duration = 500L - startAnimation(a) + layoutParams = layoutParams.apply { + height = RelativeLayout.LayoutParams.WRAP_CONTENT } + measure(0, 0) + + val initialHeight = measuredHeight + val anim = ValueAnimator.ofFloat( + alpha, + 1f + ).apply { + this.duration = duration + addUpdateListener { + val animatedValue = animatedValue as Float + layoutParams = layoutParams.apply { + height = (initialHeight * animatedValue).toInt() + } + translationY = (initialHeight * animatedValue - initialHeight) + alpha = animatedValue + } + addListener( + onStart = { + isVisible = true + } + ) + } + tag = anim + anim.start() } fun View.collapse(duration: Long = 500L) { - if (visibility != View.GONE) { - val initialHeight = measuredHeight - - val a = object : Animation() { - protected override fun applyTransformation(interpolatedTime: Float, t: Transformation) { - if (interpolatedTime == 1f) { - visibility = View.GONE - } else { - layoutParams.height = initialHeight - (initialHeight * interpolatedTime).toInt() - requestLayout() - } - } - - override fun willChangeBounds(): Boolean { - return true + clearAnimation() + try { + val animator = (tag as ValueAnimator) + animator.removeAllListeners() + animator.cancel() + } catch (ex: java.lang.Exception) {} + val initialHeight = measuredHeight + val anim = ValueAnimator.ofFloat( + alpha, + 0f + ).apply { + this.duration = duration + addUpdateListener { + val animatedValue = animatedValue as Float + layoutParams = layoutParams.apply { + height = (initialHeight * animatedValue).toInt() } + translationY = (initialHeight * animatedValue - initialHeight) + alpha = animatedValue } - - a.duration = duration //(initialHeight / v.context.resources.displayMetrics.density).toLong() - startAnimation(a) + addListener( + onEnd = { + isVisible = false + } + ) } + tag = anim + anim.start() } fun Context.openURI(url: String) { diff --git a/app/src/main/res/layout/fragment_weather_settings.xml b/app/src/main/res/layout/fragment_weather_settings.xml index 13214d0..855fa8d 100644 --- a/app/src/main/res/layout/fragment_weather_settings.xml +++ b/app/src/main/res/layout/fragment_weather_settings.xml @@ -21,45 +21,6 @@ android:paddingTop="8dp" android:paddingBottom="8dp" android:orientation="vertical"> - - - - - - - + - + android:textAllCaps="false" + android:clickable="true" + android:focusable="true" + android:layout_marginTop="8dp" + android:layout_marginStart="64dp" + app:rippleColor="@color/errorColorText_op10" + app:backgroundTint="@color/errorColorText_op10" + android:id="@+id/location_permission_alert" + android:textColor="@color/errorColorText" + android:visibility="gone" + android:text="@string/action_grant_permission"/> - + + + + - + + + + + android:layout_marginEnd="4dp" + android:id="@+id/special_weather_icon"/> - + android:layout_marginStart="4dp"> + + +