diff options
author | RaindropsSys <contact@minteck.org> | 2023-05-28 22:24:02 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-05-28 22:24:02 +0200 |
commit | 657ceb4591aa69c289a082312ae8d0222cf105cb (patch) | |
tree | 2fe6bece53ae739cd7ab8134615275559411f0f7 | |
parent | 9c5ec4168931cdc08ccfb390c1fa9660fa254c8d (diff) | |
download | ponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.tar.gz ponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.tar.bz2 ponywatch-657ceb4591aa69c289a082312ae8d0222cf105cb.zip |
Updated 8 files and added 3 files (automated)
-rw-r--r-- | .idea/deploymentTargetDropDown.xml | 30 | ||||
-rw-r--r-- | .idea/misc.xml | 1 | ||||
-rw-r--r-- | app/build.gradle | 4 | ||||
-rw-r--r-- | app/plural/release/app-plural-release.apk | bin | 0 -> 8972893 bytes | |||
-rw-r--r-- | app/plural/release/output-metadata.json | 20 | ||||
-rw-r--r-- | app/src/main/AndroidManifest.xml | 26 | ||||
-rw-r--r-- | app/src/main/java/dev/equestria/ponywatch/UnitedFace.kt | 646 | ||||
-rw-r--r-- | app/src/main/res/values/colors.xml | 3 | ||||
-rw-r--r-- | build.gradle | 4 | ||||
-rw-r--r-- | gradle.properties | 4 | ||||
-rw-r--r-- | gradle/wrapper/gradle-wrapper.properties | 2 |
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 Binary files differnew file mode 100644 index 0000000..6134b00 --- /dev/null +++ b/app/plural/release/app-plural-release.apk 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 |