Kaynağa Gözat

feat(clock): adds different kinds of clocks (#186)

Co-authored-by: Joshua Kuestersteffen <jkuester@kuester7.com>
Klaus-Hendrik Wolf 2 yıl önce
ebeveyn
işleme
52aa530d1a

+ 3 - 1
app/src/main/java/com/sduduzog/slimlauncher/datasource/UnlauncherDataSource.kt

@@ -10,6 +10,7 @@ import com.jkuester.unlauncher.datastore.UnlauncherApps
 import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsMigrations
 import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsRepository
 import com.sduduzog.slimlauncher.datasource.apps.UnlauncherAppsSerializer
+import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesMigrations
 import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesRepository
 import com.sduduzog.slimlauncher.datasource.coreprefs.CorePreferencesSerializer
 import com.sduduzog.slimlauncher.datasource.quickbuttonprefs.QuickButtonPreferencesMigrations
@@ -30,7 +31,8 @@ private val Context.unlauncherAppsStore: DataStore<UnlauncherApps> by dataStore(
 
 private val Context.corePreferencesStore: DataStore<CorePreferences> by dataStore(
     fileName = "core_preferences.proto",
-    serializer = CorePreferencesSerializer
+    serializer = CorePreferencesSerializer,
+    produceMigrations = { _ -> CorePreferencesMigrations().get() }
 )
 
 class UnlauncherDataSource(context: Context, lifecycleScope: LifecycleCoroutineScope) {

+ 23 - 0
app/src/main/java/com/sduduzog/slimlauncher/datasource/coreprefs/CorePreferencesMigrations.kt

@@ -0,0 +1,23 @@
+package com.sduduzog.slimlauncher.datasource.coreprefs
+
+import androidx.datastore.core.DataMigration
+import com.jkuester.unlauncher.datastore.ClockType
+import com.jkuester.unlauncher.datastore.CorePreferences
+
+class CorePreferencesMigrations {
+    fun get(): List<DataMigration<CorePreferences>> {
+        return listOf(
+            object : DataMigration<CorePreferences> {
+                override suspend fun shouldMigrate(currentData: CorePreferences) = !currentData.hasClockType()
+
+                override suspend fun migrate(currentData: CorePreferences): CorePreferences {
+                    val prefBuilder = currentData.toBuilder()
+                    prefBuilder.clockType = ClockType.digital
+                    return prefBuilder.build()
+                }
+
+                override suspend fun cleanUp() {}
+            }
+        )
+    }
+}

+ 9 - 0
app/src/main/java/com/sduduzog/slimlauncher/datasource/coreprefs/CorePreferencesRepository.kt

@@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore
 import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
+import com.jkuester.unlauncher.datastore.ClockType
 import com.jkuester.unlauncher.datastore.CorePreferences
 import com.jkuester.unlauncher.datastore.SearchBarPosition
 import kotlinx.coroutines.flow.Flow
@@ -101,4 +102,12 @@ class CorePreferencesRepository(
             }
         }
     }
+
+    fun updateClockType(clockType: ClockType) {
+        lifecycleScope.launch {
+            corePreferencesStore.updateData {
+                it.toBuilder().setClockType(clockType).build()
+            }
+        }
+    }
 }

+ 35 - 0
app/src/main/java/com/sduduzog/slimlauncher/ui/dialogs/ChooseClockTypeDialog.kt

@@ -0,0 +1,35 @@
+package com.sduduzog.slimlauncher.ui.dialogs
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import com.jkuester.unlauncher.datastore.ClockType
+import com.sduduzog.slimlauncher.R
+import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class ChooseClockTypeDialog : DialogFragment(){
+
+    @Inject
+    lateinit var unlauncherDataSource: UnlauncherDataSource
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val builder = AlertDialog.Builder(requireContext())
+
+        val repo = unlauncherDataSource.corePreferencesRepo
+        val active = repo.get().clockType.number
+        builder.setTitle(R.string.choose_clock_type_dialog_title)
+        builder.setSingleChoiceItems(R.array.clock_type_array, active) {dialogInterface, i ->
+            dialogInterface.dismiss()
+            repo.updateClockType(ClockType.forNumber(i))
+        }
+        return builder.create()
+    }
+
+    companion object {
+        fun getInstance(): ChooseClockTypeDialog = ChooseClockTypeDialog()
+    }
+}

