-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat : initial implementation of LawnDeck (#5255)
Introducing the home screen layout feature! Now, users have the ability to personalize their experience by disabling the app drawer and arranging apps directly on their home screen. - closes : #4323
- Loading branch information
Showing
13 changed files
with
498 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package app.lawnchair.deck | ||
|
||
import android.content.Context | ||
import android.util.Log | ||
import app.lawnchair.LawnchairLauncher | ||
import app.lawnchair.launcher | ||
import app.lawnchair.launcherNullable | ||
import app.lawnchair.util.restartLauncher | ||
import com.android.launcher3.InvariantDeviceProfile | ||
import com.android.launcher3.model.ItemInstallQueue | ||
import com.android.launcher3.model.ModelDbController | ||
import com.android.launcher3.provider.RestoreDbTask | ||
import java.io.File | ||
import java.util.Locale | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
|
||
class LawndeckManager(private val context: Context) { | ||
|
||
// TODO | ||
|
||
private val launcher = context.launcherNullable ?: LawnchairLauncher.instance?.launcher | ||
|
||
suspend fun enableLawndeck() = withContext(Dispatchers.IO) { | ||
if (!backupExists("bk")) createBackup("bk") | ||
if (backupExists("lawndeck")) { | ||
restoreBackup("lawndeck") | ||
} else { | ||
addAllAppsToWorkspace() | ||
} | ||
} | ||
|
||
suspend fun disableLawndeck() = withContext(Dispatchers.IO) { | ||
if (backupExists("bk")) { | ||
createBackup("lawndeck") | ||
restoreBackup("bk") | ||
} | ||
} | ||
|
||
private fun createBackup(suffix: String) = runCatching { | ||
getDatabaseFiles(suffix).apply { | ||
db.copyTo(backupDb, overwrite = true) | ||
if (journal.exists()) journal.copyTo(backupJournal, overwrite = true) | ||
} | ||
}.onFailure { Log.e("LawndeckManager", "Failed to create backup: $suffix", it) } | ||
|
||
private fun restoreBackup(suffix: String) = runCatching { | ||
getDatabaseFiles(suffix).apply { | ||
backupDb.copyTo(db, overwrite = true) | ||
if (backupJournal.exists()) backupJournal.copyTo(journal, overwrite = true) | ||
} | ||
postRestoreActions() | ||
}.onFailure { Log.e("LawndeckManager", "Failed to restore backup: $suffix", it) } | ||
|
||
private fun getDatabaseFiles(suffix: String): DatabaseFiles { | ||
val idp = InvariantDeviceProfile.INSTANCE.get(context) | ||
val dbFile = context.getDatabasePath(idp.dbFile) | ||
return DatabaseFiles( | ||
db = dbFile, | ||
backupDb = File(dbFile.parent, "${suffix}_${idp.dbFile}"), | ||
journal = File(dbFile.parent, "${idp.dbFile}-journal"), | ||
backupJournal = File(dbFile.parent, "${suffix}_${idp.dbFile}-journal"), | ||
) | ||
} | ||
|
||
private fun backupExists(suffix: String): Boolean = getDatabaseFiles(suffix).backupDb.exists() | ||
|
||
private fun postRestoreActions() { | ||
ModelDbController(context).let { RestoreDbTask.performRestore(context, it) } | ||
restartLauncher(context) | ||
} | ||
|
||
private fun addAllAppsToWorkspace() { | ||
launcher?.mAppsView?.appsStore?.apps | ||
?.sortedBy { it.title?.toString()?.lowercase(Locale.getDefault()) } | ||
?.forEach { app -> | ||
ItemInstallQueue.INSTANCE.get(context).queueItem(app.targetPackage, app.user) | ||
} | ||
} | ||
|
||
private data class DatabaseFiles( | ||
val db: File, | ||
val backupDb: File, | ||
val journal: File, | ||
val backupJournal: File, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
lawnchair/src/app/lawnchair/ui/preferences/components/layout/ButtonSection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package app.lawnchair.ui.preferences.components.layout | ||
|
||
import androidx.compose.animation.animateColorAsState | ||
import androidx.compose.animation.core.tween | ||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.unit.dp | ||
import androidx.compose.ui.unit.sp | ||
|
||
@Composable | ||
fun ButtonSection( | ||
label: String, | ||
isSelected: Boolean, | ||
onClick: () -> Unit, | ||
modifier: Modifier = Modifier, | ||
gridLayout: @Composable () -> Unit, | ||
) { | ||
val backgroundColor by animateColorAsState( | ||
targetValue = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceContainer, | ||
animationSpec = tween(durationMillis = 300), | ||
) | ||
|
||
val textColor by animateColorAsState( | ||
targetValue = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary, | ||
animationSpec = tween(durationMillis = 300), | ||
) | ||
Column( | ||
modifier = modifier, | ||
horizontalAlignment = Alignment.CenterHorizontally, | ||
) { | ||
Box( | ||
modifier = Modifier | ||
.size(160.dp, 120.dp) | ||
.clip(MaterialTheme.shapes.large) | ||
.background(backgroundColor) | ||
.clickable { onClick() } | ||
.padding(12.dp), | ||
) { | ||
Column( | ||
modifier = Modifier.fillMaxSize(), | ||
verticalArrangement = Arrangement.SpaceEvenly, | ||
horizontalAlignment = Alignment.CenterHorizontally, | ||
) { | ||
gridLayout() | ||
} | ||
} | ||
|
||
Spacer(modifier = Modifier.height(8.dp)) | ||
Text( | ||
text = label, | ||
color = textColor, | ||
fontSize = 14.sp, | ||
) | ||
} | ||
} |
Oops, something went wrong.