Skip to content

Commit

Permalink
Fixed some bug and Improved app icon
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumman04 committed May 25, 2024
1 parent 8ca1eef commit 1ebba4a
Show file tree
Hide file tree
Showing 21 changed files with 68 additions and 225 deletions.
3 changes: 2 additions & 1 deletion AnalogClock/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ android {

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.annotation)
}

afterEvaluate {
Expand All @@ -43,7 +44,7 @@ afterEvaluate {
register<MavenPublication>("release") {
groupId = "com.github.Jumman04"
artifactId = "Analogue-Watch"
version = "4.0"
version = "4.1"

afterEvaluate {
from(components["release"])
Expand Down
80 changes: 62 additions & 18 deletions AnalogClock/src/main/java/com/jummania/AnalogClock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.media.MediaPlayer
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
Expand Down Expand Up @@ -111,6 +113,19 @@ class AnalogClock @JvmOverloads constructor(
field = value // Assign the new MediaPlayer to the property.
}


/**
* Handler to manage the timing of the clock updates, running on the main UI thread.
*/
private val handler = Handler(Looper.getMainLooper())


/**
* Runnable that defines the task to be repeatedly executed to update the clock.
*/
private var runnable: Runnable? = null


// Initialization block to apply custom attributes from XML
init {
// Obtain the styled attributes from the XML attributes
Expand Down Expand Up @@ -229,6 +244,24 @@ class AnalogClock @JvmOverloads constructor(
R.styleable.AnalogClock_textSize, textSize
)
}

// Define the Runnable that will handle the clock updates
runnable = object : Runnable {
override fun run() {
// Remove any pending posts of the Runnable from the message queue
removeCallbacks(this)
// Update the clock face by invalidating the view, causing a redraw
invalidate()
// Schedule the next redraw in 1000 milliseconds (1 second)
handler.postDelayed(this, 1000)
}
}

// If the runnable is not null, schedule the first redraw in 1000 milliseconds
runnable?.let {
handler.postDelayed(it, 1000)
}

}


Expand All @@ -251,7 +284,7 @@ class AnalogClock @JvmOverloads constructor(
createMarker(canvas)

// Create and draw hands (hour, minute, and optionally second)
val shouldRedraw = createHand(canvas)
createHand(canvas)

// If at least one hand is being drawn, add additional visual elements
if (secondHand || minuteHand || hourHand) {
Expand All @@ -263,10 +296,25 @@ class AnalogClock @JvmOverloads constructor(
canvas, minSize / 2, ColorUtils.blendARGB(backgroundColor, 0xFFFFFFFF.toInt(), 0.3f)
)
}
}


// If the clock is running (i.e., at least one hand is being drawn), schedule a redraw
if (shouldRedraw) {
postInvalidateDelayed(1000) // Redraw after 1 second
/**
* Called when the visibility of this view and its ancestors change.
* This is used to start or stop the clock updates based on the view's visibility.
*
* @param isVisible True if this view and its ancestors are visible, false otherwise.
*/
override fun onVisibilityAggregated(isVisible: Boolean) {
super.onVisibilityAggregated(isVisible)
if (isVisible) {
// If the view is visible, start the Runnable to update the clock every second
runnable?.let {
handler.postDelayed(it, 1000)
}
} else {
// If the view is not visible, remove any pending callbacks to stop updates
handler.removeCallbacksAndMessages(null)
}
}

Expand Down Expand Up @@ -376,14 +424,12 @@ class AnalogClock @JvmOverloads constructor(
* @param canvas The canvas on which the clock hands will be drawn.
* @return A boolean value indicating whether the clock should be redrawn.
*/
private fun createHand(canvas: Canvas): Boolean {
private fun createHand(canvas: Canvas) {
val calendar = Calendar.getInstance()

val seconds = calendar.get(Calendar.SECOND)
val minutes = calendar.get(Calendar.MINUTE) + seconds / 60.0f

var shouldRedraw = false

// Draw hour hand if enabled
if (hourHand) {
createHand(
Expand All @@ -408,17 +454,17 @@ class AnalogClock @JvmOverloads constructor(
)
}

// Update seconds and play ticking sound if necessary
if (this.seconds != seconds) {
this.seconds = seconds
if (!tik1.isPlaying && sound) {
tik1.start()
}
shouldRedraw = true
}

// Draw second hand if enabled
if (secondHand) {

// Update seconds and play ticking sound if necessary
if (this.seconds != seconds) {
this.seconds = seconds
if (!tik1.isPlaying && sound) {
tik1.start()
}
}

createHand(
canvas,
radius * secondHandWidth,
Expand All @@ -428,8 +474,6 @@ class AnalogClock @JvmOverloads constructor(
seconds.toFloat()
)
}

return shouldRedraw
}


Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId = "com.jummania.analogue_watch"
minSdk = 21
targetSdk = 34
versionCode = 4
versionName = "4.0"
versionCode = 5
versionName = "4.1"

resValue("string", "versionName", versionName.toString())
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Binary file modified app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -78,210 +78,6 @@ class WatchFragment : Fragment() {
return preferenceManager.getBoolean(key, true)
}

/*
private fun startClock() {
binding?.apply {
val tik1 = getMediaPlayer(R.raw.tik1)
val tik2 = getMediaPlayer(R.raw.tik2)
val sound = getBoolean("sound")
runnable = object : Runnable {
override fun run() {
val calendar = Calendar.getInstance()
val seconds = calendar.get(Calendar.SECOND)
val minutes = calendar.get(Calendar.MINUTE)
if (!tik1.isPlaying && !tik2.isPlaying && sound) {
if (seconds % 2 == 0) tik2.start()
else tik1.start()
}
second.rotation = seconds * 6f
minute.rotation = (minutes + seconds / 60.0f) * 6f
hour.rotation = (calendar.get(Calendar.HOUR) + minutes / 60.0f) * 30f
handler.postDelayed(this, 1000)
}
}
runnable?.let { handler.post(it) }
}
}
private fun setResponsive(relativeLayoutParam: RelativeLayout.LayoutParams, frame: Boolean) {
binding?.apply {
mainCircle.post {
mainFrame.visibility(true)
val width = mainCircle.width - mainCircle.paddingLeft - mainCircle.paddingRight
second.setHeight(width * 0.8f)
minute.setHeight(width * 0.7f)
hour.setHeight(width * 0.6f)
val circleWidth = width / 14f
circle.setHeight(circleWidth)
circle.setWidth(circleWidth)
val secondWidth = circleWidth / 4
second.setWidth(secondWidth)
minute.setWidth(circleWidth / 3)
hour.setWidth(circleWidth / 2)
relativeLayoutParam.width = (secondWidth * 0.8).toInt()
if (frame) {
val padding = (circleWidth * 0.6f).toInt()
mainFrame.setPadding(padding)
secondFrame.setPadding(padding)
}
}
}
}
private fun createMarker(
relativeLayoutParam: RelativeLayout.LayoutParams, primaryColor: Int, secondaryColor: Int
) {
binding?.apply {
val hourMarker = getBoolean("hourMarker")
val minuteMarker = getBoolean("minuteMarker")
if (hourMarker || minuteMarker) {
relativeLayoutParam.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
for (i in 1..60) {
val divider = LinearLayout(requireContext())
divider.layoutParams = relativeLayoutParam
divider.weightSum = 2f
divider.rotation = 6f * i
divider.orientation = LinearLayout.VERTICAL
divider.elevation = 0f
val markers = LinearLayout(requireContext())
if (i % 5 == 0 && hourMarker) {
markers.setBackgroundColor(secondaryColor)
markers.setWeight(0.2f)
} else if (minuteMarker) {
markers.setBackgroundColor(primaryColor)
markers.setWeight(0.1f)
}
divider.addView(markers)
mainCircle.addView(divider)
}
}
}
}
private fun visibility(frame: Boolean) {
val secondHand = getBoolean("secondHand")
val minuteHand = getBoolean("minuteHand")
val hourHand = getBoolean("hourHand")
binding?.apply {
second.visibility(secondHand)
minute.visibility(minuteHand)
hour.visibility(hourHand)
circle.visibility(secondHand || minuteHand || hourHand)
if (!frame) {
mainFrame.setShadowElevation(0f)
secondFrame.setShadowElevation(0f)
mainFrame.setPadding(0)
secondFrame.setPadding(0)
mainFrame.setShapeAppearanceModel(NeumorphShapeAppearanceModel.builder().build())
}
}
}
private fun getMediaPlayer(@RawRes res: Int): MediaPlayer {
return MediaPlayer.create(requireContext(), res).also {
val volume = preferenceManager.getInt("volume", 1) / 10f
it.setVolume(volume, volume)
}
}
private fun getColor(key: String, defaultColor: String): Int {
return preferenceManager.getInt(key, Color.parseColor(defaultColor))
}
private fun LinearLayout.setWeight(weight: Float) {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, weight
)
}
override fun onPause() {
super.onPause()
handler.removeCallbacksAndMessages(null)
}
override fun onResume() {
super.onResume()
runnable?.let { handler.post(it) }
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.settings) parentFragmentManager.beginTransaction()
.setReorderingAllowed(true).addToBackStack(null)
.replace(R.id.fragmentContainerView, SettingsFragment()).commit()
return super.onOptionsItemSelected(item)
}
private fun getBoolean(key: String): Boolean {
return preferenceManager.getBoolean(key, true)
}
private fun View.visibility(boolean: Boolean) {
animate().alpha(if (boolean) 1f else 0f).start()
}
private fun View.setHeight(height: Float) {
layoutParams = layoutParams.also { it.height = height.toInt() }
}
private fun View.setWidth(width: Float) {
layoutParams = layoutParams.also { it.width = width.toInt() }
}
private fun View.setPadding(padding: Int) {
setPadding(padding, padding, padding, padding)
}
private fun setColor(primaryColor: Int, secondaryColor: Int) {
binding?.apply {
val backgroundColor = getColor("backgroundColor", "#FEF7FF")
root.setBackgroundColor(backgroundColor)
secondHand.setBackgroundColor(secondaryColor)
minuteHand.setBackgroundColor(primaryColor)
hourHand.setBackgroundColor(primaryColor)
val darkerColor = ColorUtils.blendARGB(backgroundColor, 0xFF000000.toInt(), 0.3f)
val lighterColor = ColorUtils.blendARGB(backgroundColor, 0xFFFFFFFF.toInt(), 0.3f)
mainFrame.setShadowColorDark(darkerColor)
mainFrame.setShadowColorLight(lighterColor)
secondFrame.setShadowColorDark(darkerColor)
secondFrame.setShadowColorLight(lighterColor)
}
}
*/

override fun onDestroy() {
super.onDestroy()
binding = null
Expand Down
Binary file modified app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary file not shown.
Binary file modified app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary file not shown.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[versions]
agp = "8.4.1"
androidColorpickerpreference = "v1.2.1"
annotation = "1.8.0"
kotlin = "1.9.0"
coreKtx = "1.13.1"
material = "1.12.0"
Expand All @@ -9,6 +10,7 @@ preference = "1.2.1"

[libraries]
android-colorpickerpreference = { module = "com.github.attenzione:android-ColorPickerPreference", version.ref = "androidColorpickerpreference" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
Expand Down

0 comments on commit 1ebba4a

Please sign in to comment.