+ 98 - 0
app/src/main/java/com/sduduzog/slimlauncher/ui/main/AnalogClockView.kt

@@ -0,0 +1,98 @@
+package com.sduduzog.slimlauncher.ui.main
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import androidx.core.view.marginBottom
+import androidx.core.view.marginEnd
+import androidx.core.view.marginStart
+import androidx.core.view.marginTop
+import com.sduduzog.slimlauncher.R
+import java.util.Calendar
+import kotlin.math.max
+import kotlin.math.min
+
+class AnalogClockView(context: Context, attrs: AttributeSet) : ClockView(context, attrs) {
+    private var handPaint = getColorPaint(R.attr.colorAccent)
+    private var radius: Float
+    private var border: Float
+
+    // Length is given in fraction of radius, width is in pixels
+    private val handWidthHour = 10F
+    private val handWidthMinute = 5F
+    private val handLengthHour = .6F
+    private val handLengthMinute = .8F
+    private val tickWidth = 4F
+    private val tickLength = 1F - .1F
+
+    init {
+        handPaint.strokeWidth = handWidthMinute
+        handPaint.style = Paint.Style.STROKE
+        handPaint.strokeCap = Paint.Cap.ROUND
+
+        context.theme.obtainStyledAttributes(
+            attrs,
+            R.styleable.AnalogClockView,
+            0, 0).apply {
+            try {
+                radius = getDimension(R.styleable.AnalogClockView_radius, 200F)
+                border = getFloat(R.styleable.AnalogClockView_rim, 0F)
+            } finally {
+                recycle()
+            }
+        }
+    }
+
+    override fun onDraw(canvas : Canvas)
+    {
+        super.onDraw(canvas)
+        val calendar = Calendar.getInstance()
+
+        val hour = calendar[Calendar.HOUR]
+        val minute = calendar[Calendar.MINUTE]
+
+        val cx = width / 2F
+        val cy = height / 2F + marginTop / 2F
+
+        handPaint.strokeWidth = border
+        if (border > 2) canvas.drawCircle(cx, cy, radius, handPaint)
+        handPaint.strokeWidth = tickWidth
+        drawTicks(canvas, cx, cy)
+        handPaint.strokeWidth = handWidthHour
+        drawHand(canvas, cx, cy, radius * handLengthHour, hour * 5)
+        handPaint.strokeWidth = handWidthMinute
+        drawHand(canvas, cx, cy, radius * handLengthMinute, minute)
+    }
+
+    private fun drawTicks(canvas: Canvas, cx : Float, cy : Float) {
+        canvas.save()
+        for (i in 1..12) {
+            canvas.rotate(30f, cx, cy)
+            canvas.drawLine(cx, cy + radius, cx, cy + (radius * tickLength), handPaint)
+        }
+        canvas.restore()
+    }
+
+    private fun drawHand(canvas: Canvas, cx : Float, cy : Float, size : Float, minute : Int) {
+        val angle : Float = (minute.toFloat() * 6)
+
+        canvas.save()
+        canvas.rotate(angle, cx, cy)
+        canvas.drawLine(cx, cy, cx, cy - size, handPaint)
+        canvas.restore()
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        val dim = max(min(suggestedMinimumWidth, suggestedMinimumHeight),
+            2 * radius.toInt()) + 4 * border.toInt()
+        val minw: Int = dim + paddingLeft + paddingRight + marginStart + marginEnd
+        val w: Int = resolveSizeAndState(minw, widthMeasureSpec, 0)
+
+        val minh: Int = dim + paddingBottom + paddingTop + marginTop + marginBottom
+        val h: Int = resolveSizeAndState(minh, heightMeasureSpec, 0)
+
+        setMeasuredDimension(w, h)
+    }
+
+}

+ 95 - 0
app/src/main/java/com/sduduzog/slimlauncher/ui/main/BinaryClockView.kt

