Skip to content

Commit 30fdab8

Browse files
committed
Implement JetPack Compose
This commit reimplements the UI with JetPack Compose.
1 parent 442f44f commit 30fdab8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1759
-1032
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
language: android
2+
jdk:
3+
- openjdk11
24
android:
35
components:
46
- tools

app/build.gradle

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
apply plugin: 'com.android.application'
22
apply plugin: 'kotlin-android'
33
apply plugin: 'kotlin-kapt'
4-
apply plugin: "androidx.navigation.safeargs.kotlin"
4+
apply plugin: "androidx.navigation.safeargs"
55
apply plugin: 'com.google.firebase.crashlytics'
66
apply plugin: 'com.google.gms.google-services'
77
apply plugin: 'dagger.hilt.android.plugin'
8-
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
98
apply plugin: 'com.google.firebase.firebase-perf'
109
apply plugin: 'com.github.triplet.play'
1110

@@ -28,10 +27,15 @@ android {
2827
// Version info
2928
buildConfigField 'String', 'GIT_SHA', "\"${project.ext.gitHash}\""
3029

30+
vectorDrawables {
31+
useSupportLibrary true
32+
}
33+
3134
javaCompileOptions.annotationProcessorOptions.arguments['room.schemaLocation'] = rootProject.file('schemas').toString()
3235
}
3336
buildFeatures {
3437
viewBinding true
38+
compose true
3539
}
3640
compileOptions {
3741
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -47,6 +51,7 @@ android {
4751
"-Xopt-in=kotlinx.coroutines.FlowPreview",
4852
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
4953
]
54+
useIR = true
5055
}
5156
testOptions {
5257
unitTests {
@@ -105,6 +110,9 @@ android {
105110
exclude '**/NOTICE.txt'
106111
exclude '**/*.gwt.xml'
107112
}
113+
composeOptions {
114+
kotlinCompilerExtensionVersion Versions.androidXCompose
115+
}
108116
}
109117

110118
dependencies {
@@ -136,17 +144,31 @@ dependencies {
136144
implementation Libs.androidx_fragment
137145
implementation Libs.androidx_hilt_work
138146
implementation Libs.androidx_lifecycle_viewmodel
147+
implementation Libs.androidx_lifecycle_livedata
139148
implementation Libs.androidx_lifecycle_java8
149+
implementation Libs.androidx_lifecycle_runtime
140150
implementation Libs.androidx_lifecycle_process
141151
implementation Libs.androidx_navigation_fragment
142152
implementation Libs.androidx_navigation_ui
153+
implementation "androidx.navigation:navigation-compose:$Versions.androidXNavigation"
143154
implementation Libs.androidx_preference
144155
implementation Libs.androidx_recyclerview
145156
implementation Libs.androidx_recyclerview_selection
146157
implementation Libs.androidx_room_runtime
147158
implementation Libs.androidx_room_ktx
148159
implementation Libs.androidx_work_runtime
149160
implementation Libs.androidx_work_gcm
161+
implementation 'com.google.android.material:material:1.3.0'
162+
implementation 'androidx.activity:activity-compose:1.3.0-alpha08'
163+
implementation "androidx.compose.ui:ui:$Versions.androidXCompose"
164+
implementation "androidx.compose.foundation:foundation:$Versions.androidXCompose"
165+
implementation "androidx.compose.material:material:$Versions.androidXCompose"
166+
implementation "androidx.compose.material:material-icons-core:$Versions.androidXCompose"
167+
implementation "androidx.compose.material:material-icons-extended:$Versions.androidXCompose"
168+
implementation "androidx.compose.ui:ui-tooling:$Versions.androidXCompose"
169+
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha05'
170+
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha02'
171+
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$Versions.androidXCompose"
150172
kapt Libs.androidx_room_compiler
151173
kapt Libs.androidx_hilt_compiler
152174

@@ -182,8 +204,8 @@ dependencies {
182204
implementation Libs.kotlinCoroutinesAndroid
183205

184206
// LeakCanary
185-
debugImplementation Libs.leakCanary
186-
implementation Libs.leakCanaryPlumberAndroid
207+
// debugImplementation Libs.leakCanary
208+
// implementation Libs.leakCanaryPlumberAndroid
187209

188210
// Logging
189211
implementation Libs.slf4jAndroidLogger

app/src/main/AndroidManifest.xml

+7-9
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,19 @@
6565
android:name=".receivers.UpdateReceiver"
6666
android:exported="false" />
6767

68+
<receiver
69+
android:name=".receivers.PinWidgetSuccessReceiver"
70+
android:exported="false" />
71+
6872
<service
6973
android:name=".appwidget.WidgetService"
7074
android:permission="android.permission.BIND_REMOTEVIEWS" />
7175

7276
<!-- Disable auto-init of WorkManager -->
7377
<provider
74-
android:name="androidx.startup.InitializationProvider"
75-
android:authorities="${applicationId}.androidx-startup"
76-
android:exported="false"
77-
tools:node="merge">
78-
<meta-data
79-
android:name="androidx.work.impl.WorkManagerInitializer"
80-
android:value="androidx.startup"
81-
tools:node="remove" />
82-
</provider>
78+
android:name="androidx.work.impl.WorkManagerInitializer"
79+
android:authorities="${applicationId}.workmanager-init"
80+
tools:node="remove" />
8381

8482
<!-- Configure Firebase Analytics -->
8583
<meta-data
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,26 @@
11
package de.psdev.devdrawer
22

3-
import android.os.Bundle
4-
import android.view.LayoutInflater
5-
import android.view.View
6-
import android.view.ViewGroup
7-
import androidx.annotation.CallSuper
83
import androidx.annotation.StringRes
94
import androidx.fragment.app.Fragment
105
import androidx.lifecycle.LifecycleCoroutineScope
116
import androidx.lifecycle.lifecycleScope
12-
import androidx.viewbinding.ViewBinding
137
import de.psdev.devdrawer.analytics.TrackingService
148
import javax.inject.Inject
159

16-
abstract class BaseFragment<T : ViewBinding> : Fragment() {
10+
open class BaseFragment : Fragment() {
1711

1812
@Inject
1913
lateinit var trackingService: TrackingService
20-
21-
private var _binding: T? = null
22-
// This property is only valid between onCreateView and onDestroyView.
23-
protected val binding get() = _binding!!
24-
2514
protected var toolbarTitle: CharSequence
2615
get() = requireActivity().title
2716
set(value) {
2817
requireActivity().title = value
2918
}
30-
31-
final override fun onCreateView(
32-
inflater: LayoutInflater,
33-
container: ViewGroup?,
34-
savedInstanceState: Bundle?
35-
): View = createViewBinding(inflater, container, savedInstanceState).also { viewBinding ->
36-
_binding = viewBinding
37-
}.root
38-
39-
protected abstract fun createViewBinding(
40-
inflater: LayoutInflater,
41-
container: ViewGroup?,
42-
savedInstanceState: Bundle?
43-
): T
44-
45-
@CallSuper
46-
override fun onDestroyView() {
47-
super.onDestroyView()
48-
_binding = null
49-
}
19+
val Fragment.viewLifecycleScope: LifecycleCoroutineScope
20+
get() = viewLifecycleOwner.lifecycleScope
5021

5122
protected fun updateToolbarTitle(@StringRes resId: Int) {
5223
requireActivity().setTitle(resId)
5324
trackingService.trackScreen(this::class.java, getString(resId))
5425
}
55-
56-
val Fragment.viewLifecycleScope: LifecycleCoroutineScope
57-
get() = viewLifecycleOwner.lifecycleScope
58-
59-
}
26+
}

app/src/main/java/de/psdev/devdrawer/DevDrawerApplication.kt

+10-7
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,23 @@ class DevDrawerApplication: Application(), Configuration.Provider {
2929
registerAppInstallationReceiver()
3030
setupWorkers()
3131
}.let {
32-
logger.warn("{} version {} ({}) took {}ms to init", this::class.java.simpleName, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, it)
32+
logger.warn(
33+
"{} version {} ({}) took {}ms to init",
34+
this::class.java.simpleName,
35+
BuildConfig.VERSION_NAME,
36+
BuildConfig.VERSION_CODE,
37+
it
38+
)
3339
}
3440
}
3541

3642
// ==========================================================================================================================
3743
// Configuration.Provider
3844
// ==========================================================================================================================
3945

40-
override fun getWorkManagerConfiguration(): Configuration {
41-
logger.warn { "getWorkManagerConfiguration" }
42-
return Configuration.Builder()
43-
.setWorkerFactory(workerFactory)
44-
.build()
45-
}
46+
override fun getWorkManagerConfiguration(): Configuration = Configuration.Builder()
47+
.setWorkerFactory(workerFactory)
48+
.build()
4649

4750
// ==========================================================================================================================
4851
// Private API
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package de.psdev.devdrawer
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import androidx.annotation.CallSuper
8+
import androidx.viewbinding.ViewBinding
9+
10+
abstract class ViewBindingBaseFragment<T : ViewBinding> : BaseFragment() {
11+
12+
private var _binding: T? = null
13+
14+
// This property is only valid between onCreateView and onDestroyView.
15+
protected val binding get() = _binding!!
16+
17+
final override fun onCreateView(
18+
inflater: LayoutInflater,
19+
container: ViewGroup?,
20+
savedInstanceState: Bundle?
21+
): View = createViewBinding(inflater, container, savedInstanceState).also { viewBinding ->
22+
_binding = viewBinding
23+
}.root
24+
25+
protected abstract fun createViewBinding(
26+
inflater: LayoutInflater,
27+
container: ViewGroup?,
28+
savedInstanceState: Bundle?
29+
): T
30+
31+
@CallSuper
32+
override fun onDestroyView() {
33+
super.onDestroyView()
34+
_binding = null
35+
}
36+
37+
}

app/src/main/java/de/psdev/devdrawer/about/AboutFragment.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import com.mikepenz.aboutlibraries.Libs
1313
import com.mikepenz.aboutlibraries.LibsBuilder
1414
import com.mikepenz.aboutlibraries.util.LibsListenerImpl
1515
import dagger.hilt.android.AndroidEntryPoint
16-
import de.psdev.devdrawer.BaseFragment
1716
import de.psdev.devdrawer.R
17+
import de.psdev.devdrawer.ViewBindingBaseFragment
1818
import de.psdev.devdrawer.databinding.FragmentAboutBinding
1919
import de.psdev.devdrawer.utils.consume
2020

2121
@AndroidEntryPoint
22-
class AboutFragment : BaseFragment<FragmentAboutBinding>() {
22+
class AboutFragment : ViewBindingBaseFragment<FragmentAboutBinding>() {
2323

2424
override fun createViewBinding(
2525
inflater: LayoutInflater,

app/src/main/java/de/psdev/devdrawer/appwidget/DDWidgetProvider.kt

+6-33
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ import android.appwidget.AppWidgetProvider
66
import android.content.Context
77
import android.content.Intent
88
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
9-
import android.graphics.Color
109
import android.net.Uri
1110
import android.widget.RemoteViews
1211
import dagger.hilt.android.AndroidEntryPoint
1312
import de.psdev.devdrawer.R
1413
import de.psdev.devdrawer.database.DevDrawerDatabase
1514
import de.psdev.devdrawer.database.Widget
16-
import de.psdev.devdrawer.database.WidgetProfile
1715
import de.psdev.devdrawer.receivers.UpdateReceiver
18-
import de.psdev.devdrawer.utils.Constants
1916
import de.psdev.devdrawer.utils.textColorForBackground
2017
import de.psdev.devdrawer.widgets.WidgetConfigActivity
2118
import kotlinx.coroutines.Dispatchers
@@ -41,34 +38,6 @@ class DDWidgetProvider : AppWidgetProvider() {
4138
// AppWidgetProvider
4239
// ==========================================================================================================================
4340

44-
override fun onReceive(context: Context, intent: Intent) {
45-
super.onReceive(context, intent)
46-
when (intent.action) {
47-
Constants.ACTION_WIDGET_PINNED -> GlobalScope.launch(Dispatchers.IO) {
48-
val widgetDao = devDrawerDatabase.widgetDao()
49-
val widgetProfileDao = devDrawerDatabase.widgetProfileDao()
50-
val defaultWidgetProfile = widgetProfileDao.findAll().firstOrNull()
51-
?: WidgetProfile(name = "Default").also {
52-
widgetProfileDao.insert(it)
53-
}
54-
val widgetId = intent.getIntExtra(
55-
AppWidgetManager.EXTRA_APPWIDGET_ID,
56-
AppWidgetManager.INVALID_APPWIDGET_ID
57-
)
58-
59-
// Create entries in database
60-
val widget = Widget(
61-
id = widgetId,
62-
name = "Widget $widgetId",
63-
color = Color.BLACK,
64-
profileId = defaultWidgetProfile.id
65-
)
66-
widgetDao.insert(widget)
67-
UpdateReceiver.send(context)
68-
}
69-
}
70-
}
71-
7241
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
7342
super.onUpdate(context, appWidgetManager, appWidgetIds)
7443
GlobalScope.launch(Dispatchers.IO) {
@@ -126,8 +95,12 @@ class DDWidgetProvider : AppWidgetProvider() {
12695
val configActivityIntent = WidgetConfigActivity.createStartIntent(context, widget.id)
12796
configActivityIntent.putExtra("from_widget", true)
12897
configActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
129-
val configActivityPendingIntent =
130-
PendingIntent.getActivity(context, 0, configActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
98+
val configActivityPendingIntent = PendingIntent.getActivity(
99+
context,
100+
0,
101+
configActivityIntent,
102+
PendingIntent.FLAG_UPDATE_CURRENT
103+
)
131104
widgetView.setOnClickPendingIntent(R.id.btn_settings, configActivityPendingIntent)
132105

133106
// Apps list

app/src/main/java/de/psdev/devdrawer/database/WidgetDao.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package de.psdev.devdrawer.database
22

33
import androidx.room.Dao
44
import androidx.room.Query
5+
import androidx.room.Transaction
56
import kotlinx.coroutines.flow.Flow
67

78
@Dao
8-
abstract class WidgetDao : BaseDao<Widget>() {
9+
abstract class WidgetDao: BaseDao<Widget>() {
910

1011
@Query("SELECT * FROM widgets")
1112
abstract suspend fun findAll(): List<Widget>
@@ -19,6 +20,10 @@ abstract class WidgetDao : BaseDao<Widget>() {
1920
@Query("SELECT * FROM widgets WHERE id = :id")
2021
abstract suspend fun findById(id: Int): Widget?
2122

23+
@Transaction
24+
@Query("SELECT * FROM widgets WHERE id = :id")
25+
abstract fun widgetWithIdObservable(id: Int): Flow<Widget>
26+
2227
@Query("DELETE FROM widgets WHERE id IN (:ids)")
2328
abstract suspend fun deleteByIds(ids: List<Int>)
2429

app/src/main/java/de/psdev/devdrawer/database/WidgetProfileDao.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package de.psdev.devdrawer.database
22

33
import androidx.room.Dao
44
import androidx.room.Query
5+
import androidx.room.Transaction
56
import kotlinx.coroutines.flow.Flow
67

78
@Dao
8-
abstract class WidgetProfileDao: BaseDao<WidgetProfile>() {
9+
abstract class WidgetProfileDao : BaseDao<WidgetProfile>() {
910
@Query("SELECT * FROM widget_profiles")
1011
abstract suspend fun findAll(): List<WidgetProfile>
1112

@@ -15,4 +16,8 @@ abstract class WidgetProfileDao: BaseDao<WidgetProfile>() {
1516
@Query("SELECT * FROM widget_profiles WHERE id = :id")
1617
abstract suspend fun findById(id: String): WidgetProfile?
1718

19+
@Transaction
20+
@Query("SELECT * FROM widget_profiles WHERE id = :id")
21+
abstract fun widgetProfileWithIdObservable(id: String): Flow<WidgetProfile>
22+
1823
}

0 commit comments

Comments
 (0)