diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 38e4216..30cc77b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" - android:theme="@style/AppTheme.Monet"> + android:theme="@style/AppTheme"> () { companion object { @JvmStatic fun newInstance() = HomeFragment() + + private const val FEATURE_NOT_IMPLEMENTED_ALPHA = 0.5f } @Inject lateinit var appProvider: AppProvider + private var lastProgress = 0 + private val maxProgress = 100 + @Inject lateinit var usageTracker: UsageTracker override fun onViewReady(bundle: Bundle?) { + val rootView = binding.root + ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets -> + val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updatePadding(top = systemBarsInsets.top, bottom = systemBarsInsets.bottom) + insets + } + observeFlow(appProvider.blockConfigFlow) { config -> updateUIForBlockOption(config.blockOption) - updateInfoText(usageTracker.dailyUsageInMemory, config.timeLimit) + updateInfoText( + usageTracker.dailyUsageInMemory, + config.blockOption == BlockOption.DailyLimit, + config.timeLimit, + ) + + binding.switchTimerOverlay.isVisible = config.blockOption == BlockOption.DailyLimit } + startGradientAnimation() + observeFlow(usageTracker.dailyUsageInMemoryFlow) { config -> - updateInfoText(config, appProvider.blockConfig.timeLimit) + updateInfoText( + config, + appProvider.blockConfig.blockOption == BlockOption.DailyLimit, + appProvider.blockConfig.timeLimit, + ) } - binding.btnSettings.setOnClickListener { - appProvider.totalDailyUsage = 0L - } +// binding.btnSettings.setOnClickListener { +// appProvider.totalDailyUsage = 0L +// } binding.blockAllButton.setOnClickListener { val currentConfig = appProvider.blockConfig @@ -66,61 +102,66 @@ class HomeFragment : BaseFragment() { updateUIForBlockOption(newBlockOption) } - binding.dayLimitButton.setOnClickListener { + binding.dailyLimitButton.setOnClickListener { val currentConfig = appProvider.blockConfig // Toggle selection - val newBlockOption = if (currentConfig.blockOption == BlockOption.DayLimit) { + val newBlockOption = if (currentConfig.blockOption == BlockOption.DailyLimit) { BlockOption.NothingSelected } else { - BlockOption.DayLimit + BlockOption.DailyLimit } - appProvider.blockConfig = currentConfig.copy(blockOption = newBlockOption) - updateUIForBlockOption(newBlockOption) - - if (newBlockOption == BlockOption.DayLimit) { - DurationSheet().show(requireContext(), childFragmentManager) { - title(R.string.duration) - format(DurationTimeFormat.HH_MM) - onPositive { durationTimeInSeconds: Long -> - val newTimeLimit = durationTimeInSeconds * 1000 - val updatedConfig = appProvider.blockConfig.copy(timeLimit = newTimeLimit) + if (appProvider.blockConfig.timeLimit == 0L) { + showDurationPicker( + onSuccess = { + val updatedConfig = + appProvider.blockConfig.copy(blockOption = newBlockOption) appProvider.blockConfig = updatedConfig - } - } + updateUIForBlockOption(newBlockOption) + }, + onCancel = { + // Nothing to do + }, + ) + } else { + appProvider.blockConfig = currentConfig.copy(blockOption = newBlockOption) + updateUIForBlockOption(newBlockOption) } } - binding.temporaryUnblockButton.setOnClickListener { - val currentConfig = appProvider.blockConfig - - // Toggle selection - val newBlockOption = if (currentConfig.blockOption == BlockOption.TemporaryUnblock) { - BlockOption.NothingSelected - } else { - BlockOption.TemporaryUnblock + binding.pauseButton.apply { + alpha = FEATURE_NOT_IMPLEMENTED_ALPHA + setOnClickListener { + showFeatureComingSoonSnackBar() } - - appProvider.blockConfig = currentConfig.copy(blockOption = newBlockOption) - - updateUIForBlockOption(newBlockOption) } - binding.intervalTimerButton.setOnClickListener { - val currentConfig = appProvider.blockConfig - - // Toggle selection - val newBlockOption = if (currentConfig.blockOption == BlockOption.IntervalTimer) { - BlockOption.NothingSelected - } else { - BlockOption.IntervalTimer + binding.intervalTimerButton.apply { + alpha = FEATURE_NOT_IMPLEMENTED_ALPHA + setOnClickListener { + showFeatureComingSoonSnackBar() } + } - appProvider.blockConfig = currentConfig.copy(blockOption = newBlockOption) - updateUIForBlockOption(newBlockOption) + binding.configDailyLimitButton.setOnClickListener { + showDurationPicker(onSuccess = {}, onCancel = {}) } +// binding.intervalTimerButton.setOnClickListener { +// val currentConfig = appProvider.blockConfig +// +// // Toggle selection +// val newBlockOption = if (currentConfig.blockOption == BlockOption.IntervalTimer) { +// BlockOption.NothingSelected +// } else { +// BlockOption.IntervalTimer +// } +// +// appProvider.blockConfig = currentConfig.copy(blockOption = newBlockOption) +// updateUIForBlockOption(newBlockOption) +// } + // TODO ON First run APP TUTORIAL WITH THE EXPLANATION OF WHY THE ACCESSIBILITY SERVICE IS NEEDED // OR if this is disabled, ask it to enable on a popup if (!isAccessibilityServiceEnabled(requireContext())) { @@ -128,6 +169,188 @@ class HomeFragment : BaseFragment() { } setTimerOverlayCheckBoxListener() + + setupProgressIndicator() + // TODO add Block icon by Icons8 + } + + private fun showFeatureComingSoonSnackBar() { + Snackbar.make( + requireView(), + getString(R.string.feature_coming_soon), + Snackbar.LENGTH_SHORT, + ).show() + } + + /** + * Setup the progress indicator + */ + + private fun setupProgressIndicator() { + observeFlow(appProvider.blockConfigFlow) { config -> + updateProgress() + } + + // Update progress when resuming + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + updateProgress() + } + } + } + + /** + * show the duration picker. + */ + private fun showDurationPicker(onSuccess: () -> Unit, onCancel: () -> Unit) { + DurationSheet().show(requireContext(), childFragmentManager) { + title(R.string.duration) + format(DurationTimeFormat.HH_MM) + onPositive { durationTimeInSeconds: Long -> + val newTimeLimit = durationTimeInSeconds * 1000 + val updatedConfig = appProvider.blockConfig.copy( + timeLimit = newTimeLimit, + blockOption = BlockOption.DailyLimit, + ) + appProvider.blockConfig = updatedConfig + onSuccess() + } + onNegative { onCancel() } + } + } + + /** + * Updates the progress indicator + */ + private fun updateProgress() { + val config = appProvider.blockConfig + val timeLimit = config.timeLimit + + // If the block option is daily limit, use the daily usage, + // otherwise use 0 as there's no time limit associated with the block option + val currentUsage = + if (appProvider.blockConfig.blockOption == BlockOption.DailyLimit) usageTracker.dailyUsageInMemory else 0L + + val targetProgress = calculateTargetProgress(currentUsage, timeLimit) + animateProgressTo(targetProgress) + } + + /** + * Calculates the target progress for the progress indicator. + * + * @param currentUsage The current usage time in milliseconds. + * @param timeLimit The time limit in milliseconds. + * @return The target progress (0-100). + * + * If the time limit is greater than 0, + * the progress is calculated based on the ratio of current usage to the time limit. + * If the remaining time (timeLimit - currentUsage) is greater than 0, + * the progress is a percentage of the current usage relative to the time limit. + * If the remaining time is not greater than 0, + * it means the limit has been reached or exceeded, so the progress is set to maxProgress. + * If the time limit is 0 or less, it means there's no limit, so the progress is set to 0. + */ + private fun calculateTargetProgress(currentUsage: Long, timeLimit: Long): Int = if (timeLimit > 0) { + val remainingTime = timeLimit - currentUsage + if (remainingTime > 0) { + ((currentUsage.toDouble() / timeLimit.toDouble()) * maxProgress).toInt() + } else { // The limit is reached or exceeded + maxProgress + } + } else { + 0 + } + + /** + * Animate the progress indicator + * @param targetProgress The target progress to animate to. + * @param duration The duration of the animation. + */ + + private fun animateProgressTo(targetProgress: Int, duration: Long = 1000) { + val startProgress = lastProgress + val valueAnimator = ValueAnimator.ofInt(startProgress, targetProgress) + valueAnimator.apply { + this.duration = duration + interpolator = AccelerateDecelerateInterpolator() + + addUpdateListener { animator -> + val progress = animator.animatedValue as Int + binding.circleProgress.progress = progress + updateProgressIndicatorColor(progress) + lastProgress = progress + } + + start() + } + } + + /** + * Updates the color of the progress indicator based on the current progress. + * + * @param progress The current progress value (0-100). + */ + private fun updateProgressIndicatorColor(progress: Int) { + binding.circleProgress.setIndicatorColor(calculateColorForProgress(progress)) + } + + /** + * + * Calculates the color for the progress indicator based on the current progress. + * + * @param progress The current progress value (0-100). + * @return The color to be used for the progress indicator. + */ + private fun calculateColorForProgress(progress: Int): Int { + val startColor = ContextCompat.getColor(requireContext(), R.color.green) + val middleColor = ContextCompat.getColor(requireContext(), R.color.orangeDark) + val endColor = ContextCompat.getColor(requireContext(), R.color.red) + + val middleProgress = 75 + + return when { + progress < middleProgress -> blendColors( + startColor, + middleColor, + progress / middleProgress.toFloat(), + ) + + else -> blendColors( + middleColor, + endColor, + (progress - middleProgress) / (100f - middleProgress), + ) + } + } + + /** + * Blends two colors together based on a given ratio. + * + * @param color1 The first color. + * @param color2 The second color. + * @param ratio The ratio of the second color to blend (0.0-1.0). + * @return The blended color. + */ + private fun blendColors(color1: Int, color2: Int, ratio: Float): Int = + android.graphics.Color.rgb( + (android.graphics.Color.red(color1) * (1 - ratio) + android.graphics.Color.red(color2) * ratio).toInt(), + ( + android.graphics.Color.green(color1) * (1 - ratio) + android.graphics.Color.green( + color2, + ) * ratio + ).toInt(), + (android.graphics.Color.blue(color1) * (1 - ratio) + android.graphics.Color.blue(color2) * ratio).toInt(), + ) + + /** + * Start the gradient animation. + */ + private fun startGradientAnimation() { + val layout = binding.layout + val animationDrawable = layout.background as AnimationDrawable + animationDrawable.setEnterFadeDuration(6000) + animationDrawable.setExitFadeDuration(2000) + animationDrawable.start() } /** @@ -137,9 +360,9 @@ class HomeFragment : BaseFragment() { * and updates it when the checkbox is toggled. */ private fun setTimerOverlayCheckBoxListener() { - binding.checkBoxTimerOverlay.isChecked = appProvider.timerOverlayEnabled + binding.switchTimerOverlay.isChecked = appProvider.timerOverlayEnabled - binding.checkBoxTimerOverlay.setOnCheckedChangeListener { _, isChecked -> + binding.switchTimerOverlay.setOnCheckedChangeListener { _, isChecked -> appProvider.timerOverlayEnabled = isChecked } } @@ -148,13 +371,23 @@ class HomeFragment : BaseFragment() { super.onResume() } + /** + * Reset the buttons. + */ private fun resetButtons() { + binding.configDailyLimitButton.beGone() applyButtonEffect(binding.blockAllButton, false) - applyButtonEffect(binding.dayLimitButton, false) - applyButtonEffect(binding.temporaryUnblockButton, false) + applyButtonEffect(binding.dailyLimitButton, false) + applyButtonEffect(binding.pauseButton, false) applyButtonEffect(binding.intervalTimerButton, false) } + /** + * Apply the button effect. + * @param button The button to apply the effect to. + * @param activated True if the button is activated, false otherwise. + */ + private fun applyButtonEffect( button: MaterialButton, activated: Boolean @@ -162,28 +395,40 @@ class HomeFragment : BaseFragment() { button.apply { if (activated) { strokeWidth = resources.getDimensionPixelSize(R.dimen.card_stroke) - strokeColor = - ColorStateList.valueOf( - ContextCompat.getColor( - context, - R.color.green, - ), - ) - icon = ResourcesCompat.getDrawable(resources, R.drawable.book_cancel_outline, null) + strokeColor = ColorStateList.valueOf( + context.getColorFromAttr(androidx.appcompat.R.attr.colorPrimary), + ) + backgroundTintList = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.color_background_dark, + ), + ) } else { strokeWidth = resources.getDimensionPixelSize(R.dimen.card_stroke) - strokeColor = - ColorStateList.valueOf( - ContextCompat.getColor( - context, - R.color.gray_600, - ), - ) - icon = ResourcesCompat.getDrawable(resources, R.drawable.book_cancel_outline, null) + strokeColor = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.gray_600, + ), + ) + backgroundTintList = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.color_background_darker, + ), + ) } } } + /** + * Checks if the accessibility service is enabled for the current application. + * + * @param context The application context. + * @return `true` if the accessibility service is enabled, `false` otherwise. + * + */ private fun isAccessibilityServiceEnabled(context: Context): Boolean { val enabledServices = Settings.Secure.getString( context.contentResolver, @@ -194,26 +439,54 @@ class HomeFragment : BaseFragment() { return enabledServices?.contains(serviceName) == true } + /** + * Open the accessibility settings. + * @param context The application context. + */ private fun openAccessibilitySettings(context: Context) { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) context.startActivity(intent) - Toast.makeText(context, getString(R.string.accessibility_settings_toast), Toast.LENGTH_LONG).show() + context.showToast(getString(R.string.accessibility_settings_toast)) } override fun onDestroyView() { super.onDestroyView() } - private fun updateInfoText(totalDailyUsage: Long, timeLimit: Long) { - binding.tvTime.text = totalDailyUsage.formatTime() + "/" + timeLimit.formatTime() + /** + * Updates the info text. + * @param totalDailyUsage The total daily usage in milliseconds. + * @param showTimeLimit True if the time limit should be shown, false otherwise. + * @param timeLimit The time limit in milliseconds. + */ + private fun updateInfoText(totalDailyUsage: Long, showTimeLimit: Boolean, timeLimit: Long) { + if (showTimeLimit) { + binding.trackTime.text = getString( + R.string.time_track_limit, + totalDailyUsage.formatTime(), + timeLimit.formatTime(), + ) + } else { + binding.trackTime.text = getString( + R.string.time_track, + totalDailyUsage.formatTime(), + ) + } } + /** + * Update the UI for the block option. + * @param blockOption The block option. + */ private fun updateUIForBlockOption(blockOption: BlockOption) { resetButtons() when (blockOption) { BlockOption.BlockAll -> applyButtonEffect(binding.blockAllButton, true) - BlockOption.DayLimit -> applyButtonEffect(binding.dayLimitButton, true) - BlockOption.TemporaryUnblock -> applyButtonEffect(binding.temporaryUnblockButton, true) + BlockOption.DailyLimit -> { + binding.configDailyLimitButton.visibility = View.VISIBLE + applyButtonEffect(binding.dailyLimitButton, true) + } + BlockOption.IntervalTimer -> applyButtonEffect(binding.intervalTimerButton, true) BlockOption.NothingSelected -> Unit // No action needed } diff --git a/app/src/main/kotlin/com/scrolless/app/features/main/MainActivity.kt b/app/src/main/kotlin/com/scrolless/app/features/main/MainActivity.kt index 85792d0..e00283c 100644 --- a/app/src/main/kotlin/com/scrolless/app/features/main/MainActivity.kt +++ b/app/src/main/kotlin/com/scrolless/app/features/main/MainActivity.kt @@ -11,7 +11,6 @@ import com.scrolless.app.R import com.scrolless.app.base.BaseActivity import com.scrolless.app.databinding.ActivityMainBinding import com.scrolless.app.features.home.HomeFragment -import com.scrolless.framework.extensions.isDarkMode import com.scrolless.framework.extensions.showSnackBar import com.scrolless.framework.navigation.navigateFragment import dagger.hilt.android.AndroidEntryPoint @@ -29,13 +28,8 @@ class MainActivity : BaseActivity() { clearBackStack = true, ) - isDarkMode.let { - if (it == false) { - val windowInsetsController = - WindowCompat.getInsetsController(window, window.decorView) - windowInsetsController.isAppearanceLightNavigationBars = true - } - } + // Disable system insets from resizing your layout + WindowCompat.setDecorFitsSystemWindows(window, false) } override fun onBackPressed() { diff --git a/app/src/main/kotlin/com/scrolless/app/overlay/TimerOverlayManagerImpl.kt b/app/src/main/kotlin/com/scrolless/app/overlay/TimerOverlayManagerImpl.kt index 5451736..1c81e12 100644 --- a/app/src/main/kotlin/com/scrolless/app/overlay/TimerOverlayManagerImpl.kt +++ b/app/src/main/kotlin/com/scrolless/app/overlay/TimerOverlayManagerImpl.kt @@ -35,6 +35,10 @@ class TimerOverlayManagerImpl @Inject constructor( private val appProvider: AppProvider ) : TimerOverlayManager { + companion object { + private const val START_TIMER_OFFSET = 1000L + } + private var overlayView: View? = null private var timerTextView: TextView? = null @@ -68,10 +72,10 @@ class TimerOverlayManagerImpl @Inject constructor( if (overlayView != null) return // Offset the start time by 1 second to account for delays. - val startTimeMillis = System.currentTimeMillis() - 1000 + val startTimeMillis = System.currentTimeMillis() - START_TIMER_OFFSET // Inflate the overlay view using the app's theme - val themedContext = ContextThemeWrapper(serviceContext, R.style.AppTheme) + val themedContext = ContextThemeWrapper(serviceContext, R.style.AppTheme_Base) val inflater = LayoutInflater.from(themedContext) overlayView = inflater.inflate(R.layout.overlay_timer, null) timerTextView = overlayView?.findViewById(R.id.timerTextView) diff --git a/app/src/main/kotlin/com/scrolless/app/provider/AppProviderImpl.kt b/app/src/main/kotlin/com/scrolless/app/provider/AppProviderImpl.kt index 3d0b8e6..55daf12 100644 --- a/app/src/main/kotlin/com/scrolless/app/provider/AppProviderImpl.kt +++ b/app/src/main/kotlin/com/scrolless/app/provider/AppProviderImpl.kt @@ -80,7 +80,7 @@ class AppProviderImpl(context: Context) : AppProvider { private fun readBlockConfigFromCache(): BlockConfig { val blockOption = cacheManager.read(PREF_BLOCK_OPTION, BlockOption.NothingSelected) - val timeLimit = cacheManager.read(PREF_TIME_LIMIT, 20000L) + val timeLimit = cacheManager.read(PREF_TIME_LIMIT, 0L) val intervalLength = cacheManager.read(PREF_INTERVAL_LENGTH, 1000L) return BlockConfig(blockOption, timeLimit, intervalLength) } diff --git a/app/src/main/kotlin/com/scrolless/app/services/BlockControllerImpl.kt b/app/src/main/kotlin/com/scrolless/app/services/BlockControllerImpl.kt index f2cea1b..e8a3983 100644 --- a/app/src/main/kotlin/com/scrolless/app/services/BlockControllerImpl.kt +++ b/app/src/main/kotlin/com/scrolless/app/services/BlockControllerImpl.kt @@ -11,7 +11,6 @@ import com.scrolless.app.services.handlers.BlockAllBlockHandler import com.scrolless.app.services.handlers.DayLimitBlockHandler import com.scrolless.app.services.handlers.IntervalTimerBlockHandler import com.scrolless.app.services.handlers.NothingSelectedBlockHandler -import com.scrolless.app.services.handlers.TemporaryUnblockBlockHandler /** * Implementation of [BlockController] that uses a [BlockOptionHandler] to handle blocking logic. @@ -48,13 +47,12 @@ class BlockControllerImpl( private fun createHandlerForConfig(config: BlockConfig): BlockOptionHandler = when (config.blockOption) { BlockOption.BlockAll -> BlockAllBlockHandler() - BlockOption.DayLimit -> DayLimitBlockHandler(config.timeLimit) + BlockOption.DailyLimit -> DayLimitBlockHandler(config.timeLimit) BlockOption.IntervalTimer -> IntervalTimerBlockHandler( config.timeLimit, config.intervalLength, ) - BlockOption.TemporaryUnblock -> TemporaryUnblockBlockHandler(config.timeLimit) BlockOption.NothingSelected -> NothingSelectedBlockHandler() } diff --git a/app/src/main/res/drawable/baseline_help_outline_24.xml b/app/src/main/res/drawable/baseline_help_outline_24.xml new file mode 100644 index 0000000..a0107d1 --- /dev/null +++ b/app/src/main/res/drawable/baseline_help_outline_24.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/drawable/baseline_rate_review_24.xml b/app/src/main/res/drawable/baseline_rate_review_24.xml new file mode 100644 index 0000000..2cfa7a9 --- /dev/null +++ b/app/src/main/res/drawable/baseline_rate_review_24.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/drawable/circle_progress_drawable.xml b/app/src/main/res/drawable/circle_progress_drawable.xml deleted file mode 100644 index 4288940..0000000 --- a/app/src/main/res/drawable/circle_progress_drawable.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/gradient_four.xml b/app/src/main/res/drawable/gradient_four.xml new file mode 100644 index 0000000..1b66d03 --- /dev/null +++ b/app/src/main/res/drawable/gradient_four.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_list.xml b/app/src/main/res/drawable/gradient_list.xml new file mode 100644 index 0000000..325929d --- /dev/null +++ b/app/src/main/res/drawable/gradient_list.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/gradient_one.xml b/app/src/main/res/drawable/gradient_one.xml new file mode 100644 index 0000000..0789d2d --- /dev/null +++ b/app/src/main/res/drawable/gradient_one.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_three.xml b/app/src/main/res/drawable/gradient_three.xml new file mode 100644 index 0000000..9b04282 --- /dev/null +++ b/app/src/main/res/drawable/gradient_three.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_two.xml b/app/src/main/res/drawable/gradient_two.xml new file mode 100644 index 0000000..17060d8 --- /dev/null +++ b/app/src/main/res/drawable/gradient_two.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icons8_block_120.webp b/app/src/main/res/drawable/icons8_block_120.webp new file mode 100644 index 0000000..1056a71 Binary files /dev/null and b/app/src/main/res/drawable/icons8_block_120.webp differ diff --git a/app/src/main/res/drawable/icons8_control_48.webp b/app/src/main/res/drawable/icons8_control_48.webp new file mode 100644 index 0000000..3101d1d Binary files /dev/null and b/app/src/main/res/drawable/icons8_control_48.webp differ diff --git a/app/src/main/res/drawable/icons8_pause_100.webp b/app/src/main/res/drawable/icons8_pause_100.webp new file mode 100644 index 0000000..f792a35 Binary files /dev/null and b/app/src/main/res/drawable/icons8_pause_100.webp differ diff --git a/app/src/main/res/drawable/icons8_stopwatch_64.webp b/app/src/main/res/drawable/icons8_stopwatch_64.webp new file mode 100644 index 0000000..aa04757 Binary files /dev/null and b/app/src/main/res/drawable/icons8_stopwatch_64.webp differ diff --git a/app/src/main/res/drawable/icons8_timer_64.webp b/app/src/main/res/drawable/icons8_timer_64.webp new file mode 100644 index 0000000..f520c2c Binary files /dev/null and b/app/src/main/res/drawable/icons8_timer_64.webp differ diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 3aee691..2ba465e 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -5,347 +5,234 @@ --> + android:animateLayoutChanges="true" + android:background="@drawable/gradient_list" + android:padding="@dimen/margin_medium"> - + + app:layout_constraintWidth_max="@dimen/details_button_max_width" + app:rippleColor="?attr/colorPrimary" /> -