@@ -0,0 +1,95 @@
+package com.sduduzog.slimlauncher.ui.main
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.RectF
+import android.util.AttributeSet
+import com.sduduzog.slimlauncher.R
+import java.util.Calendar
+
+class BinaryClockView(context: Context, attrs: AttributeSet)
+    : ClockView(context, attrs){
+
+    private var offPaint = getColorPaint(R.attr.colorAccent)
+    private var onPaint  = getColorPaint(R.attr.colorAccent)
+    private var bitSize: Float
+    private var border: Float
+    private var distance: Float
+    private val bounds = RectF(0F, 0F, 0F, 0F)
+    private var is24Hour: Boolean = false
+
+    init {
+        onPaint.style  = Paint.Style.FILL_AND_STROKE
+        offPaint.style = Paint.Style.STROKE
+        context.theme.obtainStyledAttributes(
+            attrs,
+            R.styleable.BinaryClockView,
+            0, 0).apply {
+            try {
+                bitSize = getFloat(R.styleable.BinaryClockView_bitSize, 40F)
+                border = getFloat(R.styleable.BinaryClockView_border, 4F)
+                distance = getFloat(R.styleable.BinaryClockView_distance, 0F)
+            } finally {
+                recycle()
+            }
+        }
+    }
+
+    override fun onDraw(canvas : Canvas)
+    {
+        super.onDraw(canvas)
+        val calendar = Calendar.getInstance()
+
+        val middle = if (distance > 0) distance * 3 + bitSize * 2 else height.toFloat() / 2
+        var hour = calendar[if (is24Hour) Calendar.HOUR_OF_DAY else Calendar.HOUR]
+        if (hour == 0 && calendar[Calendar.AM] != 0) hour = 12
+        bounds.set(0f, 0f, width.toFloat(), middle)
+        renderBits(canvas, bounds, if (is24Hour) 5 else 4, hour)
+
+        bounds.set(0f, middle, width.toFloat(), middle * 2)
+        val minute = calendar[Calendar.MINUTE]
+        renderBits(canvas, bounds, 6, minute)
+    }
+
+    private fun renderBits(
+        canvas: Canvas,
+        bounds: RectF,
+        nBits: Int,
+        value: Int
+    ) {
+        val cw = if (distance > 0)
+                    distance + 2 * bitSize
+                 else
+                     bounds.width() / 18 // divide width by maximal number of bits * 3
+        val ch = bounds.height()
+        val cpx = cw / 2 - bitSize
+        val cpy = ch / 2 - bitSize
+        var x = bounds.right  - cpx - bitSize
+        val y = bounds.bottom - cpy - bitSize
+
+        var bit = nBits
+        var leftover = value
+        while (bit > 0) {
+            canvas.drawCircle(x, y, bitSize, if ((leftover and 1) != 1) offPaint else onPaint)
+            x -= cw
+            bit--
+            leftover = leftover.ushr(1)
+        }
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        // Try for a width based on your minimum.
+        val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth +
+                12 * bitSize.toInt() + 7 * distance.toInt()
+        val w: Int = resolveSizeAndState(minw, widthMeasureSpec, 0)
+
+        // Whatever the width is, ask for a height that lets the pie get as big as
+        // it can.
+        val minh: Int = paddingBottom + paddingTop +
+                4 * bitSize.toInt() + 5 * distance.toInt()
+        val h: Int = resolveSizeAndState(minh, heightMeasureSpec, 0)
+
+        setMeasuredDimension(w, h)
+    }
+}

+ 36 - 0
app/src/main/java/com/sduduzog/slimlauncher/ui/main/ClockView.kt

@@ -0,0 +1,36 @@
+package com.sduduzog.slimlauncher.ui.main
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+
+abstract class ClockView(context: Context, attrs: AttributeSet) : View(context, attrs) {
+
+    private fun getColor(constant: Int): Int {
+        val tv = TypedValue()
+        context.theme.resolveAttribute(constant, tv, true)
+        return tv.data
+    }
+
+    fun getColorPaint(constant: Int): Paint {
+        val paint = Paint()
+        paint.reset()
+        paint.isAntiAlias = true
+        paint.style = Paint.Style.FILL
+        paint.color = getColor(constant)
+        return paint
+    }
+
+    fun updateClock () {
+        requestLayout()
+        invalidate()
+    }
+
+    override fun onDraw(canvas: Canvas) = super.onDraw(canvas)
+
+    abstract override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
+
+}

+ 47 - 16
app/src/main/java/com/sduduzog/slimlauncher/ui/main/HomeFragment.kt

@@ -15,9 +15,11 @@ import android.provider.AlarmClock
 import android.provider.CalendarContract
 import android.provider.MediaStore
 import android.provider.Settings
