Feat: Added work profile support with icon.
Signed-off-by: HeCodes2Much <wayne6324@gmail.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.AlarmClock
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.Settings
|
||||
@@ -297,6 +298,16 @@ fun Context.searchCustomSearchEngine(searchQuery: String? = null): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun Context.isWorkProfileEnabled(): Boolean {
|
||||
val userManager = getSystemService(Context.USER_SERVICE) as? UserManager
|
||||
return if (userManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val profiles = userManager.userProfiles
|
||||
profiles.size > 1
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.backupSharedPreferences(backupFileName: String) {
|
||||
val sharedPreferences: SharedPreferences =
|
||||
this.getSharedPreferences(Constants.PREFS_FILENAME, 0)
|
||||
|
||||
@@ -17,7 +17,7 @@ interface AppInfoDAO {
|
||||
suspend fun update(app: AppInfo)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(app: AppInfo)
|
||||
fun delete(app: AppInfo)
|
||||
|
||||
@Query("SELECT * FROM app ORDER BY app_name COLLATE NOCASE ASC")
|
||||
fun getAllApps(): List<AppInfo>
|
||||
@@ -73,9 +73,12 @@ interface AppInfoDAO {
|
||||
@Query("SELECT * FROM app WHERE is_favorite = 1")
|
||||
fun getFavoriteAppInfo(): List<AppInfo>
|
||||
|
||||
@Query("SELECT * FROM app WHERE package_name = :packageName")
|
||||
@Query("SELECT * FROM app WHERE package_name = :packageName AND is_work = 0")
|
||||
suspend fun getAppByPackageName(packageName: String): AppInfo?
|
||||
|
||||
@Query("SELECT * FROM app WHERE package_name = :packageName AND is_work = 1")
|
||||
suspend fun getAppByPackageNameWork(packageName: String): AppInfo?
|
||||
|
||||
private fun logUpdate(message: String, appInfo: AppInfo) {
|
||||
// You can replace this with a logging library like Timber for more advanced logging capabilities.
|
||||
Log.d(
|
||||
|
||||
@@ -26,8 +26,11 @@ data class AppInfo(
|
||||
@ColumnInfo(name = "is_lock")
|
||||
var lock: Boolean,
|
||||
|
||||
@ColumnInfo(name = "is_work")
|
||||
var work: Boolean,
|
||||
|
||||
@ColumnInfo(name = "create_time")
|
||||
var createTime: String = "",
|
||||
var createTime: String,
|
||||
|
||||
@ColumnInfo(name = "app_order")
|
||||
var appOrder: Int = -1
|
||||
|
||||
@@ -85,8 +85,8 @@ internal open class OnSwipeTouchListener(c: Context?) : OnTouchListener {
|
||||
if (diffY < 0) onSwipeUp() else onSwipeDown()
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
exception.printStackTrace()
|
||||
} catch (exception: NullPointerException) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.github.droidworksstudio.launcher.repository
|
||||
import android.content.Context
|
||||
import android.content.pm.LauncherApps
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.github.droidworksstudio.launcher.Constants
|
||||
import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
@@ -12,11 +15,13 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.reflect.Method
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class AppInfoRepository @Inject constructor(
|
||||
private val appDao: AppInfoDAO
|
||||
private val appDao: AppInfoDAO,
|
||||
) {
|
||||
|
||||
@Inject
|
||||
@@ -98,7 +103,9 @@ class AppInfoRepository @Inject constructor(
|
||||
packages
|
||||
}
|
||||
|
||||
suspend fun initInstalledAppInfo(context: Context): List<AppInfo> = withContext(Dispatchers.IO) {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
suspend fun initInstalledAppInfo(context: Context): List<AppInfo> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val appList: MutableList<AppInfo> = mutableListOf()
|
||||
|
||||
val allApps = appDao.getAllAppsFlow().firstOrNull()
|
||||
@@ -109,29 +116,69 @@ class AppInfoRepository @Inject constructor(
|
||||
val launcherApps =
|
||||
context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
|
||||
val excludedPackageNames = mutableListOf(Constants.PACKAGE_NAME,Constants.PACKAGE_NAME_DEBUG)
|
||||
val excludedPackageNames =
|
||||
mutableListOf(Constants.PACKAGE_NAME, Constants.PACKAGE_NAME_DEBUG)
|
||||
|
||||
val getIdentifierMethod: Method =
|
||||
UserHandle::class.java.getDeclaredMethod("getIdentifier")
|
||||
|
||||
val newAppList: List<AppInfo> = userManager.userProfiles
|
||||
.flatMap { profile ->
|
||||
launcherApps.getActivityList(null, profile)
|
||||
.mapNotNull { app ->
|
||||
val packageName = app.applicationInfo.packageName
|
||||
if (packageName !in existingPackageNames && packageName !in excludedPackageNames) {
|
||||
AppInfo(
|
||||
appName = app.label.toString(),
|
||||
packageName = packageName,
|
||||
favorite = false,
|
||||
hidden = false,
|
||||
lock = false
|
||||
)
|
||||
} else {
|
||||
val existingApp = getAppByPackageName(packageName)
|
||||
existingApp?.let { appList.add(it) }
|
||||
existingApp
|
||||
}
|
||||
// Invoke the getIdentifier method on the UserHandle instance
|
||||
val userId = getIdentifierMethod.invoke(profile) as Int
|
||||
|
||||
when (userId) {
|
||||
0 -> {
|
||||
// Handle the case when profile is UserHandle{0}
|
||||
launcherApps.getActivityList(null, profile)
|
||||
.mapNotNull { app ->
|
||||
val packageName = app.applicationInfo.packageName
|
||||
val currentDateTime = LocalDateTime.now()
|
||||
if (packageName !in existingPackageNames && packageName !in excludedPackageNames) {
|
||||
AppInfo(
|
||||
appName = app.label.toString(),
|
||||
packageName = packageName,
|
||||
favorite = false,
|
||||
hidden = false,
|
||||
lock = false,
|
||||
createTime = currentDateTime.toString(),
|
||||
work = false,
|
||||
)
|
||||
} else {
|
||||
val existingApp = getAppByPackageName(packageName)
|
||||
existingApp?.let { appList.add(it) }
|
||||
existingApp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Handle other profiles
|
||||
launcherApps.getActivityList(null, profile)
|
||||
.mapNotNull { app ->
|
||||
val packageName = app.applicationInfo.packageName
|
||||
val currentDateTime = LocalDateTime.now()
|
||||
if (packageName !in existingPackageNames && packageName !in excludedPackageNames) {
|
||||
AppInfo(
|
||||
appName = app.label.toString(),
|
||||
packageName = packageName,
|
||||
favorite = false,
|
||||
hidden = false,
|
||||
lock = false,
|
||||
createTime = currentDateTime.toString(),
|
||||
work = true,
|
||||
)
|
||||
} else {
|
||||
val existingApp = getAppByPackageNameWork(packageName)
|
||||
existingApp?.let { appList.add(it) }
|
||||
existingApp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
appDao.insertAll(newAppList.sortedBy { it.appName })
|
||||
Log.d("Tag", "State: ${newAppList.sortedBy { it.appName }}")
|
||||
|
||||
@@ -142,6 +189,7 @@ class AppInfoRepository @Inject constructor(
|
||||
appList
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
suspend fun compareInstalledApp(): List<AppInfo> = withContext(Dispatchers.IO) {
|
||||
val installedPackages = getInstalledPackages()
|
||||
val uninstalledApps = mutableListOf<AppInfo>()
|
||||
@@ -176,4 +224,8 @@ class AppInfoRepository @Inject constructor(
|
||||
private suspend fun getAppByPackageName(packageName: String): AppInfo? {
|
||||
return appDao.getAppByPackageName(packageName)
|
||||
}
|
||||
|
||||
private suspend fun getAppByPackageNameWork(packageName: String): AppInfo? {
|
||||
return appDao.getAppByPackageNameWork(packageName)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.github.droidworksstudio.launcher.ui.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
@@ -19,7 +17,6 @@ import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import com.github.droidworksstudio.ktx.isTablet
|
||||
import com.github.droidworksstudio.launcher.Constants
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.databinding.ActivityMainBinding
|
||||
import com.github.droidworksstudio.launcher.helper.AppHelper
|
||||
@@ -68,6 +65,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
private fun setupDataBase() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.initializeInstalledAppInfo(this@MainActivity)
|
||||
@@ -118,6 +116,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|| super.onSupportNavigateUp()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
backToHomeScreen()
|
||||
|
||||
@@ -37,7 +37,6 @@ class DrawAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
|
||||
val appInfo = getItem(position)
|
||||
(holder as DrawViewHolder).bind(appInfo)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.droidworksstudio.launcher.ui.drawer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
@@ -10,6 +11,7 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -64,7 +66,13 @@ class DrawFragment : Fragment(),
|
||||
|
||||
private val viewModel: AppViewModel by viewModels()
|
||||
|
||||
private val drawAdapter: DrawAdapter by lazy { DrawAdapter(this, this, preferenceHelper) }
|
||||
private val drawAdapter: DrawAdapter by lazy {
|
||||
DrawAdapter(
|
||||
this,
|
||||
this,
|
||||
preferenceHelper
|
||||
)
|
||||
}
|
||||
|
||||
private lateinit var context: Context
|
||||
override fun onCreateView(
|
||||
@@ -98,6 +106,7 @@ class DrawFragment : Fragment(),
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
private fun observeDrawerApps() {
|
||||
viewModel.compareInstalledAppInfo()
|
||||
|
||||
@@ -242,12 +251,14 @@ class DrawFragment : Fragment(),
|
||||
binding.searchViewText.hideKeyboard()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
observeDrawerApps()
|
||||
if (preferenceHelper.automaticKeyboard) binding.searchViewText.showKeyboard()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
observeDrawerApps()
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package com.github.droidworksstudio.launcher.ui.drawer
|
||||
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.widget.LinearLayoutCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.droidworksstudio.ktx.dpToPx
|
||||
import com.github.droidworksstudio.ktx.isWorkProfileEnabled
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
import com.github.droidworksstudio.launcher.databinding.ItemDrawBinding
|
||||
import com.github.droidworksstudio.launcher.helper.PreferenceHelper
|
||||
@@ -14,7 +20,9 @@ class DrawViewHolder(
|
||||
private val onAppLongClickedListener: OnItemClickedListener.OnAppLongClickedListener,
|
||||
private val preferenceHelper: PreferenceHelper
|
||||
) :
|
||||
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(appInfo: AppInfo) {
|
||||
binding.apply {
|
||||
val layoutParams = LinearLayoutCompat.LayoutParams(
|
||||
@@ -30,7 +38,28 @@ class DrawViewHolder(
|
||||
appDrawName.text = appInfo.appName
|
||||
appDrawName.setTextColor(preferenceHelper.appColor)
|
||||
appDrawName.textSize = preferenceHelper.appTextSize
|
||||
Log.d("Tag", "Draw Adapter: ${appInfo.appName + appInfo.id}")
|
||||
Log.d("Tag", "Draw Adapter: ${appInfo.appName + appInfo.id} | ${appInfo.work}")
|
||||
val icon = AppCompatResources.getDrawable(appDrawName.context, R.drawable.work_profile)
|
||||
val px = preferenceHelper.appTextSize.toInt().dpToPx()
|
||||
icon?.setBounds(0, 0, px, px)
|
||||
if (appInfo.work) {
|
||||
val appLabelGravity = preferenceHelper.homeAppAlignment
|
||||
|
||||
if (appLabelGravity == Gravity.START) {
|
||||
appDrawName.setCompoundDrawables(icon, null, null, null)
|
||||
} else {
|
||||
appDrawName.setCompoundDrawables(null, null, icon, null)
|
||||
}
|
||||
appDrawName.compoundDrawablePadding = 20
|
||||
if (!appDrawName.context.isWorkProfileEnabled()) {
|
||||
appDrawName.visibility = View.GONE
|
||||
}
|
||||
} else {
|
||||
|
||||
// If appInfo.work is false, remove the drawable
|
||||
appDrawName.setCompoundDrawables(null, null, null, null)
|
||||
appDrawName.compoundDrawablePadding = 0
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.github.droidworksstudio.launcher.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
@@ -22,47 +24,58 @@ class AppViewModel @Inject constructor(
|
||||
val drawApps: Flow<List<AppInfo>> = appInfoRepository.getDrawApps().conflate()
|
||||
val favoriteApps: Flow<List<AppInfo>> = appInfoRepository.getFavoriteApps().conflate()
|
||||
val hiddenApps: Flow<List<AppInfo>> = appInfoRepository.getHiddenApps().conflate()
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun initializeInstalledAppInfo(context: Context) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.initInstalledAppInfo(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppInfoFavorite(appInfo: AppInfo) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.updateFavoriteAppInfo(appInfo)
|
||||
Log.d("Tag", "ViewModel Home Order : ${appInfo.appOrder}")
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppInfoAppName(appInfo: AppInfo, newAppName: String) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.updateAppName(appInfo, newAppName)
|
||||
Log.d("Tag", "ViewModel Home Name : ${appInfo.appName}")
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppHidden(appInfo: AppInfo, appHidden: Boolean) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.updateAppHidden(appInfo, appHidden)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppLock(appInfo: AppInfo, appLock: Boolean) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.updateAppLock(appInfo, appLock)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun compareInstalledAppInfo() {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.compareInstalledApp()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateAppOrder(appInfoList: List<AppInfo>) {
|
||||
withContext(Dispatchers.IO) {
|
||||
appInfoRepository.updateAppOrder(appInfoList)
|
||||
}
|
||||
}
|
||||
|
||||
fun update(appInfo: AppInfo) {
|
||||
viewModelScope.launch {
|
||||
appInfoRepository.updateInfo(appInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fun searchAppInfo(query: String?) = appInfoRepository.searchNote(query)
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/work_profile.xml
Normal file
9
app/src/main/res/drawable/work_profile.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40sp"
|
||||
android:height="40sp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6.542,36.292Q4.708,36.292 3.438,35.021Q2.167,33.75 2.167,31.917V13.292Q2.167,11.458 3.438,10.188Q4.708,8.917 6.542,8.917H12.25V5.583Q12.25,3.75 13.521,2.479Q14.792,1.208 16.625,1.208H23.375Q25.208,1.208 26.479,2.479Q27.75,3.75 27.75,5.583V8.917H33.458Q35.292,8.917 36.583,10.188Q37.875,11.458 37.875,13.292V31.917Q37.875,33.75 36.583,35.021Q35.292,36.292 33.458,36.292ZM6.542,31.917H33.458Q33.458,31.917 33.458,31.917Q33.458,31.917 33.458,31.917V13.292Q33.458,13.292 33.458,13.292Q33.458,13.292 33.458,13.292H6.542Q6.542,13.292 6.542,13.292Q6.542,13.292 6.542,13.292V31.917Q6.542,31.917 6.542,31.917Q6.542,31.917 6.542,31.917ZM16.625,8.917H23.375V5.583Q23.375,5.583 23.375,5.583Q23.375,5.583 23.375,5.583H16.625Q16.625,5.583 16.625,5.583Q16.625,5.583 16.625,5.583ZM6.542,31.917Q6.542,31.917 6.542,31.917Q6.542,31.917 6.542,31.917V13.292Q6.542,13.292 6.542,13.292Q6.542,13.292 6.542,13.292Q6.542,13.292 6.542,13.292Q6.542,13.292 6.542,13.292V31.917Q6.542,31.917 6.542,31.917Q6.542,31.917 6.542,31.917Z" />
|
||||
</vector>
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/drawAdapter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/search_view_text" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user