Restore old layout for timers - Fix #265

- Add an icon in the bottom left corner to edit the new timer duration (except for portrait phones with multiple timers);
This commit is contained in:
BlackyHawky 2025-06-06 07:52:25 +02:00
parent fc8d5c6dcb
commit 8d52e50ac9
13 changed files with 530 additions and 209 deletions

View file

@ -15,8 +15,6 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.TextView;
@ -28,6 +26,7 @@ import com.best.deskclock.data.DataModel;
import com.best.deskclock.data.Timer;
import com.best.deskclock.utils.ThemeUtils;
import com.best.deskclock.widget.CircleButtonsLayout;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.color.MaterialColors;
@ -38,10 +37,12 @@ import java.util.Locale;
*/
public class TimerItem extends ConstraintLayout {
Context mContext;
/**
* The container of TimerCircleView and TimerTextController
*/
private FrameLayout mCircleContainer;
private CircleButtonsLayout mCircleContainer;
/**
* Formats and displays the text in the timer.
@ -59,6 +60,9 @@ public class TimerItem extends ConstraintLayout {
/** A button that resets the timer. */
private ImageButton mResetButton;
/** A button that edits a new duration to the timer. */
private ImageButton mTimerEditNewDurationButton;
/** A button that adds time to the timer. */
private MaterialButton mAddTimeButton;
@ -82,7 +86,6 @@ public class TimerItem extends ConstraintLayout {
private boolean mIsTablet;
private boolean mIsPortrait;
private boolean mIsLandscape;
public TimerItem(Context context) {
this(context, null);
@ -96,29 +99,22 @@ public class TimerItem extends ConstraintLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mContext = getContext();
mIsTablet = ThemeUtils.isTablet();
mIsPortrait = ThemeUtils.isPortrait();
mIsLandscape = ThemeUtils.isLandscape();
// To avoid creating a layout specifically for tablets, adjust the layout width and height here
if (mIsTablet && mIsLandscape) {
final ViewGroup.LayoutParams params = getLayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.WRAP_CONTENT;
setLayoutParams(params);
}
setBackground(ThemeUtils.cardBackground(getContext()));
setBackground(ThemeUtils.cardBackground(mContext));
mLabelView = findViewById(R.id.timer_label);
mResetButton = findViewById(R.id.reset);
mAddTimeButton = findViewById(R.id.timer_add_time_button);
mPlayPauseButton = findViewById(R.id.play_pause);
mCircleContainer = findViewById(R.id.circle_container);
mCircleView = mCircleContainer.findViewById(R.id.timer_time);
// Displays the remaining time or time since expiration. Timer text serves as a virtual start/stop button.
mTimerText = mCircleContainer.findViewById(R.id.timer_time_text);
final int colorAccent = MaterialColors.getColor(getContext(),
mCircleView = findViewById(R.id.timer_time);
// Displays the remaining time or time since expiration.
// Timer text serves as a virtual start/stop button.
mTimerText = findViewById(R.id.timer_time_text);
final int colorAccent = MaterialColors.getColor(mContext,
com.google.android.material.R.attr.colorPrimary, Color.BLACK);
final int textColorPrimary = mTimerText.getCurrentTextColor();
final ColorStateList timeTextColor = new ColorStateList(
@ -126,32 +122,12 @@ public class TimerItem extends ConstraintLayout {
new int[]{textColorPrimary, colorAccent});
mTimerText.setTextColor(timeTextColor);
mTimerTextController = new TimerTextController(mTimerText);
// Necessary to avoid the null pointer exception,
// as only the timer_item layout for portrait mode has these attributes
mTimerEditNewDurationButton = findViewById(R.id.timer_edit_new_duration_button);
// Needed to avoid the null pointer exception, as only phones in portrait mode with
// multiple timers have this text
if (isPortraitPhoneWithMultipleTimers()) {
mTimerTotalDurationText = findViewById(R.id.timer_total_duration);
}
// The size of the Play/Pause and add time buttons are reduced for phones in landscape mode
// due to the size of the timers unlike tablets
if (!mIsTablet && mIsLandscape) {
mAddTimeButton.setIncludeFontPadding(false);
mAddTimeButton.setMinHeight(0);
mAddTimeButton.setMinimumHeight(0);
mAddTimeButton.setMinWidth(0);
mAddTimeButton.setMinimumWidth(0);
mAddTimeButton.setPadding(ThemeUtils.convertDpToPixels(10, getContext()), mAddTimeButton.getPaddingTop(),
ThemeUtils.convertDpToPixels(10, getContext()), mAddTimeButton.getPaddingBottom());
mPlayPauseButton.setIncludeFontPadding(false);
mPlayPauseButton.setMinHeight(0);
mPlayPauseButton.setMinimumHeight(0);
mPlayPauseButton.setMinWidth(0);
mPlayPauseButton.setMinimumWidth(0);
mPlayPauseButton.setPadding(ThemeUtils.convertDpToPixels(20, getContext()), mPlayPauseButton.getPaddingTop(),
ThemeUtils.convertDpToPixels(20, getContext()), mPlayPauseButton.getPaddingBottom());
}
}
/**
@ -163,8 +139,7 @@ public class TimerItem extends ConstraintLayout {
mTimerTextController.setTimeString(timer.getRemainingTime());
if (mCircleView != null) {
final boolean hideCircle = ((timer.isExpired() || timer.isMissed()) && blinkOff)
|| (!mIsTablet && mIsLandscape);
final boolean hideCircle = ((timer.isExpired() || timer.isMissed()) && blinkOff);
mCircleView.setVisibility(hideCircle ? INVISIBLE : VISIBLE);
@ -187,6 +162,7 @@ public class TimerItem extends ConstraintLayout {
// Initialize the time.
mTimerTextController.setTimeString(timer.getRemainingTime());
// Initialize text for timer total duration
if (isPortraitPhoneWithMultipleTimers() && mTimerTotalDurationText != null) {
mTimerTotalDurationText.setText(timer.getTotalDuration());
}
@ -203,54 +179,86 @@ public class TimerItem extends ConstraintLayout {
}
// Initialize the circle
// (the circle is hidden for landscape phones because there is not enough space)
if (mCircleView != null) {
final boolean hideCircle = !mIsTablet && mIsLandscape;
mCircleView.setVisibility(hideCircle ? INVISIBLE : VISIBLE);
if (!hideCircle) {
mCircleView.update(timer);
}
mCircleView.update(timer);
}
// Initialize the alpha value of the time text color
mTimerText.setAlpha(1f);
// Initialize the time value to add to timer in the "timer_add_time_button"
// Initialize the time value to add to timer in the "Add time" button
String buttonTime = timer.getButtonTime();
long totalSeconds = Long.parseLong(buttonTime);
long buttonTimeMinutes = (totalSeconds) / 60;
long buttonTimeSeconds = totalSeconds % 60;
String buttonTimeFormatted = String.format(
Locale.getDefault(),
buttonTimeMinutes < 10 ? "%d:%02d" : "%02d:%02d",
buttonTimeMinutes,
buttonTimeSeconds);
mAddTimeButton.setText(getContext().getString(R.string.timer_add_custom_time, buttonTimeFormatted));
mAddTimeButton.setText(mContext.getString(R.string.timer_add_custom_time, buttonTimeFormatted));
String buttonContentDescription = buttonTimeSeconds == 0
? getContext().getString(R.string.timer_add_custom_time_description, String.valueOf(buttonTimeMinutes))
: getContext().getString(R.string.timer_add_custom_time_with_seconds_description,
? mContext.getString(R.string.timer_add_custom_time_description, String.valueOf(buttonTimeMinutes))
: mContext.getString(R.string.timer_add_custom_time_with_seconds_description,
String.valueOf(buttonTimeMinutes),
String.valueOf(buttonTimeSeconds));
mAddTimeButton.setContentDescription(buttonContentDescription);
// For tablets in portrait mode with single timer, adjust the size of the "Add time" and
// "Play/Pause" buttons
final ConstraintLayout.LayoutParams addTimeButtonParams =
(ConstraintLayout.LayoutParams) mAddTimeButton.getLayoutParams();
final ConstraintLayout.LayoutParams playPauseButtonParams =
(ConstraintLayout.LayoutParams) mPlayPauseButton.getLayoutParams();
if (mIsTablet && mIsPortrait && DataModel.getDataModel().getTimers().size() == 1) {
addTimeButtonParams.matchConstraintMaxHeight = ThemeUtils.convertDpToPixels(200, mContext);
playPauseButtonParams.matchConstraintMaxHeight = ThemeUtils.convertDpToPixels(200, mContext);
}
// Initialize some potentially expensive areas of the user interface only on state changes.
if (timer.getState() != mLastState) {
final Context context = getContext();
final String resetDesc = context.getString(R.string.reset);
final String resetDesc = mContext.getString(R.string.reset);
mResetButton.setVisibility(VISIBLE);
mResetButton.setContentDescription(resetDesc);
mAddTimeButton.setVisibility(VISIBLE);
mLastState = timer.getState();
// If the timer is reset, add a top margin so that the "Play" button is not stuck to the "Delete" button.
// For phones in portrait mode, when there are multiple timers and they are reset,
// adjust the constraints, margins and paddings of the "Play/Pause" button to have
// a suitable reduced view
if (isPortraitPhoneWithMultipleTimers()) {
final ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mPlayPauseButton.getLayoutParams();
params.topMargin = ThemeUtils.convertDpToPixels(timer.getState().equals(Timer.State.RESET) ? 10 : 0, context);
mPlayPauseButton.setLayoutParams(params);
if (mLastState.equals(Timer.State.RESET)) {
playPauseButtonParams.width = LayoutParams.WRAP_CONTENT;
playPauseButtonParams.startToStart = ConstraintLayout.LayoutParams.UNSET;
playPauseButtonParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
playPauseButtonParams.topToBottom = mLabelView.getId();
playPauseButtonParams.bottomToBottom = ConstraintLayout.LayoutParams.UNSET;
playPauseButtonParams.rightMargin = ThemeUtils.convertDpToPixels(12, mContext);
playPauseButtonParams.topMargin = ThemeUtils.convertDpToPixels(10, mContext);
mPlayPauseButton.setPadding(0, 0, 0, 0);
} else {
int playPauseButtonPadding = ThemeUtils.convertDpToPixels(24, mContext);
playPauseButtonParams.width = 0;
playPauseButtonParams.startToStart = mAddTimeButton.getId();
playPauseButtonParams.endToEnd = mAddTimeButton.getId();
playPauseButtonParams.topToBottom = ConstraintLayout.LayoutParams.UNSET;
playPauseButtonParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
playPauseButtonParams.rightMargin = 0;
playPauseButtonParams.topMargin = 0;
mPlayPauseButton.setPadding(playPauseButtonPadding, playPauseButtonPadding,
playPauseButtonPadding, playPauseButtonPadding);
}
}
switch (mLastState) {
@ -258,7 +266,11 @@ public class TimerItem extends ConstraintLayout {
mResetButton.setVisibility(GONE);
mResetButton.setContentDescription(null);
mAddTimeButton.setVisibility(INVISIBLE);
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(context, R.drawable.ic_fab_play));
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(mContext, R.drawable.ic_fab_play));
if (isTabletOrLandscapePhone() || isPhoneWithSingleTimer()) {
mTimerEditNewDurationButton.setVisibility(VISIBLE);
}
if (isPortraitPhoneWithMultipleTimers()) {
mCircleContainer.setVisibility(GONE);
@ -267,7 +279,12 @@ public class TimerItem extends ConstraintLayout {
}
case PAUSED -> {
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(context, R.drawable.ic_fab_play));
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(mContext, R.drawable.ic_fab_play));
if (isTabletOrLandscapePhone() || isPhoneWithSingleTimer()) {
mTimerEditNewDurationButton.setVisibility(GONE);
}
if (isPortraitPhoneWithMultipleTimers()) {
mCircleContainer.setVisibility(VISIBLE);
mTimerTotalDurationText.setVisibility(GONE);
@ -275,7 +292,11 @@ public class TimerItem extends ConstraintLayout {
}
case RUNNING -> {
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(context, R.drawable.ic_fab_pause));
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(mContext, R.drawable.ic_fab_pause));
if (isTabletOrLandscapePhone() || isPhoneWithSingleTimer()) {
mTimerEditNewDurationButton.setVisibility(GONE);
}
if (isPortraitPhoneWithMultipleTimers()) {
mCircleContainer.setVisibility(VISIBLE);
@ -284,14 +305,38 @@ public class TimerItem extends ConstraintLayout {
}
case EXPIRED, MISSED -> {
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(mContext, R.drawable.ic_fab_stop));
if (isTabletOrLandscapePhone() || isPhoneWithSingleTimer()) {
mTimerEditNewDurationButton.setVisibility(GONE);
}
mResetButton.setVisibility(GONE);
mPlayPauseButton.setIcon(AppCompatResources.getDrawable(context, R.drawable.ic_fab_stop));
}
}
}
}
/**
* @return {@code true} if the device is a phone in portrait mode with multiple timers displayed.
* {@code false} otherwise.
*/
private boolean isPortraitPhoneWithMultipleTimers() {
return !mIsTablet && mIsPortrait && DataModel.getDataModel().getTimers().size() > 1;
}
/**
* @return {@code true} if the device is a tablet or phone in landscape mode.
* {@code false} otherwise.
*/
private boolean isTabletOrLandscapePhone() {
return (mIsTablet || !mIsPortrait);
}
/**
* @return {@code true} if the device is a phone with a single timer displayed.
*/
private boolean isPhoneWithSingleTimer() {
return !mIsTablet && DataModel.getDataModel().getTimers().size() == 1;
}
}

View file

@ -44,6 +44,7 @@ public class TimerViewHolder extends RecyclerView.ViewHolder {
View timerLabel = view.findViewById(R.id.timer_label);
View resetButton = view.findViewById(R.id.reset);
View timerTotalDuration = view.findViewById(R.id.timer_total_duration);
View timerEditNewDurationButton = view.findViewById(R.id.timer_edit_new_duration_button);
View addTimeButton = view.findViewById(R.id.timer_add_time_button);
View circleContainer = view.findViewById(R.id.circle_container);
View timerTimeText = view.findViewById(R.id.timer_time_text);
@ -61,15 +62,6 @@ public class TimerViewHolder extends RecyclerView.ViewHolder {
}
};
View.OnLongClickListener setNewDurationListener = v -> {
if (!getTimer().isReset()) {
return false;
}
mTimerClickHandler.onDurationClicked(getTimer());
return true;
};
timerLabel.setOnClickListener(v -> mTimerClickHandler.onEditLabelClicked(getTimer()));
resetButton.setOnClickListener(v -> {
@ -96,23 +88,37 @@ public class TimerViewHolder extends RecyclerView.ViewHolder {
return true;
});
// Only possible for portrait mode phones with multiple timers
if (timerTotalDuration != null) {
timerTotalDuration.setOnLongClickListener(setNewDurationListener);
timerTotalDuration.setOnClickListener(v -> {
if (!getTimer().isReset()) {
return;
}
mTimerClickHandler.onDurationClicked(getTimer());
});
}
// If we click on the circular container when the phones (only) are in landscape mode,
// indicating a title for the timers is not possible so in this case we click on the
// time to start the timer.
// Long press on the time displays the dialog to set a new timer duration.
if (!ThemeUtils.isTablet() && ThemeUtils.isLandscape()) {
timerTimeText.setOnLongClickListener(setNewDurationListener);
timerTimeText.setOnClickListener(playPauseListener);
} else {
circleContainer.setOnLongClickListener(setNewDurationListener);
// Only possible for tablets, landscape phones or when there is only one timer
if (timerEditNewDurationButton != null) {
timerEditNewDurationButton.setOnClickListener(v -> {
if (!getTimer().isReset()) {
return;
}
mTimerClickHandler.onDurationClicked(getTimer());
});
}
if (circleContainer != null) {
circleContainer.setOnClickListener(playPauseListener);
circleContainer.setOnTouchListener(new Utils.CircleTouchListener());
}
if (!ThemeUtils.isTablet() && ThemeUtils.isLandscape()) {
timerTimeText.setOnClickListener(playPauseListener);
}
playPauseButton.setOnClickListener(playPauseListener);
deleteButton.setOnClickListener(v -> {

View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* modified
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
*/
package com.best.deskclock.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.best.deskclock.R;
import com.best.deskclock.utils.ThemeUtils;
/**
* This class adjusts the location of the reset button.
*/
public class CircleButtonsLayout extends FrameLayout {
private final float mDiamOffset;
@SuppressWarnings("unused")
public CircleButtonsLayout(Context context) {
this(context, null);
}
public CircleButtonsLayout(Context context, AttributeSet attrs) {
super(context, attrs);
final float strokeSize = ThemeUtils.convertDpToPixels(6, context);
mDiamOffset = strokeSize * 2;
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// We must call onMeasure both before and after re-measuring our views because the circle
// may not always be drawn here yet. The first onMeasure will force the circle to be drawn,
// and the second will force our re-measurements to take effect.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
remeasureViews();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void remeasureViews() {
View mCircleView = findViewById(R.id.timer_time);
View mResetAddButton = findViewById(R.id.reset);
final int frameWidth = mCircleView.getMeasuredWidth();
final int frameHeight = mCircleView.getMeasuredHeight();
final int minBound = Math.min(frameWidth, frameHeight);
final int circleDiam = (int) (minBound - mDiamOffset);
if (mResetAddButton != null) {
final MarginLayoutParams resetParams = (MarginLayoutParams) mResetAddButton
.getLayoutParams();
resetParams.bottomMargin = circleDiam / 8;
if (minBound == frameWidth) {
resetParams.bottomMargin += (frameHeight - frameWidth) / 2;
}
}
}
}

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: Material Design Authors / Google LLC
modified
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFF"
android:pathData="M619 341l29 28-57-57 28 29Zm141-85-56-56 56 56ZM160 840h97q16 0 30.5-6T313 817L817 313q12-12 17.5-26.5T840 256q0-15-5.5-30T817 200l-55-56q-11-12-26-18t-31-6-30.5 6T648 143L143 647q-11 11-17 25.5T120 703v97q0 17 11.5 28.5T160 840Zm40-80V703L591 312l57 57L257 760H200Z" />
</vector>

View file

@ -5,14 +5,16 @@
modified
-->
<!-- This TimerItem excludes the circle because not enough space exists. -->
<com.best.deskclock.timer.TimerItem
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_marginVertical="4dp"
android:layout_marginHorizontal="10dp"
android:paddingBottom="5dp"
tools:background="@drawable/card_background_for_preview">
<com.google.android.material.textview.MaterialTextView
@ -32,7 +34,7 @@
app:drawableStartCompat="@drawable/ic_label"
app:drawableTint="?attr/colorOnSurfaceVariant"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/delete_timer"
app:layout_constraintEnd_toStartOf="@+id/delete_timer"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
@ -49,76 +51,87 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Show circle for tablets only. See TimerItem.java -->
<FrameLayout
android:id="@+id/circle_container"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/time_container"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:focusable="true"
app:layout_constraintDimensionRatio="1:1"
android:layout_height="0dp"
android:orientation="vertical"
android:gravity="center_vertical|center_horizontal"
app:layout_constraintWidth_min="120dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/timer_label"
app:layout_constraintBottom_toTopOf="@id/play_pause">
<com.best.deskclock.timer.TimerCircleView
android:id="@+id/timer_time"
android:layout_width="match_parent"
android:layout_height="match_parent" />
app:layout_constraintEnd_toStartOf="@+id/timer_add_time_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<com.best.deskclock.widget.AutoSizingTextView
android:id="@+id/timer_time_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingHorizontal="16dp"
android:gravity="center"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:includeFontPadding="false"
android:textSize="@dimen/timer_time_text_size"
android:textSize="40sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="01:23" />
</FrameLayout>
<ImageButton
android:id="@+id/reset"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_reset"
android:scaleType="centerCrop"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/timer_time_text"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- Buttons size is set programmatically in the TimerItem.java file. -->
<com.google.android.material.button.MaterialButton
android:id="@+id/timer_add_time_button"
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageButton
android:id="@+id/timer_edit_new_duration_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginBottom="5dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="+ 1:00" />
<ImageButton
android:id="@+id/reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_reset"
android:layout_marginBottom="7dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_edit"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary"
android:contentDescription="@null"
app:tint="?android:attr/textColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/timer_add_time_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:padding="12dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/play_pause"
app:layout_constraintBottom_toBottomOf="@id/play_pause" />
app:layout_constraintBottom_toTopOf="@+id/play_pause"
tools:text="+ 1:00" />
<com.google.android.material.button.MaterialButton
android:id="@+id/play_pause"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginBottom="5dp"
android:padding="12dp"
android:contentDescription="@string/timer_start"
android:scaleType="centerInside"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/timer_add_time_button"
app:layout_constraintEnd_toEndOf="@+id/timer_add_time_button"
app:layout_constraintBottom_toBottomOf="parent"
tools:icon="@drawable/ic_fab_play" />

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
modified
-->
<!-- This TimerItem includes the circle because ample space exists. -->
<com.best.deskclock.timer.TimerItem
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_marginHorizontal="10dp"
android:paddingBottom="5dp"
tools:background="@drawable/card_background_for_preview"
tools:layout_width="400dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/timer_label"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="?attr/selectableItemBackground"
android:drawablePadding="8dp"
android:hint="@string/add_label"
android:gravity="center_vertical"
android:textSize="16sp"
android:ellipsize="end"
android:maxLines="1"
android:focusable="true"
app:drawableStartCompat="@drawable/ic_label"
app:drawableTint="?attr/colorOnSurfaceVariant"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/delete_timer"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/delete_timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginTop="12dp"
android:src="@drawable/ic_delete"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/delete"
app:tint="?attr/colorPrimary"
android:scaleType="centerInside"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.best.deskclock.widget.CircleButtonsLayout
android:id="@+id/circle_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_marginTop="5dp"
android:focusable="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintHeight_max="220dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/timer_add_time_button"
app:layout_constraintTop_toBottomOf="@+id/timer_label"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="InconsistentLayout">
<com.best.deskclock.timer.TimerCircleView
android:id="@+id/timer_time"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="InconsistentLayout" />
<com.best.deskclock.widget.AutoSizingTextView
android:id="@+id/timer_time_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:includeFontPadding="false"
android:textSize="40sp"
tools:text="01:23" />
<ImageButton
android:id="@+id/reset"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_reset"
android:scaleType="centerCrop"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary" />
</com.best.deskclock.widget.CircleButtonsLayout>
<ImageButton
android:id="@+id/timer_edit_new_duration_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginBottom="7dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_edit"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@null"
app:tint="?android:attr/textColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/timer_add_time_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginBottom="5dp"
android:padding="24dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/play_pause"
tools:text="+ 1:00" />
<com.google.android.material.button.MaterialButton
android:id="@+id/play_pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="24dp"
android:contentDescription="@string/timer_start"
android:scaleType="centerInside"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintStart_toStartOf="@+id/timer_add_time_button"
app:layout_constraintEnd_toEndOf="@+id/timer_add_time_button"
app:layout_constraintBottom_toBottomOf="parent"
tools:icon="@drawable/ic_fab_play" />
</com.best.deskclock.timer.TimerItem>

View file

@ -14,6 +14,7 @@
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_marginHorizontal="10dp"
android:paddingBottom="5dp"
tools:background="@drawable/card_background_for_preview">
<com.google.android.material.textview.MaterialTextView
@ -33,7 +34,7 @@
app:drawableStartCompat="@drawable/ic_label"
app:drawableTint="?attr/colorOnSurfaceVariant"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/delete_timer"
app:layout_constraintEnd_toStartOf="@+id/delete_timer"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
@ -50,26 +51,30 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
<com.best.deskclock.widget.CircleButtonsLayout
android:id="@+id/circle_container"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_height="0dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="5dp"
android:focusable="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintHeight_max="240dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/timer_label">
app:layout_constraintEnd_toStartOf="@+id/timer_add_time_button"
app:layout_constraintTop_toBottomOf="@+id/timer_label"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="InconsistentLayout">
<com.best.deskclock.timer.TimerCircleView
android:id="@+id/timer_time"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:ignore="InconsistentLayout" />
<com.best.deskclock.widget.AutoSizingTextView
android:id="@+id/timer_time_text"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
@ -79,69 +84,90 @@
android:textSize="40sp"
tools:text="01:23" />
</FrameLayout>
<ImageButton
android:id="@+id/reset"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_reset"
android:scaleType="centerCrop"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/timer_total_duration"
</com.best.deskclock.widget.CircleButtonsLayout>
<!-- Only displayed for phones when the timer is reset. See the TimerItem.java file. -->
<LinearLayout
android:id="@+id/timer_total_duration_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/timer_add_time_button"
app:layout_constraintTop_toBottomOf="@+id/timer_label"
app:layout_constraintBottom_toBottomOf="@+id/play_pause">
<com.best.deskclock.widget.AutoSizingTextView
android:id="@+id/timer_total_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="8dp"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:textStyle="bold"
android:textSize="26sp"
android:drawablePadding="8dp"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_edit"
app:drawableTint="?android:attr/textColor"
tools:ignore="InconsistentLayout" />
</LinearLayout>
<!-- For phones with multiple timers, this icon is not displayed. See the TimerItem.java file. -->
<ImageButton
android:id="@+id/timer_edit_new_duration_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:paddingStart="@null"
android:paddingEnd="4dp"
android:paddingVertical="8dp"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:textStyle="bold"
android:textSize="24sp"
android:includeFontPadding="false"
android:drawablePadding="8dp"
android:layout_marginBottom="7dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_edit"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@null"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_hourglass_top"
app:drawableTint="?attr/colorOnSurfaceVariant"
app:tint="?android:attr/textColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/play_pause"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="InconsistentLayout" />
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/timer_add_time_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginBottom="5dp"
android:layout_marginEnd="12dp"
android:padding="24dp"
android:maxLines="1"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/circle_container"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/play_pause"
tools:text="+ 1:00" />
<ImageButton
android:id="@+id/reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_reset"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/play_pause"
app:layout_constraintBottom_toBottomOf="@+id/play_pause" />
<!-- Constraints, margins and paddings adjusted in the TimerItem.java file to have a suitable
reduced view. -->
<com.google.android.material.button.MaterialButton
android:id="@+id/play_pause"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginBottom="5dp"
android:padding="24dp"
android:contentDescription="@string/timer_start"
android:scaleType="centerInside"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/circle_container"
app:layout_constraintStart_toStartOf="@+id/timer_add_time_button"
app:layout_constraintEnd_toEndOf="@+id/timer_add_time_button"
app:layout_constraintBottom_toBottomOf="parent"
tools:icon="@drawable/ic_fab_play" />

View file

@ -22,18 +22,11 @@
app:layout_constraintGuide_percent="0.70" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/left_button_end_guide"
android:id="@+id/button_center_guide"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.4" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/right_button_start_guide"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.6" />
app:layout_constraintGuide_percent="0.5" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/timer_label"
@ -69,12 +62,12 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
<com.best.deskclock.widget.CircleButtonsLayout
android:id="@+id/circle_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="5dp"
android:focusable="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
@ -99,8 +92,36 @@
android:textSize="70sp"
tools:text="01:23" />
</FrameLayout>
<ImageButton
android:id="@+id/reset"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_reset"
android:scaleType="centerCrop"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary" />
</com.best.deskclock.widget.CircleButtonsLayout>
<ImageButton
android:id="@+id/timer_edit_new_duration_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginBottom="12dp"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/ic_edit"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@null"
app:tint="?android:attr/textColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- For tablets with single timer, the size of this button is adjusted.
See the TimerItem.java file. -->
<com.google.android.material.button.MaterialButton
android:id="@+id/timer_add_time_button"
android:layout_width="0dp"
@ -114,25 +135,13 @@
app:layout_constraintWidth_max="320dp"
app:layout_constraintHeight_max="100dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/left_button_end_guide"
app:layout_constraintEnd_toStartOf="@+id/button_center_guide"
app:layout_constraintTop_toBottomOf="@+id/circle_container_end_guide"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="+ 1:00" />
<ImageButton
android:id="@+id/reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_reset"
android:scaleType="centerInside"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/reset"
app:tint="?attr/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/play_pause"
app:layout_constraintBottom_toBottomOf="@+id/play_pause" />
<!-- For tablets in portrait mode with single timer, the size of this button is adjusted.
See the TimerItem.java file. -->
<com.google.android.material.button.MaterialButton
android:id="@+id/play_pause"
android:layout_width="0dp"
@ -147,7 +156,7 @@
app:iconPadding="0dp"
app:layout_constraintWidth_max="320dp"
app:layout_constraintHeight_max="100dp"
app:layout_constraintStart_toEndOf="@+id/right_button_start_guide"
app:layout_constraintStart_toEndOf="@+id/button_center_guide"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/circle_container_end_guide"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -9,7 +9,6 @@
for different hardware and product builds. -->
<resources>
<dimen name="main_clock_font_size">125sp</dimen>
<dimen name="timer_time_text_size">40sp</dimen>
<item name="city_list_end_guide_percent" type="fraction">0.65</item>
</resources>

View file

@ -10,7 +10,6 @@
<resources>
<dimen name="main_clock_font_size">64sp</dimen>
<dimen name="analog_clock_size">294dp</dimen>
<dimen name="timer_time_text_size">32sp</dimen>
<item name="timer_setup_time_bottom_percent" type="fraction">0.2</item>
<item name="city_list_end_guide_percent" type="fraction">0.55</item>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 376 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 KiB

After

Width:  |  Height:  |  Size: 319 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 444 KiB

Before After
Before After