+import android.text.format.DateFormat
 import android.view.LayoutInflater
 import android.view.MenuItem
 import android.view.View
+import android.view.View.OnClickListener
 import android.view.ViewGroup
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
@@ -31,6 +33,7 @@ import androidx.fragment.app.viewModels
 import androidx.lifecycle.lifecycleScope
 import androidx.navigation.Navigation
 import androidx.recyclerview.widget.LinearLayoutManager
+import com.jkuester.unlauncher.datastore.ClockType
 import com.jkuester.unlauncher.datastore.SearchBarPosition
 import com.jkuester.unlauncher.datastore.UnlauncherApp
 import com.sduduzog.slimlauncher.R
@@ -44,11 +47,20 @@ import com.sduduzog.slimlauncher.ui.dialogs.RenameAppDisplayNameDialog
 import com.sduduzog.slimlauncher.utils.BaseFragment
 import com.sduduzog.slimlauncher.utils.OnLaunchAppListener
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.android.synthetic.main.home_fragment_default.*
-import kotlinx.android.synthetic.main.home_fragment_content.*
+import kotlinx.android.synthetic.main.home_fragment_content.app_drawer_edit_text
+import kotlinx.android.synthetic.main.home_fragment_content.app_drawer_fragment_list
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_analog_time
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_bin_time
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_call
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_camera
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_date
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_list
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_list_exp
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_options
+import kotlinx.android.synthetic.main.home_fragment_content.home_fragment_time
+import kotlinx.android.synthetic.main.home_fragment_default.home_fragment
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
-import java.text.DateFormat
 import java.text.SimpleDateFormat
 import java.util.Date
 import java.util.Locale
@@ -124,6 +136,14 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
 
         val showSearchBar = unlauncherDataSource.corePreferencesRepo.showSearchField
         app_drawer_edit_text.visibility = if (showSearchBar) View.VISIBLE else View.GONE
