diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3b31f5c..4a389de 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + + + + + + + + + @@ -22,16 +39,6 @@ android:name="android.appwidget.provider" android:resource="@xml/i_o_s_clock_widget_info" /> - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/iosclockwidget/AlarmService.kt b/app/src/main/java/com/ray650128/iosclockwidget/AlarmService.kt new file mode 100644 index 0000000..7b37011 --- /dev/null +++ b/app/src/main/java/com/ray650128/iosclockwidget/AlarmService.kt @@ -0,0 +1,119 @@ +package com.ray650128.iosclockwidget + +import android.annotation.SuppressLint +import android.app.* +import android.content.Intent +import android.os.Build +import android.os.IBinder +import android.os.PowerManager.WakeLock +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import java.util.* + + +class AlarmService : Service() { + + private val timerThread = Thread { + while (true) { + if (!isServiceRunning) break + val calendar = Calendar.getInstance() + val second = calendar[Calendar.SECOND] + // 計算時、分、秒的旋轉角度 + secondAngle = (second * 360f / 60f) + sendWidgetIntent() + Thread.sleep(1000) + } + } + + override fun onBind(intent: Intent): IBinder { + throw UnsupportedOperationException("Not yet implemented") + } + + override fun onCreate() { + super.onCreate() + + isServiceRunning = true + + //RetrofitClient.init("http://192.168.0.173:8080") + + startForeground() + } + + @SuppressLint("WakelockTimeout") + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + timerThread.start() +// return START_STICKY START_NOT_STICKY START_REDELIVER_INTENT + return super.onStartCommand(intent, flags, startId) + } + + override fun onDestroy() { + super.onDestroy() + isServiceRunning = false + } + + //region Foreground Service 必要通知 + private fun startForeground() { + //region Android 通知設定畫面 intent + val intent = Intent().apply { + action = "android.settings.APP_NOTIFICATION_SETTINGS" + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtra("android.provider.extra.APP_PACKAGE", packageName) + } + val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.getActivity(this.applicationContext, SERVICE_ID, intent, PendingIntent.FLAG_MUTABLE) + } else { + PendingIntent.getActivity(this.applicationContext, SERVICE_ID, intent, PendingIntent.FLAG_IMMUTABLE) + } + //endregion + + val channelId = createNotificationChannel() // 建立通知頻道 + val notification = NotificationCompat.Builder(this, channelId) + .setOngoing(true) // 持續顯示,不可被清除 + .setSmallIcon(R.drawable.ic_info) // 通知 icon + .setPriority(NotificationManager.IMPORTANCE_NONE) // 重要性=無 + .setCategory(Notification.CATEGORY_SERVICE) // 類型=服務 + .setContentIntent(pendingIntent) // 點選後的跳轉 intent + .setContentTitle("${getString(R.string.app_name)} 執行中") // 標題 + .setContentText("您可以在設定畫面中關閉\"背景服務\"通知") // 內容 + .build() + startForeground(SERVICE_ID, notification) + } + + private fun createNotificationChannel(): String { + val channel = NotificationChannel(NOTIFICATION_SERVICE_CH, "背景服務", NotificationManager.IMPORTANCE_NONE).apply { + enableLights(false) // 不啟用指示燈 + enableVibration(false) // 不啟用震動 + setSound(null, null) // 不啟用鈴聲 + setShowBadge(false) // 不要顯示通知圓點 + description = "背景服務常駐通知,此為必須存在的通知類型,可以將顯示通知關閉來隱藏。" + } + + val notificationManager = NotificationManagerCompat.from(this) + notificationManager.createNotificationChannel(channel) + return NOTIFICATION_SERVICE_CH + } + //endregion + + private fun sendWidgetIntent() { + val intent = Intent(this, IOSClockWidget::class.java).apply { + action = SECOND_CHANGED + putExtra(SECOND_ANGLE, secondAngle) + } + sendBroadcast(intent) + } + + companion object { + private val TAG = AlarmService::class.java.simpleName + + const val SERVICE_ID = 0x1 + + private const val NOTIFICATION_SERVICE_CH = "clock.service" + + var isServiceRunning = false + + var secondAngle = 0f + + const val SECOND_CHANGED = "SECOND_CHANGED" + const val SECOND_ANGLE = "SECOND_ANGLE" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/iosclockwidget/IOSClockWidget.kt b/app/src/main/java/com/ray650128/iosclockwidget/IOSClockWidget.kt index 0345da7..2fc000f 100644 --- a/app/src/main/java/com/ray650128/iosclockwidget/IOSClockWidget.kt +++ b/app/src/main/java/com/ray650128/iosclockwidget/IOSClockWidget.kt @@ -2,13 +2,33 @@ package com.ray650128.iosclockwidget import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider +import android.content.ComponentName import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas import android.widget.RemoteViews /** * Implementation of App Widget functionality. */ class IOSClockWidget : AppWidgetProvider() { + + override fun onReceive(context: Context?, intent: Intent?) { + super.onReceive(context, intent) + val appWidgetManager = context?.getSystemService(Context.APPWIDGET_SERVICE) as AppWidgetManager + val appWidgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, IOSClockWidget::class.java)) + + when (intent?.action) { + AlarmService.SECOND_CHANGED -> { + for (appWidgetId in appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId) + } + } + } + } + override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, @@ -38,7 +58,13 @@ internal fun updateAppWidget( // Construct the RemoteViews object val views = RemoteViews(context.packageName, R.layout.i_o_s_clock_widget) views.setTextViewText(R.id.appwidget_text, widgetText) + val bmpOriginal = BitmapFactory.decodeResource(context.applicationContext.resources, R.drawable.img_second_hand) + val bmpResult = Bitmap.createBitmap(285, 285, Bitmap.Config.ARGB_8888) + val tempCanvas = Canvas(bmpResult) + tempCanvas.rotate(AlarmService.secondAngle, 285 / 2.toFloat(), 285 / 2.toFloat()) + tempCanvas.drawBitmap(bmpOriginal, 0f, 0f, null) + views.setImageViewBitmap(R.id.imageView, bmpResult) // Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ray650128/iosclockwidget/MainActivity.kt b/app/src/main/java/com/ray650128/iosclockwidget/MainActivity.kt index 9fcb698..76c1444 100644 --- a/app/src/main/java/com/ray650128/iosclockwidget/MainActivity.kt +++ b/app/src/main/java/com/ray650128/iosclockwidget/MainActivity.kt @@ -1,5 +1,6 @@ package com.ray650128.iosclockwidget +import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle @@ -7,5 +8,11 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + if (!AlarmService.isServiceRunning) { + startForegroundService( + Intent(this@MainActivity, AlarmService::class.java) + ) + } } } \ No newline at end of file diff --git a/app/src/main/res/drawable-nodpi/img_second_hand.png b/app/src/main/res/drawable-nodpi/img_second_hand.png new file mode 100644 index 0000000..8559103 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/img_second_hand.png differ diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..bdf91f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/img_clock_dash.xml b/app/src/main/res/drawable/img_clock_dash.xml index c3a88e6..9cb7d5f 100644 --- a/app/src/main/res/drawable/img_clock_dash.xml +++ b/app/src/main/res/drawable/img_clock_dash.xml @@ -1,68 +1,231 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/img_minute_hand.xml b/app/src/main/res/drawable/img_minute_hand.xml index 427ff45..a636987 100644 --- a/app/src/main/res/drawable/img_minute_hand.xml +++ b/app/src/main/res/drawable/img_minute_hand.xml @@ -34,8 +34,8 @@ android:height="20dp" android:top="132.5dp"> - - + + diff --git a/app/src/main/res/layout/i_o_s_clock_widget.xml b/app/src/main/res/layout/i_o_s_clock_widget.xml index 43b8fc9..c4645d9 100644 --- a/app/src/main/res/layout/i_o_s_clock_widget.xml +++ b/app/src/main/res/layout/i_o_s_clock_widget.xml @@ -1,4 +1,5 @@ + +