mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 01:39:22 +02:00
Compare commits
12 commits
e01cf09f51
...
2ed75c7f0f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2ed75c7f0f | ||
![]() |
13fe10b543 | ||
![]() |
bc0e83b916 | ||
![]() |
679276e3cf | ||
![]() |
3206d925e3 | ||
![]() |
b0e5a338e6 | ||
![]() |
376b7ca6de | ||
![]() |
1cdaed60e1 | ||
![]() |
d5bc1f9881 | ||
![]() |
6e9641638f | ||
![]() |
6a4b4505bb | ||
![]() |
6b09de4c59 |
20 changed files with 399 additions and 236 deletions
|
@ -25,13 +25,13 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace 'io.timelimit.android'
|
||||
compileSdkVersion 34
|
||||
compileSdk 35
|
||||
defaultConfig {
|
||||
applicationId "io.timelimit.android"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 34
|
||||
versionCode 218
|
||||
versionName "7.1.0"
|
||||
targetSdkVersion 35
|
||||
versionCode 220
|
||||
versionName "7.2.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
kapt {
|
||||
arguments {
|
||||
|
@ -146,8 +146,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
|
@ -167,22 +167,23 @@ wire {
|
|||
dependencies {
|
||||
def nav_version = "2.5.3"
|
||||
def room_version = "2.6.1"
|
||||
def work_version = '2.9.0'
|
||||
def paging_version = "3.3.1"
|
||||
def work_version = '2.9.1'
|
||||
def paging_version = "3.3.2"
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.21"
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.core:core:1.13.1'
|
||||
implementation 'androidx.core:core:1.15.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation "com.google.android.material:material:1.12.0"
|
||||
implementation 'androidx.compose.material:material:1.6.8'
|
||||
implementation 'androidx.activity:activity-compose:1.9.1'
|
||||
implementation 'androidx.compose.material:material:1.7.5'
|
||||
implementation 'androidx.activity:activity-compose:1.9.3'
|
||||
implementation "com.google.accompanist:accompanist-flowlayout:0.30.0"
|
||||
implementation 'androidx.compose.material:material-icons-extended:1.6.8'
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:1.6.8"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.8.2'
|
||||
implementation 'androidx.compose.material:material-icons-extended:1.7.5'
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:1.7.5"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.8.5'
|
||||
implementation 'androidx.fragment:fragment-compose:1.8.5'
|
||||
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui:$nav_version"
|
||||
|
@ -203,7 +204,7 @@ dependencies {
|
|||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.6.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
|
||||
implementation 'org.mindrot:jbcrypt:0.4'
|
||||
|
@ -216,7 +217,7 @@ dependencies {
|
|||
implementation 'com.squareup.okhttp3:okhttp-tls:4.9.3'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
|
||||
|
||||
googleApiImplementation "com.android.billingclient:billing-ktx:7.0.0"
|
||||
googleApiImplementation "com.android.billingclient:billing-ktx:7.1.1"
|
||||
|
||||
implementation('io.socket:socket.io-client:2.0.0') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
|
|
|
@ -55,7 +55,9 @@
|
|||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
tools:targetApi="tiramisu">
|
||||
|
||||
<!-- UI -->
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.RoomWarnings
|
||||
import io.timelimit.android.data.model.UsedTimeItem
|
||||
import io.timelimit.android.data.model.UsedTimeListItem
|
||||
import io.timelimit.android.livedata.ignoreUnchanged
|
||||
|
@ -68,11 +69,13 @@ abstract class UsedTimeDao {
|
|||
|
||||
// breaking it into multiple lines causes issues during compilation ...
|
||||
// this warns about an unused column, but this column is used in the ORDER BY
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
@Query("SELECT 2 AS type, start_time_of_day AS startMinuteOfDay, end_time_of_day AS endMinuteOfDay, used_time AS duration, day_of_epoch AS day, NULL AS lastUsage, NULL AS maxSessionDuration, NULL AS pauseDuration, category.id AS categoryId, category.title AS categoryTitle FROM used_time JOIN category ON (used_time.category_id = category.id) WHERE category.id = :categoryId UNION ALL SELECT 1 AS type, start_minute_of_day AS startMinuteOfDay, end_minute_of_day AS endMinuteOfDay, last_session_duration AS duration, NULL AS day, last_usage AS lastUsage, max_session_duration AS maxSessionDuration, session_pause_duration AS pauseDuration, category.id AS categoryId, category.title AS categoryTitle FROM session_duration JOIN category ON (session_duration.category_id = category.id) WHERE category.id = :categoryId ORDER BY type, day DESC, lastUsage DESC, startMinuteOfDay, endMinuteOfDay, categoryId")
|
||||
abstract fun getUsedTimeListItemsByCategoryId(categoryId: String): Flow<List<UsedTimeListItem>>
|
||||
|
||||
// breaking it into multiple lines causes issues during compilation ...
|
||||
// this warns about an unused column, but this column is used in the ORDER BY
|
||||
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||
@Query("SELECT 2 AS type, start_time_of_day AS startMinuteOfDay, end_time_of_day AS endMinuteOfDay, used_time AS duration, day_of_epoch AS day, NULL AS lastUsage, NULL AS maxSessionDuration, NULL AS pauseDuration, category.id AS categoryId, category.title AS categoryTitle FROM used_time JOIN category ON (used_time.category_id = category.id) WHERE category.child_id = :userId UNION ALL SELECT 1 AS type, start_minute_of_day AS startMinuteOfDay, end_minute_of_day AS endMinuteOfDay, last_session_duration AS duration, NULL AS day, last_usage AS lastUsage, max_session_duration AS maxSessionDuration, session_pause_duration AS pauseDuration, category.id AS categoryId, category.title AS categoryTitle FROM session_duration JOIN category ON (session_duration.category_id = category.id) WHERE category.child_id = :userId ORDER BY type, day DESC, lastUsage DESC, startMinuteOfDay, endMinuteOfDay, categoryId")
|
||||
abstract fun getUsedTimeListItemsByUserId(userId: String): Flow<List<UsedTimeListItem>>
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -184,7 +184,7 @@ class LollipopForegroundAppHelper(context: Context) : UsageStatsForegroundAppHel
|
|||
}
|
||||
|
||||
private fun doesActivityExistAsAlias(app: ForegroundApp) = try {
|
||||
packageManager.getPackageInfo(app.packageName, PackageManager.GET_ACTIVITIES).activities.find {
|
||||
packageManager.getPackageInfo(app.packageName, PackageManager.GET_ACTIVITIES).activities?.find {
|
||||
it.enabled && it.targetActivity == app.activityName
|
||||
} != null
|
||||
} catch (ex: PackageManager.NameNotFoundException) {
|
||||
|
|
|
@ -19,23 +19,24 @@ import android.Manifest
|
|||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -129,10 +130,20 @@ class MainActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
|||
if (granted) mainModel.reportPermissionsChanged()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val isNightMode =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.dark(
|
||||
if (isNightMode) android.graphics.Color.TRANSPARENT
|
||||
else resources.getColor(R.color.colorPrimaryDark)
|
||||
)
|
||||
)
|
||||
|
||||
supportActionBar!!.hide()
|
||||
|
||||
U2fManager.setupActivity(this)
|
||||
|
@ -314,9 +325,8 @@ class MainActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
|||
screen = screen,
|
||||
fragmentManager = supportFragmentManager,
|
||||
fragmentIds = mainModel.fragmentIds,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
paddingValues = paddingValues
|
||||
)
|
||||
},
|
||||
showAuthenticationDialog = showAuthenticationDialog,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package io.timelimit.android.ui
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
@ -42,27 +44,28 @@ fun ScreenMultiplexer(
|
|||
screen: Screen?,
|
||||
fragmentManager: FragmentManager,
|
||||
fragmentIds: MutableSet<Int>,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues
|
||||
) {
|
||||
when (screen) {
|
||||
null -> {/* nothing to do */ }
|
||||
is Screen.FragmentScreen -> FragmentScreen(screen, fragmentManager, fragmentIds, modifier = modifier)
|
||||
is Screen.OverviewScreen -> OverviewScreen(screen.content, modifier = modifier)
|
||||
is Screen.ManageDeviceUserScreen -> ManageDeviceUserScreen(screen.items, screen.actions, screen.overlay, modifier)
|
||||
is Screen.DeviceOwnerScreen -> DeviceOwnerScreen(screen.content, modifier = modifier)
|
||||
is Screen.SetupDevicePermissionsScreen -> SetupDevicePermissionsScreen(screen, modifier)
|
||||
is Screen.ManageDevicePermissions -> ManageDevicePermissionScreen(screen.content, modifier)
|
||||
is Screen.SetupConnectModePrivacyScreen -> SetupConnectedModePrivacyScreen(screen.customServerDomain, screen.accept, modifier)
|
||||
is Screen.SetupSelectConnectedModeScreen -> SelectConnectedModeScreen(mailLogin = screen.mailLogin, codeLogin = screen.codeLogin, modifier = modifier)
|
||||
is Screen.SetupSelectModeScreen -> SelectModeScreen(selectLocal = screen.selectLocal, selectConnected = screen.selectConnected, selectUninstall = screen.selectUninstall, modifier = modifier)
|
||||
is Screen.DeleteRegistration -> DeleteRegistrationScreen(screen.content, modifier)
|
||||
is Screen.ManageBlockedTimes -> BlockedTimesScreen(screen.content, screen.intro, modifier)
|
||||
is Screen.ChildUsageHistory -> UsageHistoryScreen(screen.content, modifier)
|
||||
is Screen.SetupParentMailAuthentication -> AuthenticateByMailScreen(screen.content, modifier)
|
||||
is Screen.SignupBlocked -> SignupBlockedScreen(modifier)
|
||||
is Screen.SignInWrongMailAddress -> SignInWrongMailAddress(modifier)
|
||||
is Screen.ConfirmNewParentAccount -> ConfirmNewParentAccount(confirm = screen.confirm, reject = screen.reject, modifier = modifier)
|
||||
is Screen.ParentBaseConfiguration -> ParentBaseConfiguration(content = screen.content, modifier = modifier)
|
||||
is Screen.ParentSetupConsent -> ParentSetupConsent(content = screen.content, errorDialog = screen.errorDialog, modifier = modifier)
|
||||
is Screen.FragmentScreen -> FragmentScreen(screen, fragmentManager, fragmentIds, modifier = modifier.padding(paddingValues))
|
||||
is Screen.OverviewScreen -> OverviewScreen(screen.content, modifier = modifier, paddingValues = paddingValues)
|
||||
is Screen.ManageDeviceUserScreen -> ManageDeviceUserScreen(screen.items, screen.actions, screen.overlay, modifier.padding(paddingValues))
|
||||
is Screen.DeviceOwnerScreen -> DeviceOwnerScreen(screen.content, modifier = modifier.padding(paddingValues))
|
||||
is Screen.SetupDevicePermissionsScreen -> SetupDevicePermissionsScreen(screen, modifier.padding(paddingValues))
|
||||
is Screen.ManageDevicePermissions -> ManageDevicePermissionScreen(screen.content, modifier.padding(paddingValues))
|
||||
is Screen.SetupConnectModePrivacyScreen -> SetupConnectedModePrivacyScreen(screen.customServerDomain, screen.accept, modifier.padding(paddingValues))
|
||||
is Screen.SetupSelectConnectedModeScreen -> SelectConnectedModeScreen(mailLogin = screen.mailLogin, codeLogin = screen.codeLogin, modifier = modifier.padding(paddingValues))
|
||||
is Screen.SetupSelectModeScreen -> SelectModeScreen(selectLocal = screen.selectLocal, selectConnected = screen.selectConnected, selectUninstall = screen.selectUninstall, modifier = modifier.padding(paddingValues))
|
||||
is Screen.DeleteRegistration -> DeleteRegistrationScreen(screen.content, modifier.padding(paddingValues))
|
||||
is Screen.ManageBlockedTimes -> BlockedTimesScreen(screen.content, screen.intro, modifier.padding(paddingValues))
|
||||
is Screen.ChildUsageHistory -> UsageHistoryScreen(screen.content, modifier.padding(paddingValues))
|
||||
is Screen.SetupParentMailAuthentication -> AuthenticateByMailScreen(screen.content, modifier.padding(paddingValues))
|
||||
is Screen.SignupBlocked -> SignupBlockedScreen(modifier.padding(paddingValues))
|
||||
is Screen.SignInWrongMailAddress -> SignInWrongMailAddress(modifier.padding(paddingValues))
|
||||
is Screen.ConfirmNewParentAccount -> ConfirmNewParentAccount(confirm = screen.confirm, reject = screen.reject, modifier = modifier.padding(paddingValues))
|
||||
is Screen.ParentBaseConfiguration -> ParentBaseConfiguration(content = screen.content, modifier = modifier.padding(paddingValues))
|
||||
is Screen.ParentSetupConsent -> ParentSetupConsent(content = screen.content, errorDialog = screen.errorDialog, modifier = modifier.padding(paddingValues))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,8 +17,13 @@ package io.timelimit.android.ui
|
|||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.navigationBarsIgnoringVisibility
|
||||
import androidx.compose.foundation.layout.statusBarsIgnoringVisibility
|
||||
import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -37,6 +42,7 @@ import io.timelimit.android.ui.model.Screen
|
|||
import io.timelimit.android.ui.model.Title
|
||||
import io.timelimit.android.ui.model.UpdateStateCommand
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun ScreenScaffold(
|
||||
screen: Screen?,
|
||||
|
@ -111,7 +117,9 @@ fun ScreenScaffold(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier,
|
||||
windowInsets = WindowInsets.statusBarsIgnoringVisibility
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
|
@ -159,7 +167,8 @@ fun ScreenScaffold(
|
|||
Text(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.navigationBarsIgnoringVisibility
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
|
@ -170,6 +179,7 @@ fun ScreenScaffold(
|
|||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState ?: it) },
|
||||
content = content
|
||||
content = content,
|
||||
contentWindowInsets = WindowInsets.systemBarsIgnoringVisibility
|
||||
)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -18,15 +18,35 @@ package io.timelimit.android.ui.lock
|
|||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.TabRowDefaults
|
||||
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.compose.AndroidFragment
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.map
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.databinding.LockActivityBinding
|
||||
import io.timelimit.android.data.model.UserType
|
||||
import io.timelimit.android.extensions.showSafe
|
||||
import io.timelimit.android.logic.BlockingReason
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
|
@ -34,11 +54,12 @@ import io.timelimit.android.sync.network.UpdatePrimaryDeviceRequestType
|
|||
import io.timelimit.android.u2f.U2fManager
|
||||
import io.timelimit.android.u2f.protocol.U2FDevice
|
||||
import io.timelimit.android.ui.IsAppInForeground
|
||||
import io.timelimit.android.ui.ScreenScaffold
|
||||
import io.timelimit.android.ui.Theme
|
||||
import io.timelimit.android.ui.login.AuthTokenLoginProcessor
|
||||
import io.timelimit.android.ui.login.NewLoginFragment
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
||||
import io.timelimit.android.ui.main.AuthenticationFab
|
||||
import io.timelimit.android.ui.manage.child.primarydevice.UpdatePrimaryDeviceDialogFragment
|
||||
import io.timelimit.android.ui.util.SyncStatusModel
|
||||
|
||||
|
@ -85,17 +106,113 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
|||
null
|
||||
}
|
||||
|
||||
private val showAuth = MutableLiveData<Boolean>().apply { value = false }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val isNightMode =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.dark(
|
||||
if (isNightMode) android.graphics.Color.TRANSPARENT
|
||||
else resources.getColor(R.color.colorPrimaryDark)
|
||||
)
|
||||
)
|
||||
|
||||
supportActionBar!!.hide()
|
||||
|
||||
U2fManager.setupActivity(this)
|
||||
|
||||
val adapter = LockActivityAdapter(supportFragmentManager, this)
|
||||
val subtitleLive = syncModel.statusText.asFlow()
|
||||
val showTasksLive = model.content.map {
|
||||
val isTimeOver = it is LockscreenContent.Blocked.BlockedCategory && it.blockingHandling.activityBlockingReason == BlockingReason.TimeOver
|
||||
|
||||
val binding = LockActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
isTimeOver
|
||||
}.asFlow()
|
||||
|
||||
setContent {
|
||||
val subtitle by subtitleLive.collectAsState(null)
|
||||
val showTasks by showTasksLive.collectAsState(false)
|
||||
val pager = rememberPagerState(initialPage = 0, pageCount = {
|
||||
if (showTasks) 3
|
||||
else 2
|
||||
})
|
||||
val isAuthenticated by getActivityViewModel().authenticatedUser
|
||||
.map { it?.second?.type == UserType.Parent }
|
||||
.asFlow().collectAsState(initial = false)
|
||||
|
||||
Theme {
|
||||
ScreenScaffold(
|
||||
screen = null,
|
||||
title = getString(R.string.app_name),
|
||||
subtitle = subtitle,
|
||||
backStack = emptyList(),
|
||||
snackbarHostState = null,
|
||||
content = { padding ->
|
||||
Column (Modifier.fillMaxSize().padding(padding)) {
|
||||
TabRow(
|
||||
pager.currentPage,
|
||||
indicator = { tabPositions ->
|
||||
// workaround for bug
|
||||
TabRowDefaults.Indicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[
|
||||
pager.currentPage.coerceAtMost(tabPositions.size - 1)
|
||||
])
|
||||
)
|
||||
}
|
||||
) {
|
||||
Tab(
|
||||
selected = pager.currentPage == 0,
|
||||
onClick = { pager.requestScrollToPage(0) }
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.lock_tab_reason),
|
||||
Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Tab(
|
||||
selected = pager.currentPage == 1,
|
||||
onClick = { pager.requestScrollToPage(1) }
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.lock_tab_action),
|
||||
Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (showTasks) Tab(
|
||||
selected = pager.currentPage == 2,
|
||||
onClick = { pager.requestScrollToPage(2) }
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.lock_tab_task),
|
||||
Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalPager(
|
||||
pager,
|
||||
Modifier.weight(1.0F, fill = true),
|
||||
pageContent = { index ->
|
||||
when (index) {
|
||||
0 -> AndroidFragment<LockReasonFragment>(Modifier.fillMaxSize())
|
||||
1 -> AndroidFragment<LockActionFragment>(Modifier.fillMaxSize())
|
||||
2 -> AndroidFragment<LockTaskFragment>(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
executeCommand = {},
|
||||
showAuthenticationDialog =
|
||||
if (pager.currentPage == 1 && !isAuthenticated) ({ showAuthenticationScreen() })
|
||||
else null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
syncModel.statusText.observe(this) { supportActionBar?.subtitle = it }
|
||||
|
||||
|
@ -103,8 +220,6 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
|||
|
||||
model.init(blockedPackageName, blockedActivityName)
|
||||
|
||||
binding.pager.adapter = adapter
|
||||
|
||||
model.content.observe(this) {
|
||||
if (isResumed && it is LockscreenContent.Blocked.BlockedCategory && it.reason == BlockingReason.RequiresCurrentDevice && !model.didOpenSetCurrentDeviceScreen) {
|
||||
model.didOpenSetCurrentDeviceScreen = true
|
||||
|
@ -115,30 +230,12 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
|||
}
|
||||
}
|
||||
|
||||
AuthenticationFab.manageAuthenticationFab(
|
||||
fab = binding.fab,
|
||||
shouldHighlight = activityModel.shouldHighlightAuthenticationButton,
|
||||
authenticatedUser = activityModel.authenticatedUser,
|
||||
activity = this,
|
||||
doesSupportAuth = showAuth
|
||||
)
|
||||
activityModel.shouldHighlightAuthenticationButton.observe(this) {
|
||||
if (it) {
|
||||
activityModel.shouldHighlightAuthenticationButton.postValue(false)
|
||||
|
||||
binding.fab.setOnClickListener { showAuthenticationScreen() }
|
||||
|
||||
binding.pager.addOnPageChangeListener(object: ViewPager.SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
|
||||
showAuth.value = position == 1
|
||||
showAuthenticationScreen()
|
||||
}
|
||||
})
|
||||
|
||||
binding.tabs.setupWithViewPager(binding.pager)
|
||||
|
||||
model.content.observe(this) {
|
||||
val isTimeOver = it is LockscreenContent.Blocked.BlockedCategory && it.blockingHandling.activityBlockingReason == BlockingReason.TimeOver
|
||||
|
||||
adapter.showTasksFragment = isTimeOver
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(object: OnBackPressedCallback(true) {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.timelimit.android.ui.lock
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import io.timelimit.android.R
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class LockActivityAdapter(fragmentManager: FragmentManager, private val context: Context): FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
var showTasksFragment: Boolean by Delegates.observable(false) { _, _, _ -> notifyDataSetChanged() }
|
||||
|
||||
override fun getCount(): Int = if (showTasksFragment) 3 else 2
|
||||
|
||||
override fun getItem(position: Int): Fragment = when (position) {
|
||||
0 -> LockReasonFragment()
|
||||
1 -> LockActionFragment()
|
||||
2 -> LockTaskFragment()
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? = context.getString(when (position) {
|
||||
0 -> R.string.lock_tab_reason
|
||||
1 -> R.string.lock_tab_action
|
||||
2 -> R.string.lock_tab_task
|
||||
else -> throw IllegalArgumentException()
|
||||
})
|
||||
}
|
|
@ -31,6 +31,7 @@ import io.timelimit.android.logic.RemainingTime
|
|||
import io.timelimit.android.ui.manage.category.timelimit_rules.TimeLimitRulesHandlers
|
||||
import io.timelimit.android.ui.util.DateUtil
|
||||
import io.timelimit.android.util.DayNameUtil
|
||||
import io.timelimit.android.util.Option
|
||||
import io.timelimit.android.util.TimeTextUtil
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
@ -159,12 +160,24 @@ class AppAndRuleAdapter: RecyclerView.Adapter<AppAndRuleAdapter.Holder>() {
|
|||
val binding = holder.itemView.tag as FragmentCategoryTimeLimitRuleItemBinding
|
||||
val context = binding.root.context
|
||||
val usedTime = date?.let { date ->
|
||||
RemainingTime.getUsedTime(
|
||||
usedTimes = usedTimes,
|
||||
rule = rule,
|
||||
firstDayOfWeekAsEpochDay = date.firstDayOfWeekAsEpochDay,
|
||||
dayOfWeekForDailyRule = if (rule.perDay) date.dayOfWeek else null
|
||||
).toInt()
|
||||
val dayOfWeekForDailyRule: Option<Int?>? =
|
||||
if (rule.perDay) {
|
||||
(0 until 7)
|
||||
.map { (7 + date.dayOfWeek - it) % 7 } // make the current day the last one
|
||||
.firstOrNull { rule.dayMask.toInt() and (1 shl it) != 0 }
|
||||
?.let { Option.Some(it) } // skip calculation if no day matches
|
||||
} else Option.Some(null) // use the value null
|
||||
|
||||
dayOfWeekForDailyRule?.let {
|
||||
RemainingTime.getUsedTime(
|
||||
usedTimes = usedTimes,
|
||||
rule = rule,
|
||||
firstDayOfWeekAsEpochDay = date.firstDayOfWeekAsEpochDay,
|
||||
dayOfWeekForDailyRule =
|
||||
if (it is Option.Some) it.value
|
||||
else null
|
||||
).toInt()
|
||||
}
|
||||
} ?: 0
|
||||
|
||||
binding.maxTimeString = rule.maximumTimeInMillis.let { time ->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -18,12 +18,21 @@ package io.timelimit.android.ui.manipulation
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.map
|
||||
import io.timelimit.android.BuildConfig
|
||||
import io.timelimit.android.R
|
||||
|
@ -34,6 +43,8 @@ import io.timelimit.android.integration.platform.android.AndroidIntegrationApps
|
|||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.u2f.U2fManager
|
||||
import io.timelimit.android.u2f.protocol.U2FDevice
|
||||
import io.timelimit.android.ui.ScreenScaffold
|
||||
import io.timelimit.android.ui.Theme
|
||||
import io.timelimit.android.ui.backdoor.BackdoorDialogFragment
|
||||
import io.timelimit.android.ui.login.AuthTokenLoginProcessor
|
||||
import io.timelimit.android.ui.login.NewLoginFragment
|
||||
|
@ -65,12 +76,75 @@ class AnnoyActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.D
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
U2fManager.setupActivity(this)
|
||||
|
||||
val logic = DefaultAppLogic.with(this)
|
||||
|
||||
val binding = AnnoyActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
val isNightMode =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.dark(
|
||||
if (isNightMode) android.graphics.Color.TRANSPARENT
|
||||
else resources.getColor(R.color.colorPrimaryDark)
|
||||
)
|
||||
)
|
||||
|
||||
supportActionBar!!.hide()
|
||||
|
||||
setContent {
|
||||
Theme {
|
||||
ScreenScaffold(
|
||||
screen = null,
|
||||
title = getString(R.string.app_name),
|
||||
subtitle = null,
|
||||
backStack = emptyList(),
|
||||
snackbarHostState = null,
|
||||
content = { padding ->
|
||||
AndroidView(
|
||||
factory = {
|
||||
val binding = AnnoyActivityBinding.inflate(LayoutInflater.from(it))
|
||||
|
||||
logic.annoyLogic.nextManualUnblockCountdown.observe(this) { countdown ->
|
||||
binding.canRequestUnlock = countdown == 0L
|
||||
binding.countdownText = getString(R.string.annoy_timer, TimeTextUtil.seconds((countdown / 1000).toInt(), this@AnnoyActivity))
|
||||
}
|
||||
|
||||
logic.deviceEntry.map {
|
||||
val reasonItems = (it?.let { ManipulationWarnings.getFromDevice(it) } ?: ManipulationWarnings.empty)
|
||||
.current
|
||||
.map { getString(it.labelResourceId) }
|
||||
|
||||
if (reasonItems.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
getString(R.string.annoy_reason, reasonItems.joinToString(separator = ", "))
|
||||
}
|
||||
}.observe(this) { binding.reasonText = it }
|
||||
|
||||
binding.unlockTemporarilyButton.setOnClickListener {
|
||||
AnnoyUnlockDialogFragment.newInstance(AnnoyUnlockDialogFragment.UnlockDuration.Short)
|
||||
.show(supportFragmentManager)
|
||||
}
|
||||
|
||||
binding.parentUnlockButton.setOnClickListener {
|
||||
AnnoyUnlockDialogFragment.newInstance(AnnoyUnlockDialogFragment.UnlockDuration.Long)
|
||||
.show(supportFragmentManager)
|
||||
}
|
||||
|
||||
binding.useBackdoorButton.setOnClickListener { BackdoorDialogFragment().show(supportFragmentManager) }
|
||||
|
||||
binding.root
|
||||
},
|
||||
modifier = Modifier.fillMaxSize().padding(padding)
|
||||
)
|
||||
},
|
||||
executeCommand = {},
|
||||
showAuthenticationDialog = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
U2fManager.setupActivity(this)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val systemImageApps = packageManager.getInstalledApplications(0)
|
||||
|
@ -92,35 +166,6 @@ class AnnoyActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.D
|
|||
if (!shouldRun) shutdown()
|
||||
}
|
||||
|
||||
logic.annoyLogic.nextManualUnblockCountdown.observe(this) { countdown ->
|
||||
binding.canRequestUnlock = countdown == 0L
|
||||
binding.countdownText = getString(R.string.annoy_timer, TimeTextUtil.seconds((countdown / 1000).toInt(), this@AnnoyActivity))
|
||||
}
|
||||
|
||||
logic.deviceEntry.map {
|
||||
val reasonItems = (it?.let { ManipulationWarnings.getFromDevice(it) } ?: ManipulationWarnings.empty)
|
||||
.current
|
||||
.map { getString(it.labelResourceId) }
|
||||
|
||||
if (reasonItems.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
getString(R.string.annoy_reason, reasonItems.joinToString(separator = ", "))
|
||||
}
|
||||
}.observe(this) { binding.reasonText = it }
|
||||
|
||||
binding.unlockTemporarilyButton.setOnClickListener {
|
||||
AnnoyUnlockDialogFragment.newInstance(AnnoyUnlockDialogFragment.UnlockDuration.Short)
|
||||
.show(supportFragmentManager)
|
||||
}
|
||||
|
||||
binding.parentUnlockButton.setOnClickListener {
|
||||
AnnoyUnlockDialogFragment.newInstance(AnnoyUnlockDialogFragment.UnlockDuration.Long)
|
||||
.show(supportFragmentManager)
|
||||
}
|
||||
|
||||
binding.useBackdoorButton.setOnClickListener { BackdoorDialogFragment().show(supportFragmentManager) }
|
||||
|
||||
model.authenticatedUser.observe(this) { user ->
|
||||
if (user?.second?.type == UserType.Parent) {
|
||||
logic.annoyLogic.doParentTempUnlock()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,16 +19,24 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.timelimit.android.ui.model.main.OverviewHandling
|
||||
|
||||
@Composable
|
||||
fun OverviewScreen(
|
||||
screen: OverviewHandling.OverviewScreen,
|
||||
paddingValues: PaddingValues,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
LazyColumn (
|
||||
contentPadding = PaddingValues(0.dp, 8.dp),
|
||||
contentPadding = object: PaddingValues {
|
||||
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = paddingValues.calculateLeftPadding(layoutDirection)
|
||||
override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = paddingValues.calculateRightPadding(layoutDirection)
|
||||
override fun calculateTopPadding(): Dp = paddingValues.calculateTopPadding() + 8.dp
|
||||
override fun calculateBottomPadding(): Dp = paddingValues.calculateBottomPadding() + 8.dp
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = modifier
|
||||
) {
|
||||
|
|
|
@ -1,23 +1,83 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.update
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.databinding.UpdateActivityBinding
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.ui.ScreenScaffold
|
||||
import io.timelimit.android.ui.Theme
|
||||
|
||||
class UpdateActivity: AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val binding = DataBindingUtil.setContentView<UpdateActivityBinding>(this, R.layout.update_activity)
|
||||
val isNightMode =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
UpdateView.bind(
|
||||
view = binding.update,
|
||||
lifecycleOwner = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
appLogic = DefaultAppLogic.with(this)
|
||||
enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.dark(
|
||||
if (isNightMode) android.graphics.Color.TRANSPARENT
|
||||
else resources.getColor(R.color.colorPrimaryDark)
|
||||
)
|
||||
)
|
||||
|
||||
supportActionBar!!.hide()
|
||||
|
||||
setContent {
|
||||
Theme {
|
||||
ScreenScaffold(
|
||||
screen = null,
|
||||
title = getString(R.string.app_name),
|
||||
subtitle = null,
|
||||
backStack = emptyList(),
|
||||
snackbarHostState = null,
|
||||
content = { padding ->
|
||||
AndroidView(
|
||||
factory = {
|
||||
val binding = UpdateActivityBinding.inflate(LayoutInflater.from(it))
|
||||
|
||||
UpdateView.bind(
|
||||
view = binding.update,
|
||||
lifecycleOwner = this,
|
||||
fragmentManager = supportFragmentManager,
|
||||
appLogic = DefaultAppLogic.with(this)
|
||||
)
|
||||
|
||||
binding.root
|
||||
},
|
||||
modifier = Modifier.fillMaxSize().padding(padding)
|
||||
)
|
||||
},
|
||||
executeCommand = {},
|
||||
showAuthenticationDialog = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,6 +19,7 @@ import android.appwidget.AppWidgetManager
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import io.timelimit.android.R
|
||||
|
@ -30,6 +31,8 @@ class WidgetConfigActivity: FragmentActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
if (model.state.value == WidgetConfigModel.State.WaitingForInit) {
|
||||
model.init(
|
||||
intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -70,12 +70,12 @@ object UpdateIntegration {
|
|||
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// new signature
|
||||
|
||||
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo.apkContentsSigners
|
||||
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo!!.apkContentsSigners
|
||||
} else {
|
||||
// old signature
|
||||
// this is "unsafe", but it is not used for security features
|
||||
|
||||
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures
|
||||
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures!!
|
||||
}
|
||||
|
||||
return signatures.map { HexString.toHex(MessageDigest.getInstance("SHA-256").digest(it.toByteArray())) }
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
- verbesserter Umgang mit Fehlern im Hintergrund
|
||||
- Anpassungen für Android 15
|
||||
- falsch angezeigte Nutzungsdauer bei Regeln die je Tag gelten an Tagen, an denen diese nicht gelten, behoben
|
||||
- enthaltene Komponenten aktualisiert
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
- improved handling of errors in the background
|
||||
- adjustments for Android 15
|
||||
- fix incorrect used time at rules that apply per day at days where they do not apply
|
||||
- update contained software
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation version 3 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.lock.LockActivity" >
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/tabs"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/pager" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
app:fabSize="normal"
|
||||
android:src="@drawable/ic_lock_open_white_24dp"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</LinearLayout>
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version '8.5.1' apply false
|
||||
id 'com.android.library' version '8.5.1' apply false
|
||||
id 'com.android.application' version '8.7.2' apply false
|
||||
id 'com.android.library' version '8.7.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version "1.9.21" apply false
|
||||
id 'com.google.devtools.ksp' version '1.9.21-1.0.16' apply false
|
||||
id 'androidx.navigation.safeargs' version '2.6.0' apply false
|
||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
distributionSha256Sum=194717442575a6f96e1c1befa2c30e9a4fc90f701d7aee33eb879b79e7ff05c0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
|
||||
distributionSha256Sum=258e722ec21e955201e31447b0aed14201765a3bfbae296a46cf60b70e66db70
|
Loading…
Add table
Add a link
Reference in a new issue