+
+        unlauncherDataSource.corePreferencesRepo.liveData().observe(viewLifecycleOwner){ corePreferences ->
+            val clockType = corePreferences.clockType
+            home_fragment_time.visibility = if(clockType == ClockType.digital) View.VISIBLE else View.GONE
+            home_fragment_analog_time.visibility = if(clockType == ClockType.analog) View.VISIBLE else View.GONE
+            home_fragment_bin_time.visibility = if(clockType == ClockType.binary) View.VISIBLE else View.GONE
+            home_fragment_date.visibility = if(clockType != ClockType.none) View.VISIBLE else View.GONE
+        }
     }
 
     override fun onStart() {
@@ -166,7 +186,7 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
 
     private fun setEventListeners() {
 
-        home_fragment_time.setOnClickListener {
+        val launchShowAlarms = OnClickListener {
             try {
                 val intent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
                 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
@@ -176,6 +196,9 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
                 // Do nothing, we've failed :(
             }
         }
+        home_fragment_time.setOnClickListener(launchShowAlarms)
+        home_fragment_analog_time.setOnClickListener(launchShowAlarms)
+        home_fragment_bin_time.setOnClickListener(launchShowAlarms)
 
         home_fragment_date.setOnClickListener {
             try {
@@ -290,21 +313,29 @@ class HomeFragment : BaseFragment(), OnLaunchAppListener {
     }
 
     fun updateClock() {
-        val active = context?.getSharedPreferences(getString(R.string.prefs_settings), Context.MODE_PRIVATE)
-                ?.getInt(getString(R.string.prefs_settings_key_time_format), 0)
-        val date = Date()
-
-        val currentLocale = Locale.getDefault()
-        val fWatchTime = when(active) {
-            1 -> SimpleDateFormat("H:mm", currentLocale)
-            2 -> SimpleDateFormat("h:mm aa", currentLocale)
-            else -> DateFormat.getTimeInstance(DateFormat.SHORT)
+        updateDate()
+        when (unlauncherDataSource.corePreferencesRepo.get().clockType) {
+            ClockType.digital -> updateClockDigital()
+            ClockType.analog -> home_fragment_analog_time.updateClock()
+            ClockType.binary -> home_fragment_bin_time.updateClock()
+            else -> {}
         }
-        home_fragment_time.text = fWatchTime.format(date)
+    }
 
+    private fun updateClockDigital () {
+        val timeFormat = context?.getSharedPreferences(getString(R.string.prefs_settings), Context.MODE_PRIVATE)
+            ?.getInt(getString(R.string.prefs_settings_key_time_format), 0)
+        val fWatchTime = when (timeFormat) {
+            1 -> SimpleDateFormat("H:mm", Locale.getDefault())
+            2 -> SimpleDateFormat("h:mm aa", Locale.getDefault())
+            else -> DateFormat.getTimeFormat(context)
+        }
+        home_fragment_time.text = fWatchTime.format(Date())
+    }
 
-        val fWatchDate = SimpleDateFormat("EEE, MMM dd", currentLocale)
-        home_fragment_date.text = fWatchDate.format(date)
+    private fun updateDate() {
+        val fWatchDate = SimpleDateFormat(getString(R.string.main_date_format), Locale.getDefault())
+        home_fragment_date.text = fWatchDate.format(Date())
     }
 
     override fun onLaunch(app: HomeApp, view: View) {

+ 5 - 0
app/src/main/java/com/sduduzog/slimlauncher/ui/options/OptionsFragment.kt

@@ -16,6 +16,7 @@ import com.sduduzog.slimlauncher.R
 import com.sduduzog.slimlauncher.datasource.UnlauncherDataSource
 import com.sduduzog.slimlauncher.ui.dialogs.ChangeThemeDialog
 import com.sduduzog.slimlauncher.ui.dialogs.ChooseAlignmentDialog
+import com.sduduzog.slimlauncher.ui.dialogs.ChooseClockTypeDialog
 import com.sduduzog.slimlauncher.ui.dialogs.ChooseTimeFormatDialog
 import com.sduduzog.slimlauncher.utils.BaseFragment
 import com.sduduzog.slimlauncher.utils.createTitleAndSubtitleText
@@ -57,6 +58,10 @@ class OptionsFragment : BaseFragment() {
             val chooseTimeFormatDialog = ChooseTimeFormatDialog.getInstance()
             chooseTimeFormatDialog.showNow(childFragmentManager, "TIME_FORMAT_CHOOSER")
         }
+        options_fragment_choose_clock_type.setOnClickListener {
+            val chooseClockTypeDialog = ChooseClockTypeDialog.getInstance()
+            chooseClockTypeDialog.showNow(childFragmentManager, "CLOCK_TYPE_CHOOSER")
+        }
         options_fragment_choose_alignment.setOnClickListener {
             val chooseAlignmentDialog = ChooseAlignmentDialog.getInstance()
             chooseAlignmentDialog.showNow(childFragmentManager, "ALIGNMENT_CHOOSER")

+ 9 - 1
app/src/main/proto/core_preferences.proto

@@ -10,9 +10,17 @@ message CorePreferences {
   SearchBarPosition search_bar_position = 4;
   bool show_drawer_headings = 5;
   bool search_all_apps_in_drawer = 6;
+  optional ClockType clock_type = 7;
 }
 
 enum SearchBarPosition {
   top = 0;
   bottom = 1;
-}
+}
+
+enum ClockType {
+  none = 0;
+  digital = 1;
+  analog = 2;
+  binary = 3;
+}

+ 16 - 0
app/src/main/res/layout/home_fragment_content.xml

@@ -2,6 +2,7 @@
 <merge 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"
+    xmlns:custom="http://schemas.android.com/apk/res-auto"
     android:layout_marginStart="8dp"
     android:layout_marginLeft="8dp"
     android:layout_marginEnd="8dp"
@@ -45,6 +46,21 @@
         app:srcCompat="@drawable/ic_cog"
         tools:ignore="ContentDescription" />
 
+    <com.sduduzog.slimlauncher.ui.main.BinaryClockView
+        android:id="@+id/home_fragment_bin_time"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        custom:bitSize="20"
+        custom:border="2"
+        custom:distance="10" />
+
+    <com.sduduzog.slimlauncher.ui.main.AnalogClockView
+        android:id="@+id/home_fragment_analog_time"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        custom:radius="@dimen/_60sdp"
+        custom:rim="0" />
+
     <TextView
         android:id="@+id/home_fragment_time"
         android:layout_width="wrap_content"

+ 10 - 1
app/src/main/res/layout/options_fragment.xml

@@ -76,6 +76,15 @@
                 android:textSize="@dimen/font_size_customize_options"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/options_fragment_change_theme" />
+	        <TextView
+	            android:id="@+id/options_fragment_choose_clock_type"
+	            android:layout_width="match_parent"
+	            android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/margin_list_items"
+	            android:text="@string/options_fragment_choose_clock_type"
+	            android:textAppearance="@style/TextAppearance.AppCompat"
+	            android:textSize="@dimen/font_size_customize_options"
+	            app:layout_constraintTop_toBottomOf="@id/options_fragment_choose_time_format" />
             <TextView
                 android:id="@+id/options_fragment_toggle_status_bar"
                 android:layout_width="match_parent"
@@ -85,7 +94,7 @@
                 android:textAppearance="@style/TextAppearance.AppCompat"
                 android:textSize="@dimen/font_size_customize_options"
                 app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/options_fragment_choose_time_format" />
+                app:layout_constraintTop_toBottomOf="@+id/options_fragment_choose_clock_type" />
             <TextView
                 android:id="@+id/options_fragment_customise_apps"
                 android:layout_width="match_parent"

+ 4 - 2
app/src/main/res/values-de/strings.xml

@@ -29,8 +29,6 @@
         <item>Rechts</item>
     </string-array>
 
-    <string name="choose_theme_dialog_title">Stil auswählen</string>
-    <string name="app_name">Unlauncher</string>
     <string name="choose_time_format_dialog_title">Zeitformat</string>
     <string name="shown">angezeigt</string>
     <string name="hidden">versteckt</string>
@@ -41,6 +39,7 @@
     <string name="menu_rename">Umbenennen</string>
     <string name="options_fragment_change_theme">Stil auswählen</string>
     <string name="options_fragment_choose_time_format">Zeitformat</string>
+    <string name="options_fragment_choose_clock_type">Uhrdarstellung</string>
     <string name="options_fragment_customise_apps">Apps einstellen</string>
     <string name="options_fragment_device_settings">Geräteeinstellungen</string>
     <string name="add_apps_fragment_search_apps">Appsuche</string>
@@ -69,6 +68,7 @@
     <string name="customize_app_drawer_fragment_auto_theme_wallpaper_text">Themenhintergrund verwenden</string>
     <string name="customize_app_drawer_fragment_auto_theme_wallpaper_subtext_no_default_launcher">Unlauncher muss die standard Start-App sein</string>
     <string name="choose_search_bar_position_dialog_title">Setze Position des Suchfelds</string>
+    <string name="choose_theme_dialog_title">Stil auswählen</string>
     <string name="menu_reset">Zurücksetzen</string>
     <string name="customize_app_drawer_fragment_open_keyboard_subtitle">Tastatur anzeigen, wenn der App-Drawer angezeigt wird</string>
     <string name="remove_all_apps_dialog_title">Alle Apps entfernen</string>
@@ -80,4 +80,6 @@
     <string name="rename_app">Umbenennen</string>
     <string name="uninstall">Deinstallieren</string>
     <string name="choose_alignment_dialog_title">Ausrichtung wählen</string>
+    <string name="main_date_format">EE dd. MMMM</string>
+    <string name="choose_clock_type_dialog_title">Uhrdarstellung</string>
 </resources>

+ 9 - 0
app/src/main/res/values/attrs.xml

@@ -2,4 +2,13 @@
 <resources>
     <attr name="headerTextColor" format="reference"/>
     <attr name="switchTextColor" format="reference" />
+    <declare-styleable name="BinaryClockView">
+        <attr name="bitSize" format="integer" />
+        <attr name="border" format="integer" />
+        <attr name="distance" format="integer" />
+    </declare-styleable>
+    <declare-styleable name="AnalogClockView">
+        <attr name="radius" format="dimension" />
+        <attr name="rim" format="integer" />
+    </declare-styleable>
 </resources>

+ 11 - 2
app/src/main/res/values/strings.xml

@@ -25,6 +25,13 @@
         <item>12 Hour</item>
     </string-array>
 
+    <string-array name="clock_type_array">
+        <item>No clock</item>
+        <item>Digital clock</item>
+        <item>Analog clock</item>
+        <item>Binary clock</item>
+    </string-array>
+
     <string-array name="quick_button_array">
         <item>Show</item>
         <item>Hide</item>
@@ -47,6 +54,7 @@
     <string name="options_fragment_device_settings">Device Settings</string>
     <string name="options_fragment_change_theme">Change Theme</string>
     <string name="options_fragment_choose_time_format">Choose Time Format</string>
+    <string name="options_fragment_choose_clock_type">Choose Clock Type</string>
     <string name="options_fragment_customise_apps">Customise Apps</string>
     <string name="options_fragment_choose_alignment">Choose Alignment</string>
     <string name="options_fragment_customize_quick_buttons">Customise Quick Buttons</string>
@@ -79,6 +87,8 @@
     <string name="menu_remove">Remove</string>
     <string name="choose_theme_dialog_title">Choose Theme</string>
     <string name="choose_search_bar_position_dialog_title">Set position of the Search Field</string>
+    <string name="choose_clock_type_dialog_title">Choose Clock Type</string>
+    <string name="choose_alignment_dialog_title">Choose alignment</string>
     <string name="add_apps_fragment_search_apps">Search apps</string>
     <string name="customise_apps_fragment_remove_all">Remove all</string>
     <string name="menu_reset">Reset</string>
@@ -90,6 +100,5 @@
     <string name="hide_app">Hide App</string>
     <string name="rename_app">Rename App</string>
     <string name="uninstall">Uninstall</string>
-    <string name="choose_alignment_dialog_title">Choose alignment</string>
-
+    <string name="main_date_format">EEE, MMM dd</string>
 </resources>

+ 52 - 3
app/src/main/res/xml/home_motion_scene.xml

@@ -13,11 +13,33 @@
     </Transition>
 
     <ConstraintSet android:id="@+id/home_motion_01">
+        <Constraint
+            android:id="@+id/home_fragment_analog_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/_22sdp"
+            app:visibilityMode="ignore"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"/>
+        <Constraint
+            android:id="@+id/home_fragment_bin_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/_42sdp"
+            android:padding="@dimen/_4sdp"
+            app:visibilityMode="ignore"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.506"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"/>
         <Constraint
             android:id="@+id/home_fragment_time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/_42sdp"
+            app:visibilityMode="ignore"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintHorizontal_bias="0.506"
             app:layout_constraintStart_toStartOf="parent"
@@ -28,9 +50,11 @@
             android:layout_height="wrap_content"
             android:padding="@dimen/_4sdp"
             android:textSize="@dimen/font_size_home_date"
+            android:layout_marginTop="@dimen/_94sdp"
+            app:visibilityMode="ignore"
             app:layout_constraintEnd_toEndOf="@+id/home_fragment_time"
             app:layout_constraintStart_toStartOf="@+id/home_fragment_time"
-            app:layout_constraintTop_toBottomOf="@+id/home_fragment_time" />
+            app:layout_constraintTop_toTopOf="parent" />
         <Constraint
             android:id="@+id/home_fragment_list"
             android:layout_width="0dp"
@@ -118,9 +142,33 @@
             android:id="@+id/home_fragment_time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/_64sdp"
+            android:layout_marginTop="@dimen/_44sdp"
             android:alpha="-1"
-            app:layout_constraintBottom_toTopOf="@+id/home_fragment_date"
+            app:visibilityMode="ignore"
+            app:layout_constraintBottom_toTopOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent" />
+        <Constraint
+            android:id="@+id/home_fragment_analog_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="0px"
+            android:alpha="0"
+            app:visibilityMode="ignore"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintBottom_toTopOf="parent"
+            app:layout_constraintTop_toTopOf="parent"/>
+        <Constraint
+            android:id="@+id/home_fragment_bin_time"
+            android:layout_width="wrap_content"
+            android:layout_height="0px"
+            android:layout_marginTop="0px"
+            android:alpha="-1"
+            app:visibilityMode="ignore"
+            app:layout_constraintBottom_toTopOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintHorizontal_bias="0.5"
             app:layout_constraintStart_toStartOf="parent" />
@@ -129,6 +177,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:alpha="-1"
+            app:visibilityMode="ignore"
             app:layout_constraintBottom_toTopOf="parent"
             app:layout_constraintEnd_toEndOf="@+id/home_fragment_time"
             app:layout_constraintStart_toStartOf="@+id/home_fragment_time" />