SYSTEM_ALERT_WINDOW: Allows the app to display content on top of other apps (like chat heads or floating windows).
FOREGROUND_SERVICE: Lets the app run important tasks in the foreground with a visible notification so the system doesn’t kill it.
FOREGROUND_SERVICE_DATA_SYNC: Specifically allows a foreground service to perform data synchronization tasks (like uploading or downloading files).
POST_NOTIFICATIONS: Allows the app to send and display notifications to the user (required from Android 13+).
These permissions are mainly used for background tasks, overlays, and showing notifications while the app is actively running important processes.
//sdp
implementation("com.intuit.sdp:sdp-android:1.1.1")
// Glide (latest stable major — Glide 5.x)
implementation("com.github.bumptech.glide:glide:5.0.5")
kapt("com.github.bumptech.glide:compiler:5.0.5")
// Lifecycle ViewModel & Runtime (latest stable Lifecycle)
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.10.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0")
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />l̥
<queries>
<package android:name="com.google.android.youtube" />
<package android:name="com.android.launcher3" />
<package android:name="com.instagram.android" />
<package android:name="com.whatsapp" />
</queries>
if do this
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
This code is used to check and request Usage Access Permission, not normal storage permission.
hasUsagePermission() checks whether the app has permission to access app usage statistics using AppOpsManager.OPSTR_GET_USAGE_STATS.
If permission is not granted, requestUsagePermission() opens the Usage Access Settings screen using Settings.ACTION_USAGE_ACCESS_SETTINGS.
The user must manually enable the permission from system settings (it cannot be granted automatically)
This permission (PACKAGE_USAGE_STATS) is mainly used to track which apps are used and for how long.
It is not required for accessing files from storage.
if (!Common.hasUsagePermission(requireActivity())) {
Common.requestUsagePermission(requireActivity())
}
fun requestUsagePermission(context: Context) {
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
}
fun hasUsagePermission(context: Context): Boolean {
val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
val mode = appOps.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName
)
return mode == AppOpsManager.MODE_ALLOWED
}
MANAGE_EXTERNAL_STORAGE permission is not required for this use case.
Use READ_MEDIA_IMAGES / VIDEO / AUDIO (Android 13+) or READ_EXTERNAL_STORAGE (Android 12 and below).
Your structure is clean and suitable for a basic File Manager app.
binding.btnFileManager.id -> {
if (hasStoragePermission()) {
if (!Environment.isExternalStorageManager()) {
val intent = Intent(
Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
)
startActivity(intent)
} else {
requireActivity().startActivity(
Intent(
requireActivity(),
FileManagerActivity::class.java
)
)
}
} else {
requestPermissions(requireActivity())
Toast.makeText(requireActivity(), "Please allow Permission", Toast.LENGTH_SHORT)
.show()
}
}
fun hasStoragePermission(): Boolean {
return if (Build.VERSION.SDK_INT >= 33) {
ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.READ_MEDIA_IMAGES
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.READ_MEDIA_VIDEO
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.READ_MEDIA_AUDIO
) == PackageManager.PERMISSION_GRANTED
} else {
ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
}
}
fun requestPermissions(activity: Activity) {
if (Build.VERSION.SDK_INT >= 33) {
activity.requestPermissions(
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE
),
100
)
} else {
activity.requestPermissions(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE
),
100
)
}
}
MediaStore to get images, videos, audio, PDF, and APK files count and total size.
getCategoryStats() calculates file count and total storage size using MediaStore.MediaColumns.SIZE.
APK files are filtered using MIME type "application/vnd.android.package-archive".
getAppStats() correctly separates Installed Apps and System Apps using PackageManager.
class FileManagerActivity : AppCompatActivity() {
private lateinit var binding: ActivityFileManagerBinding
private var categoryList: List<CategoryModel> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = DataBindingUtil.setContentView(this, R.layout.activity_file_manager)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initView()
}
private fun initView() {
val adapter = FileCatAdapter { itemData ->
Log.e("TAG", "initView:gggg ${itemData.name} ")
when (itemData.name.trim()) {
"Images" -> {
val imageIntent = Intent(this, AllImagesActivity::class.java)
imageIntent.putExtra("content", "Images")
startActivity(imageIntent)
}
"Videos" -> {
val videoIntent = Intent(this, AllImagesActivity::class.java)
videoIntent.putExtra("content", "Videos")
startActivity(videoIntent)
}
"Audio" -> {
val audioIntent = Intent(this, AllImagesActivity::class.java)
audioIntent.putExtra("content", "Audio")
startActivity(audioIntent)
}
"PDF" -> {
val documentIntent = Intent(this, AllImagesActivity::class.java)
documentIntent.putExtra("content", "PDF")
startActivity(documentIntent)
}
"APK" -> {
val apkIntent = Intent(this, AllAppsActivity::class.java)
apkIntent.putExtra("content", "APK")
startActivity(apkIntent)
}
"Installed Apps" -> {
val installAppIntent = Intent(this, AllAppsActivity::class.java)
installAppIntent.putExtra("content", "Installed Apps")
startActivity(installAppIntent)
}
"System Apps" -> {
val systemAppsIntent = Intent(this, AllAppsActivity::class.java)
systemAppsIntent.putExtra("content", "System Apps")
startActivity(systemAppsIntent)
}
}
}
binding.rvCategories.adapter = adapter
binding.rvCategories.layoutManager = GridLayoutManager(this, 2)
lifecycleScope.launch(Dispatchers.Main) {
categoryList = getAllCategories(this@FileManagerActivity)
adapter.addAll(ArrayList(categoryList))
}
}
suspend fun getAllCategories(context: Context): List<CategoryModel> {
val imageStats = getCategoryStats(
context,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
val videoStats = getCategoryStats(
context,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)
val audioStats = getCategoryStats(
context,
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
)
val docUri = MediaStore.Files.getContentUri("external")
val docSelection =
"${MediaStore.Files.FileColumns.MIME_TYPE} IN (?,?,?,?)"
val docArgs = arrayOf(
"application/pdf",
/* "application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/plain"*/
)
val docStats = getCategoryStats(
context,
docUri,
docSelection,
docArgs
)
val apkSelection =
"${MediaStore.Files.FileColumns.MIME_TYPE}=?"
val apkArgs = arrayOf(
"application/vnd.android.package-archive"
)
val apkStats = getCategoryStats(
context,
docUri,
apkSelection,
apkArgs
)
val appStats = getAppStats(context)
return listOf(
CategoryModel(
"Images",
imageStats.count,
formatSize(imageStats.totalSize),
R.drawable.ic_launcher_foreground
),
CategoryModel(
"Videos",
videoStats.count,
formatSize(videoStats.totalSize),
R.drawable.ic_launcher_foreground
),
CategoryModel(
"Audio",
audioStats.count,
formatSize(audioStats.totalSize),
R.drawable.ic_launcher_foreground
),
CategoryModel(
"PDF",
docStats.count,
formatSize(docStats.totalSize),
R.drawable.ic_launcher_foreground
),
CategoryModel(
"APK",
apkStats.count,
formatSize(apkStats.totalSize),
R.drawable.ic_launcher_foreground
),
CategoryModel(
"Installed Apps",
appStats.userCount,
"",
R.drawable.ic_launcher_foreground
),
CategoryModel(
"System Apps",
appStats.systemCount,
"",
R.drawable.ic_launcher_foreground
)
)
}
fun formatSize(size: Long): String {
val kb = 1024
val mb = kb * 1024
val gb = mb * 1024
return when {
size >= gb -> String.format("%.2f GB", size.toFloat() / gb)
size >= mb -> String.format("%.2f MB", size.toFloat() / mb)
size >= kb -> String.format("%.2f KB", size.toFloat() / kb)
else -> "$size Bytes"
}
}
suspend fun getCategoryStats(
context: Context,
uri: Uri,
selection: String? = null,
selectionArgs: Array<String>? = null
): CategoryStats = withContext(Dispatchers.IO) {
var totalSize = 0L
var count = 0
val projection = arrayOf(
MediaStore.MediaColumns.SIZE
)
context.contentResolver.query(
uri,
projection,
selection,
selectionArgs,
null
)?.use { cursor ->
val sizeColumn =
cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)
while (cursor.moveToNext()) {
totalSize += cursor.getLong(sizeColumn)
count++
}
}
CategoryStats(count, totalSize)
}
fun getAppStats(context: Context): AppStats {
val pm = context.packageManager
val apps = pm.getInstalledApplications(PackageManager.GET_META_DATA)
var systemCount = 0
var userCount = 0
apps.forEach { appInfo ->
val isSystem =
(appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
val isUpdatedSystem =
(appInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
if (isSystem && !isUpdatedSystem) {
systemCount++
} else {
userCount++
}
}
return AppStats(systemCount, userCount)
}
}
CategoryModel → Represents each main category (Images, Videos, APK, etc.) with name, file count, total formatted size, and icon.
CategoryStats → Used internally to store raw count and total size (Long) before formatting.
MediaModel → Represents individual media files (image, video, audio) with name, size, URI, type, and optional duration (for videos).
AppStats → Stores total number of system apps and user-installed apps.
AllAppsModel → Represents individual installed apps with app name, package name, icon, version name, and system/user flag.
data class CategoryModel(
val name: String,
val count: Int,
val totalSize: String,
val icon: Int
)
data class CategoryStats(
val count: Int,
val totalSize: Long
)
data class MediaModel(
val name: String,
val size: Long,
val formattedSize: String,
val uri: Uri,
val type: MediaType? = null,
val duration: Long? = null // video ke liye
)
data class AppStats(
val systemCount: Int,
val userCount: Int
)
data class AllAppsModel(
val appName: String,
val packageName: String,
val icon: Drawable? = null,
val versionName: String,
val isSystemApp: Boolean
)
File Count Adapter
class FileCatAdapter(val onClick: (itemData: CategoryModel) -> Unit) :
RecyclerView.Adapter<FileCatAdapter.FileDataHolder>() {
private var fileList: ArrayList<CategoryModel> = ArrayList()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FileDataHolder {
val binding = FileDesignBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return FileDataHolder(binding)
}
override fun onBindViewHolder(
holder: FileDataHolder,
position: Int
) {
val itemData = fileList[position]
holder.setData(itemData)
holder.itemView.setOnClickListener {
onClick.invoke(itemData)
}
}
override fun getItemCount(): Int {
return fileList.size
}
fun addAll(fileList: ArrayList<CategoryModel>) {
this.fileList = ArrayList()
this.fileList.addAll(fileList)
notifyDataSetChanged()
}
class FileDataHolder(val binding: FileDesignBinding) : RecyclerView.ViewHolder(binding.root) {
fun setData(itemData: CategoryModel) {
binding.tvTitle.text = itemData.name
binding.tvCount.text = "${itemData.count}"
binding.tvSize.text = itemData.totalSize
}
}
}
This is a RecyclerView Adapter used to display file categories (Images, Videos, APK, etc.).
It takes a click listener lambda (onClick) to handle item clicks dynamically from the activity.
class AllImagesActivity : AppCompatActivity() {
private lateinit var binding: ActivityAllImagesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = DataBindingUtil.setContentView(this, R.layout.activity_all_images)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initView()
}
private fun initView() {
val adapter = AllImagesAdapter()
binding.rvAllImages.adapter = adapter
binding.rvAllImages.layoutManager = GridLayoutManager(this, 2)
val content = intent.getStringExtra("content")
Log.e("TAG", "initView:33 $content")
when (content) {
"Images" -> {
lifecycleScope.launch(Dispatchers.Main) {
val imageList = getAllImages(this@AllImagesActivity)
adapter.addAll(ArrayList(imageList))
}
}
"Videos" -> {
lifecycleScope.launch(Dispatchers.Main) {
val videoList = getAllVideos(this@AllImagesActivity)
adapter.addAll(ArrayList(videoList))
}
}
"Audio" -> {
lifecycleScope.launch(Dispatchers.Main) {
val audioList = getAllAudio(this@AllImagesActivity)
adapter.addAll(ArrayList(audioList))
}
}
"PDF" -> {
lifecycleScope.launch(Dispatchers.Main) {
val documentList = getAllDocuments(this@AllImagesActivity)
adapter.addAll(ArrayList(documentList))
}
}
}
}
suspend fun getAllImages(context: Context): List<MediaModel> =
withContext(Dispatchers.IO) {
val imageList = mutableListOf<MediaModel>()
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE
)
val sortOrder =
"${MediaStore.Images.Media.DATE_ADDED} DESC"
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
sortOrder
)?.use { cursor ->
val idColumn =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
imageList.add(
MediaModel(
name = name ?: "Unknown",
size = size,
formattedSize = Common.formatSize(size),
uri = contentUri,
type = MediaType.IMAGE,
null
)
)
}
}
imageList
}
suspend fun getAllVideos(context: Context): List<MediaModel> =
withContext(Dispatchers.IO) {
val list = mutableListOf<MediaModel>()
val projection = arrayOf(
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.DURATION
)
context.contentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
"${MediaStore.Video.Media.DATE_ADDED} DESC"
)?.use { cursor ->
val idCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)
val nameCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)
val sizeCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)
val durationCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)
while (cursor.moveToNext()) {
val id = cursor.getLong(idCol)
val name = cursor.getString(nameCol)
val size = cursor.getLong(sizeCol)
val duration = cursor.getLong(durationCol)
val uri = ContentUris.withAppendedId(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
id
)
list.add(
MediaModel(
name = name ?: "Unknown",
size = size,
formattedSize = Common.formatSize(size),
uri = uri,
type = MediaType.VIDEO,
duration = duration
)
)
}
}
list
}
suspend fun getAllAudio(context: Context): List<MediaModel> =
withContext(Dispatchers.IO) {
val list = mutableListOf<MediaModel>()
val projection = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.DURATION
)
context.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
"${MediaStore.Audio.Media.DATE_ADDED} DESC"
)?.use { cursor ->
val idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
val nameCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)
val sizeCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)
val durationCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
while (cursor.moveToNext()) {
val id = cursor.getLong(idCol)
val name = cursor.getString(nameCol)
val size = cursor.getLong(sizeCol)
val duration = cursor.getLong(durationCol)
val uri = ContentUris.withAppendedId(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
id
)
list.add(
MediaModel(
name = name ?: "Unknown",
size = size,
formattedSize = Common.formatSize(size),
uri = uri,
type = MediaType.AUDIO,
duration = duration
)
)
}
}
list
}
suspend fun getAllDocuments(context: Context): List<MediaModel> =
withContext(Dispatchers.IO) {
val list = mutableListOf<MediaModel>()
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.MIME_TYPE
)
val selection =
"${MediaStore.Files.FileColumns.MIME_TYPE}=?"
val selectionArgs = arrayOf("application/pdf")
context.contentResolver.query(
uri,
projection,
selection,
selectionArgs,
"${MediaStore.Files.FileColumns.DATE_ADDED} DESC"
)?.use { cursor ->
val idCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
val nameCol =
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
val sizeCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE)
while (cursor.moveToNext()) {
val id = cursor.getLong(idCol)
val name = cursor.getString(nameCol)
val size = cursor.getLong(sizeCol)
val contentUri = ContentUris.withAppendedId(uri, id)
list.add(
MediaModel(
name = name ?: "Unknown",
size = size,
formattedSize = Common.formatSize(size),
uri = contentUri,
type = MediaType.DOCUMENT
)
)
}
}
list
}
}
enum class MediaType {
IMAGE,
VIDEO,
AUDIO,
DOCUMENT
}
AllImagesActivity displays media files (Images, Videos, Audio, PDF) based on the selected category from intent.
It uses RecyclerView with GridLayoutManager to show items in a 2-column grid.
class AllImagesAdapter : RecyclerView.Adapter<AllImagesAdapter.ImageViewHolder>() {
private var allImageList: ArrayList<MediaModel> = ArrayList()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ImageViewHolder {
val binding = ImageDesignBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ImageViewHolder(binding)
}
override fun onBindViewHolder(
holder: ImageViewHolder,
position: Int
) {
val itemData = allImageList[position]
holder.setData(itemData)
}
override fun getItemCount(): Int {
return allImageList.size
}
fun addAll(fileList: ArrayList<MediaModel>) {
this.allImageList = ArrayList()
this.allImageList.addAll(fileList)
notifyDataSetChanged()
}
class ImageViewHolder(val binding: ImageDesignBinding) : RecyclerView.ViewHolder(binding.root) {
fun setData(itemData: MediaModel) {
binding.tvTitle.text = itemData.name
binding.tvSize.text = itemData.formattedSize
Glide.with(binding.ivImage.context).load(itemData.uri).into(binding.ivImage)
}
}
}
AllAppsActivity displays APK files, Installed Apps, and System Apps based on the selected category from intent.
It uses a RecyclerView with GridLayoutManager (3 columns) to show app items.
class AllAppsActivity : AppCompatActivity() {
private lateinit var binding: ActivityAllAppsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = DataBindingUtil.setContentView(this, R.layout.activity_all_apps)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initView()
}
private fun initView() {
val content = intent.getStringExtra("content")
val adapter = AllAppsAdapter()
binding.rvAllApps.adapter = adapter
binding.rvAllApps.layoutManager = GridLayoutManager(this, 3)
when (content) {
"APK" -> {
val apkList = getAllApkFiles()
adapter.addAll(ArrayList(apkList))
}
"Installed Apps" -> {
lifecycleScope.launch(Dispatchers.Main) {
val apkList = getInstalledApps(this@AllAppsActivity)
adapter.addAll(ArrayList(apkList))
}
}
"System Apps" -> {
lifecycleScope.launch(Dispatchers.Main) {
val apkList = getSystemApps(this@AllAppsActivity)
adapter.addAll(ArrayList(apkList))
}
}
}
}
fun getAllApkFiles(): List<AllAppsModel> {
val root = Environment.getExternalStorageDirectory()
val apkList = mutableListOf<AllAppsModel>()
root.walkTopDown().forEach { file ->
if (file.isFile && file.extension.equals("apk", true)) {
apkList.add(
AllAppsModel(
appName = file.nameWithoutExtension,
packageName = file.absolutePath,
icon = null,
versionName = "",
isSystemApp = false
)
)
}
}
return apkList
}
suspend fun getInstalledApps(context: Context): List<AllAppsModel> =
withContext(Dispatchers.IO) {
val pm = context.packageManager
val apps = pm.getInstalledApplications(PackageManager.GET_META_DATA)
val appList = mutableListOf<AllAppsModel>()
apps.forEach { appInfo ->
val packageInfo =
pm.getPackageInfo(appInfo.packageName, 0)
val apkFile = File(appInfo.sourceDir)
val appSize = if (apkFile.exists()) apkFile.length() else 0L
val isSystem =
(appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
val isUpdatedSystem =
(appInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
val finalIsSystem = isSystem && !isUpdatedSystem
appList.add(
AllAppsModel(
appName = pm.getApplicationLabel(appInfo).toString(),
packageName = appInfo.packageName,
icon = pm.getApplicationIcon(appInfo),
versionName = packageInfo.versionName ?: "",
/*apkPath = appInfo.sourceDir,
size = appSize,*/
isSystemApp = finalIsSystem
)
)
}
appList.sortedBy { it.appName.lowercase() }
}
suspend fun getSystemApps(context: Context): List<AllAppsModel> =
withContext(Dispatchers.IO) {
val pm = context.packageManager
val apps = pm.getInstalledApplications(PackageManager.GET_META_DATA)
val systemApps = mutableListOf<AllAppsModel>()
apps.forEach { appInfo ->
val isSystem =
(appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0
val isUpdatedSystem =
(appInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
// Pure system apps only
if (isSystem && !isUpdatedSystem) {
val packageInfo =
pm.getPackageInfo(appInfo.packageName, 0)
val apkFile = File(appInfo.sourceDir)
val size = if (apkFile.exists()) apkFile.length() else 0L
systemApps.add(
AllAppsModel(
appName = pm.getApplicationLabel(appInfo).toString(),
packageName = appInfo.packageName,
icon = pm.getApplicationIcon(appInfo),
versionName = packageInfo.versionName ?: "",
/* apkPath = appInfo.sourceDir,
size = size,*/
isSystemApp = true
)
)
}
}
systemApps.sortedBy { it.appName.lowercase() }
}
}
AllAppsAdapter is a RecyclerView Adapter used to display APK files and installed/system apps.
It stores a list of AllAppsModel and binds app data using ViewBinding (AppsDesignBinding).
class AllAppsAdapter : RecyclerView.Adapter<AllAppsAdapter.AppsViewHolder>() {
private var allAppsList: ArrayList<AllAppsModel> = ArrayList()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AppsViewHolder {
val binding = AppsDesignBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return AppsViewHolder(binding)
}
override fun onBindViewHolder(
holder: AppsViewHolder,
position: Int
) {
val itemData = allAppsList[position]
holder.setData(itemData)
}
override fun getItemCount(): Int {
return allAppsList.size
}
fun addAll(fileList: ArrayList<AllAppsModel>) {
this.allAppsList = ArrayList()
this.allAppsList.addAll(fileList)
notifyDataSetChanged()
}
class AppsViewHolder(val binding: AppsDesignBinding) : RecyclerView.ViewHolder(binding.root) {
fun setData(itemData: AllAppsModel) {
binding.tvAppName.text = itemData.appName
// Glide.with(binding.ivImage.context).load(itemData.uri).into(binding.ivImage)
}
}
}
This File Manager app is built using a clean and structured architecture with Activities, Adapters, and Data Models. It uses MediaStore to fetch images, videos, audio, documents, and APK files along with their count and total storage size. Installed and system apps are retrieved using PackageManager with proper filtering logic. RecyclerView with GridLayoutManager is used to display categories, media files, and apps efficiently. Coroutines with Dispatchers.IO ensure background processing for smooth performance. Overall, the implementation is modern, organized, and suitable for a functional Android File Manager application.
Top comments (0)