summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-05-28 22:24:02 +0200
committerRaindropsSys <contact@minteck.org>2023-05-28 22:24:02 +0200
commit657ceb4591aa69c289a082312ae8d0222cf105cb (patch)
tree2fe6bece53ae739cd7ab8134615275559411f0f7
parent9c5ec4168931cdc08ccfb390c1fa9660fa254c8d (diff)
downloadponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.tar.gz
ponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.tar.bz2
ponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.zip
Updated 8 files and added 3 files (automated)
-rw-r--r--.idea/deploymentTargetDropDown.xml30
-rw-r--r--.idea/misc.xml1
-rw-r--r--app/build.gradle4
-rw-r--r--app/plural/release/app-plural-release.apkbin0 -> 8972893 bytes
-rw-r--r--app/plural/release/output-metadata.json20
-rw-r--r--app/src/main/AndroidManifest.xml26
-rw-r--r--app/src/main/java/dev/equestria/ponywatch/UnitedFace.kt646
-rw-r--r--app/src/main/res/values/colors.xml3
-rw-r--r--build.gradle4
-rw-r--r--gradle.properties4
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
11 files changed, 721 insertions, 19 deletions
diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 639f9bc..370735a 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -1,17 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
- <targetSelectedWithDropDown>
- <Target>
- <type value="QUICK_BOOT_TARGET" />
- <deviceKey>
- <Key>
- <type value="VIRTUAL_DEVICE_PATH" />
- <value value="$USER_HOME$/.android/avd/Wear_OS_Small_Round_API_30_2.avd" />
- </Key>
- </deviceKey>
- </Target>
- </targetSelectedWithDropDown>
- <timeTargetWasSelectedWithDropDown value="2023-04-13T09:39:17.290509Z" />
+ <value>
+ <entry key="Unnamed">
+ <State>
+ <runningDeviceTargetSelectedWithDropDown>
+ <Target>
+ <type value="RUNNING_DEVICE_TARGET" />
+ <deviceKey>
+ <Key>
+ <type value="VIRTUAL_DEVICE_PATH" />
+ <value value="$USER_HOME$/.android/avd/Wear_OS_Small_Round_API_30_2.avd" />
+ </Key>
+ </deviceKey>
+ </Target>
+ </runningDeviceTargetSelectedWithDropDown>
+ <timeTargetWasSelectedWithDropDown value="2023-05-28T16:49:01.209946Z" />
+ </State>
+ </entry>
+ </value>
</component>
</project> \ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0ad17cb..8978d23 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
diff --git a/app/build.gradle b/app/build.gradle
index 142d53f..7278532 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,8 +11,8 @@ android {
applicationId "dev.equestria.ponywatch"
minSdk 30
targetSdk 33
- versionCode 4
- versionName "2.1"
+ versionCode 5
+ versionName "3.0"
}
diff --git a/app/plural/release/app-plural-release.apk b/app/plural/release/app-plural-release.apk
new file mode 100644
index 0000000..6134b00
--- /dev/null
+++ b/app/plural/release/app-plural-release.apk
Binary files differ
diff --git a/app/plural/release/output-metadata.json b/app/plural/release/output-metadata.json
new file mode 100644
index 0000000..561d319
--- /dev/null
+++ b/app/plural/release/output-metadata.json
@@ -0,0 +1,20 @@
+{
+ "version": 3,
+ "artifactType": {
+ "type": "APK",
+ "kind": "Directory"
+ },
+ "applicationId": "dev.equestria.ponywatch",
+ "variantName": "pluralRelease",
+ "elements": [
+ {
+ "type": "SINGLE",
+ "filters": [],
+ "attributes": [],
+ "versionCode": 5,
+ "versionName": "3.0.plural",
+ "outputFile": "app-plural-release.apk"
+ }
+ ],
+ "elementType": "File"
+} \ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 088043f..29e8a55 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -150,6 +150,32 @@
</intent-filter>
</service>
+ <service
+ android:name=".UnitedFace"
+ android:exported="true"
+ android:label="United"
+ android:permission="android.permission.BIND_WALLPAPER">
+
+ <meta-data
+ android:name="android.service.wallpaper.square_mode"
+ android:value="false" />
+ <meta-data
+ android:name="android.service.wallpaper"
+ android:resource="@xml/watch_face" />
+ <meta-data
+ android:name="com.google.android.wearable.watchface.preview"
+ android:resource="@drawable/preview_unity" />
+ <meta-data
+ android:name="com.google.android.wearable.watchface.preview_circular"
+ android:resource="@drawable/preview_unity" />
+
+ <intent-filter>
+ <action android:name="android.service.wallpaper.WallpaperService" />
+
+ <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+ </intent-filter>
+ </service>
+
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
diff --git a/app/src/main/java/dev/equestria/ponywatch/UnitedFace.kt b/app/src/main/java/dev/equestria/ponywatch/UnitedFace.kt
new file mode 100644
index 0000000..533021d
--- /dev/null
+++ b/app/src/main/java/dev/equestria/ponywatch/UnitedFace.kt
@@ -0,0 +1,646 @@
+package dev.equestria.ponywatch
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorMatrix
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.support.wearable.watchface.CanvasWatchFaceService
+import android.support.wearable.watchface.WatchFaceStyle
+import android.util.Log
+import android.view.SurfaceHolder
+import android.widget.Toast
+import com.android.volley.toolbox.JsonObjectRequest
+import com.android.volley.toolbox.Volley
+import com.squareup.picasso.MemoryPolicy
+import com.squareup.picasso.Picasso
+import com.squareup.picasso.Target
+import java.lang.ref.WeakReference
+import java.util.Calendar
+import java.util.TimeZone
+
+
+/**
+ * Updates rate in milliseconds for interactive mode. We update once a second to advance the
+ * second hand.
+ */
+private const val INTERACTIVE_UPDATE_RATE_MS = 16
+
+/**
+ * Handler message id for updating the time periodically in interactive mode.
+ */
+private const val MSG_UPDATE_TIME = 0
+
+private const val HOUR_STROKE_WIDTH = 35f
+private const val MINUTE_STROKE_WIDTH = 7f
+private const val SECOND_TICK_STROKE_WIDTH = 3f
+
+private const val CENTER_GAP_AND_CIRCLE_RADIUS = 4f
+
+private const val SHADOW_RADIUS = 3f
+
+/**
+ * Analog watch face with a ticking second hand. In ambient mode, the second hand isn"t
+ * shown. On devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient
+ * mode. The watch face is drawn with less contrast in mute mode.
+ *
+ *
+ * Important Note: Because watch face apps do not have a default Activity in
+ * their project, you will need to set your Configurations to
+ * "Do not launch Activity" for both the Wear and/or Application modules. If you
+ * are unsure how to do this, please review the "Run Starter project" section
+ * in the Google Watch Face Code Lab:
+ * https://codelabs.developers.google.com/codelabs/watchface/index.html#0
+ */
+class UnitedFace : CanvasWatchFaceService() {
+
+ override fun onCreateEngine(): Engine {
+ return Engine()
+ }
+
+ private class EngineHandler(reference: UnitedFace.Engine) : Handler(Looper.myLooper()!!) {
+ private val mWeakReference: WeakReference<Engine> = WeakReference(reference)
+
+ override fun handleMessage(msg: Message) {
+ val engine = mWeakReference.get()
+ if (engine != null) {
+ when (msg.what) {
+ MSG_UPDATE_TIME -> engine.handleUpdateTimeMessage()
+ }
+ }
+ }
+ }
+
+ inner class Engine : CanvasWatchFaceService.Engine() {
+
+ private lateinit var mCalendar: Calendar
+
+ private var mRegisteredTimeZoneReceiver = false
+ private var mMuteMode: Boolean = false
+ private var mCenterX: Float = 0F
+ private var mCenterY: Float = 0F
+ private var mHeight: Float = 0F
+
+ private var lastRefreshMinute: Int = -1
+
+ private var mSecondHandLength: Float = 0F
+ private var sMinuteHandLength: Float = 0F
+ private var sHourHandLength: Float = 0F
+
+ /* Colors for all hands (hour, minute, seconds, ticks) based on photo loaded. */
+ private var mWatchHandColor: Int = 0
+ private var mWatchHandHighlightColor: Int = 0
+ private var mWatchHandShadowColor: Int = 0
+
+ private lateinit var mHourPaint: Paint
+ private lateinit var mMinutePaint: Paint
+ private lateinit var mSecondPaint: Paint
+
+ private lateinit var mBackgroundPaint: Paint
+ private lateinit var mBackgroundBitmap: Bitmap
+ private lateinit var mGrayBackgroundBitmap: Bitmap
+
+ private var mAmbient: Boolean = false
+ private var mLowBitAmbient: Boolean = false
+ private var mBurnInProtection: Boolean = false
+
+ private var currentDay: Int = 0
+ private var twoFronters: Boolean = false
+ private var bmp: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.default_pony)
+ private var bmp2: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.default_pony)
+
+ /* Handler to update the time once a second in interactive mode. */
+ private val mUpdateTimeHandler = EngineHandler(this)
+
+ private val mTimeZoneReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ mCalendar.timeZone = TimeZone.getDefault()
+ invalidate()
+ }
+ }
+
+ override fun onCreate(holder: SurfaceHolder) {
+ super.onCreate(holder)
+
+ setWatchFaceStyle(
+ WatchFaceStyle.Builder(this@UnitedFace)
+ .setAcceptsTapEvents(true)
+ .build()
+ )
+
+ mCalendar = Calendar.getInstance()
+
+ initializeBackground()
+ initializeWatchFace()
+ }
+
+ private fun initializeBackground() {
+ mBackgroundPaint = Paint().apply {
+ color = Color.BLACK
+ }
+ mBackgroundBitmap =
+ BitmapFactory.decodeResource(
+ resources, when (currentDay) {
+ 0 -> R.drawable.watchface_unity_bg_0
+ 1 -> R.drawable.watchface_unity_bg_1
+ 2 -> R.drawable.watchface_unity_bg_2
+ 3 -> R.drawable.watchface_unity_bg_3
+ 4 -> R.drawable.watchface_unity_bg_4
+ 5 -> R.drawable.watchface_unity_bg_5
+ 6 -> R.drawable.watchface_unity_bg_6
+ else -> R.drawable.watchface_unity_bg_0
+ }
+ )
+ }
+
+ private fun initializeWatchFace() {
+ /* Set defaults for colors */
+ mWatchHandShadowColor = R.color.cutiemark_shadow
+
+ mHourPaint = Paint().apply {
+ color = resources.getColor(when (currentDay) {
+ 0 -> R.color.unity_0_hours
+ 1 -> R.color.unity_1_hours
+ 2 -> R.color.unity_2_hours
+ 3 -> R.color.unity_3_hours
+ 4 -> R.color.unity_4_hours
+ 5 -> R.color.unity_5_hours
+ 6 -> R.color.unity_6_hours
+ else -> R.color.unity_0_hours
+ })
+ strokeWidth = HOUR_STROKE_WIDTH
+ isAntiAlias = true
+ strokeCap = Paint.Cap.ROUND
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, mWatchHandShadowColor
+ )
+ }
+
+ mMinutePaint = Paint().apply {
+ color = resources.getColor(R.color.cutiemark_minutes)
+ strokeWidth = MINUTE_STROKE_WIDTH
+ isAntiAlias = true
+ strokeCap = Paint.Cap.ROUND
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, mWatchHandShadowColor
+ )
+ }
+
+ mSecondPaint = Paint().apply {
+ color = resources.getColor(when (currentDay) {
+ 0 -> R.color.unity_0_seconds
+ 1 -> R.color.unity_1_seconds
+ 2 -> R.color.unity_2_seconds
+ 3 -> R.color.unity_3_seconds
+ 4 -> R.color.unity_4_seconds
+ 5 -> R.color.unity_5_seconds
+ 6 -> R.color.unity_6_seconds
+ else -> R.color.unity_0_seconds
+ })
+ strokeWidth = SECOND_TICK_STROKE_WIDTH
+ isAntiAlias = true
+ strokeCap = Paint.Cap.ROUND
+ strokeJoin = Paint.Join.ROUND
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, mWatchHandShadowColor
+ )
+ }
+ }
+
+ override fun onDestroy() {
+ mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME)
+ super.onDestroy()
+ }
+
+ override fun onPropertiesChanged(properties: Bundle) {
+ super.onPropertiesChanged(properties)
+ mLowBitAmbient = properties.getBoolean(
+ PROPERTY_LOW_BIT_AMBIENT, false
+ )
+ mBurnInProtection = properties.getBoolean(
+ PROPERTY_BURN_IN_PROTECTION, false
+ )
+ }
+
+ override fun onTimeTick() {
+ super.onTimeTick()
+ invalidate()
+ }
+
+ override fun onAmbientModeChanged(inAmbientMode: Boolean) {
+ super.onAmbientModeChanged(inAmbientMode)
+ mAmbient = inAmbientMode
+
+ updateWatchHandStyle()
+
+ // Check and trigger whether or not timer should be running (only
+ // in active mode).
+ updateTimer()
+ }
+
+ private fun updateWatchHandStyle() {
+ if (mAmbient) {
+ mHourPaint.color = Color.WHITE
+ mMinutePaint.color = Color.WHITE
+ mSecondPaint.color = Color.WHITE
+
+ mHourPaint.isAntiAlias = false
+ mMinutePaint.isAntiAlias = false
+ mSecondPaint.isAntiAlias = false
+
+ mHourPaint.clearShadowLayer()
+ mMinutePaint.clearShadowLayer()
+ mSecondPaint.clearShadowLayer()
+
+ } else {
+ mHourPaint.color = mWatchHandColor
+ mMinutePaint.color = mWatchHandColor
+ mSecondPaint.color = mWatchHandHighlightColor
+
+ mHourPaint.isAntiAlias = true
+ mMinutePaint.isAntiAlias = true
+ mSecondPaint.isAntiAlias = true
+
+ mHourPaint.setShadowLayer(
+ SHADOW_RADIUS, 0f, 0f, mWatchHandShadowColor
+ )
+ mMinutePaint.setShadowLayer(
+ SHADOW_RADIUS, 0f, 0f, mWatchHandShadowColor
+ )
+ mSecondPaint.setShadowLayer(
+ SHADOW_RADIUS, 0f, 0f, mWatchHandShadowColor
+ )
+ }
+ }
+
+ override fun onInterruptionFilterChanged(interruptionFilter: Int) {
+ super.onInterruptionFilterChanged(interruptionFilter)
+ val inMuteMode = interruptionFilter == INTERRUPTION_FILTER_NONE
+
+ /* Dim display in mute mode. */
+ if (mMuteMode != inMuteMode) {
+ mMuteMode = inMuteMode
+ mHourPaint.alpha = if (inMuteMode) 100 else 255
+ mMinutePaint.alpha = if (inMuteMode) 100 else 255
+ mSecondPaint.alpha = if (inMuteMode) 80 else 255
+ invalidate()
+ }
+ }
+
+ override fun onSurfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ super.onSurfaceChanged(holder, format, width, height)
+
+ /*
+ * Find the coordinates of the center point on the screen, and ignore the window
+ * insets, so that, on round watches with a "chin", the watch face is centered on the
+ * entire screen, not just the usable portion.
+ */
+ mCenterX = width / 2f
+ mCenterY = height / 2f
+ mHeight = height.toFloat()
+
+ /*
+ * Calculate lengths of different hands based on watch screen size.
+ */
+ mSecondHandLength = (mCenterX * 0.875).toFloat()
+ sMinuteHandLength = (mCenterX * 0.75).toFloat()
+ sHourHandLength = (mCenterX * 0.5).toFloat()
+
+ /* Scale loaded background image (more efficient) if surface dimensions change. */
+ val scale = width.toFloat() / mBackgroundBitmap.width.toFloat()
+
+ mBackgroundBitmap = Bitmap.createScaledBitmap(
+ mBackgroundBitmap,
+ (mBackgroundBitmap.width * scale).toInt(),
+ (mBackgroundBitmap.height * scale).toInt(), true
+ )
+
+ /*
+ * Create a gray version of the image only if it will look nice on the device in
+ * ambient mode. That means we don"t want devices that support burn-in
+ * protection (slight movements in pixels, not great for images going all the way to
+ * edges) and low ambient mode (degrades image quality).
+ *
+ * Also, if your watch face will know about all images ahead of time (users aren"t
+ * selecting their own photos for the watch face), it will be more
+ * efficient to create a black/white version (png, etc.) and load that when you need it.
+ */
+ if (!mBurnInProtection && !mLowBitAmbient) {
+ initGrayBackgroundBitmap()
+ }
+ }
+
+ private fun initGrayBackgroundBitmap() {
+ mGrayBackgroundBitmap = Bitmap.createBitmap(
+ mBackgroundBitmap.width,
+ mBackgroundBitmap.height,
+ Bitmap.Config.ARGB_8888
+ )
+ val canvas = Canvas(mGrayBackgroundBitmap)
+ val grayPaint = Paint()
+ val colorMatrix = ColorMatrix()
+ colorMatrix.setSaturation(0f)
+ val filter = ColorMatrixColorFilter(colorMatrix)
+ grayPaint.colorFilter = filter
+ canvas.drawBitmap(mBackgroundBitmap, 0f, 0f, grayPaint)
+ }
+
+ /**
+ * Captures tap event (and tap type). The [WatchFaceService.TAP_TYPE_TAP] case can be
+ * used for implementing specific logic to handle the gesture.
+ */
+ override fun onTapCommand(tapType: Int, x: Int, y: Int, eventTime: Long) {
+ when (tapType) {
+ TAP_TYPE_TOUCH -> {
+ // The user has started touching the screen.
+ }
+ TAP_TYPE_TOUCH_CANCEL -> {
+ // The user has started a different gesture or otherwise cancelled the tap.
+ }
+ TAP_TYPE_TAP ->
+ // The user has completed the tap gesture.
+ // TODO: Add code to handle the tap gesture.
+ Toast.makeText(applicationContext, "tap", Toast.LENGTH_SHORT)
+ .show()
+ }
+ invalidate()
+ }
+
+ override fun onDraw(canvas: Canvas, bounds: Rect) {
+ val now = System.currentTimeMillis()
+ mCalendar.timeInMillis = now
+
+ drawBackground(canvas)
+ drawWatchFace(canvas)
+ }
+
+ private fun drawBackground(canvas: Canvas) {
+
+ if (mAmbient && (mLowBitAmbient || mBurnInProtection)) {
+ canvas.drawColor(Color.BLACK)
+ } else if (mAmbient) {
+ canvas.drawBitmap(mGrayBackgroundBitmap, 0f, 0f, mBackgroundPaint)
+ } else {
+ canvas.drawBitmap(mBackgroundBitmap, 0f, 0f, mBackgroundPaint)
+ }
+ }
+
+ private fun drawWatchFace(canvas: Canvas) {
+
+ /*
+ * Draw ticks. Usually you will want to bake this directly into the photo, but in
+ * cases where you want to allow users to select their own photos, this dynamically
+ * creates them on top of the photo.
+ */
+ /*val innerTickRadius = mCenterX - 10
+ val outerTickRadius = mCenterX
+ for (tickIndex in 0..11) {
+ val tickRot = (tickIndex.toDouble() * Math.PI * 2.0 / 12).toFloat()
+ val innerX = Math.sin(tickRot.toDouble()).toFloat() * innerTickRadius
+ val innerY = (-Math.cos(tickRot.toDouble())).toFloat() * innerTickRadius
+ val outerX = Math.sin(tickRot.toDouble()).toFloat() * outerTickRadius
+ val outerY = (-Math.cos(tickRot.toDouble())).toFloat() * outerTickRadius
+ canvas.drawLine(
+ mCenterX + innerX, mCenterY + innerY,
+ mCenterX + outerX, mCenterY + outerY, mTickAndCirclePaint
+ )
+ }*/
+
+ val paintOut1 = Paint().apply {
+ color = resources.getColor(R.color.united_out_1)
+ style = Paint.Style.STROKE
+ strokeWidth = 10f
+ isAntiAlias = true
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, resources.getColor(R.color.cutiemark_shadow)
+ )
+ }
+
+ val paintOut2 = Paint().apply {
+ color = resources.getColor(R.color.united_out_2)
+ style = Paint.Style.STROKE
+ strokeWidth = 15f
+ strokeCap = Paint.Cap.ROUND
+ isAntiAlias = true
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, resources.getColor(R.color.cutiemark_shadow)
+ )
+ }
+
+ val seconds =
+ mCalendar.get(Calendar.SECOND) + mCalendar.get(Calendar.MILLISECOND) / 1000f
+ val secondsRotation = seconds * 6f
+
+ canvas.drawArc(0f, 0f, mCenterX * 2f, mCenterY * 2f, 270f, 360f, false, paintOut1)
+ canvas.drawArc(0f, 0f, mCenterX * 2f, mCenterY * 2f, secondsRotation - 1f + 270f, 1f, false, paintOut2)
+
+ /*
+ * Save the canvas state before we can begin to rotate it.
+ */
+ canvas.save()
+
+ /* Restore the canvas" original orientation. */
+ canvas.restore()
+
+ val paint = Paint().apply {
+ color = Color.WHITE
+ isAntiAlias = true
+ setShadowLayer(
+ SHADOW_RADIUS, 5f, 0f, mWatchHandShadowColor
+ )
+ }
+
+ paint.textAlign = Paint.Align.CENTER
+ paint.textSize = 96f
+ paint.typeface = resources.getFont(R.font.alicorn_font)
+
+ val h = mCalendar.get(Calendar.HOUR_OF_DAY)
+ val hs = if (h < 10) {
+ "0$h"
+ } else {
+ h.toString()
+ }
+
+ val m = mCalendar.get(Calendar.MINUTE)
+ val ms = if (m < 10) {
+ "0$m"
+ } else {
+ m.toString()
+ }
+
+ canvas.drawText("$hs:$ms", (canvas.width / 2).toFloat(), 96f, paint)
+
+ if (BuildConfig.ENABLE_PLURALITY || true) {
+ if (mCalendar.get(Calendar.MINUTE) != lastRefreshMinute) {
+ lastRefreshMinute = mCalendar.get(Calendar.MINUTE)
+
+ val target = object : Target {
+ override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
+ try {
+ if (bitmap != null) {
+ bmp = bitmap
+ bmp = Bitmap.createScaledBitmap(
+ bmp,
+ 48,
+ 48, true
+ )
+
+ canvas.drawBitmap(bmp, 24f, mCenterY - 48f/2f, mBackgroundPaint)
+ }
+ } catch (ex: IllegalArgumentException) {
+ Log.e("Picasso", ex.toString())
+ }
+
+ val volleyQueue = Volley.newRequestQueue(baseContext)
+
+ val jsonObjectRequest =
+ JsonObjectRequest("https://ponies.equestria.horse/api/raindrops-two",
+
+ { response ->
+ Log.i("HTTPRequest", response.toString())
+ twoFronters = response!!.get("multiple") as Boolean
+
+ if (twoFronters) {
+ val target = object : Target {
+ override fun onBitmapLoaded(
+ bitmap: Bitmap?,
+ from: Picasso.LoadedFrom?
+ ) {
+ try {
+ if (bitmap != null) {
+ bmp2 = bitmap
+ bmp2 = Bitmap.createScaledBitmap(
+ bmp2,
+ 48,
+ 48, true
+ )
+ }
+ } catch (ex: IllegalArgumentException) {
+ Log.e("Picasso", ex.toString())
+ }
+ }
+
+ override fun onBitmapFailed(
+ e: Exception?,
+ errorDrawable: Drawable?
+ ) {
+ Log.e("Picasso", e.toString())
+ }
+
+ override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
+ }
+
+ Picasso.get().load(
+ "https://ponies.equestria.horse/api/raindrops-img2-round"
+ ).memoryPolicy(MemoryPolicy.NO_CACHE).into(target)
+ }
+ },
+
+ { error ->
+ Log.e(
+ "HTTPRequest",
+ "Request error: ${error.localizedMessage}"
+ )
+ })
+
+ volleyQueue.add(jsonObjectRequest)
+ }
+
+ override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
+ Log.e("Picasso", e.toString())
+ }
+
+ override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}
+ }
+
+ Picasso.get().load(
+ "https://ponies.equestria.horse/api/raindrops-img-round"
+ ).memoryPolicy(MemoryPolicy.NO_CACHE).into(target)
+ } else {
+ if (twoFronters) {
+ canvas.drawBitmap(bmp2, 24f, mCenterY + 6f/2f, mBackgroundPaint)
+ canvas.drawBitmap(bmp, 24f, mCenterY - 102f/2f, mBackgroundPaint)
+ } else {
+ canvas.drawBitmap(bmp, 24f, mCenterY - 48f/2f, mBackgroundPaint)
+ }
+ }
+ }
+ }
+
+ override fun onVisibilityChanged(visible: Boolean) {
+ super.onVisibilityChanged(visible)
+
+ if (visible) {
+ registerReceiver()
+ /* Update time zone in case it changed while we weren"t visible. */
+ mCalendar.timeZone = TimeZone.getDefault()
+ invalidate()
+ } else {
+ unregisterReceiver()
+ }
+
+ /* Check and trigger whether or not timer should be running (only in active mode). */
+ updateTimer()
+ }
+
+ private fun registerReceiver() {
+ if (mRegisteredTimeZoneReceiver) {
+ return
+ }
+ mRegisteredTimeZoneReceiver = true
+ val filter = IntentFilter(Intent.ACTION_TIMEZONE_CHANGED)
+ this@UnitedFace.registerReceiver(mTimeZoneReceiver, filter)
+ }
+
+ private fun unregisterReceiver() {
+ if (!mRegisteredTimeZoneReceiver) {
+ return
+ }
+ mRegisteredTimeZoneReceiver = false
+ this@UnitedFace.unregisterReceiver(mTimeZoneReceiver)
+ }
+
+ /**
+ * Starts/stops the [.mUpdateTimeHandler] timer based on the state of the watch face.
+ */
+ private fun updateTimer() {
+ mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME)
+ if (shouldTimerBeRunning()) {
+ mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME)
+ }
+ }
+
+ /**
+ * Returns whether the [.mUpdateTimeHandler] timer should be running. The timer
+ * should only run in active mode.
+ */
+ private fun shouldTimerBeRunning(): Boolean {
+ return isVisible && !mAmbient
+ }
+
+ /**
+ * Handle updating the time periodically in interactive mode.
+ */
+ fun handleUpdateTimeMessage() {
+ invalidate()
+ if (shouldTimerBeRunning()) {
+ val timeMs = System.currentTimeMillis()
+ val delayMs = INTERACTIVE_UPDATE_RATE_MS - timeMs % INTERACTIVE_UPDATE_RATE_MS
+ mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 315d1fe..697f295 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -26,4 +26,7 @@
<color name="unity_6_hours">#C3FFE7</color>
<color name="unity_6_seconds">#4AFFB7</color>
+
+ <color name="united_out_1">#1f1f1f</color>
+ <color name="united_out_2">#777777</color>
</resources> \ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 55cb6da..df9d376 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '7.4.2' apply false
- id 'com.android.library' version '7.4.2' apply false
+ id 'com.android.application' version '8.0.1' apply false
+ id 'com.android.library' version '8.0.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
} \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 3c5031e..a2e90d8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,6 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true \ No newline at end of file
+android.nonTransitiveRClass=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonFinalResIds=false \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a8dfefe..edd2de2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Jan 11 10:34:07 CET 2023
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME