Full Code of GustavoASantos/Noti for AI

main 7f4c1fbac4e0 cached
136 files
337.8 KB
93.0k tokens
1 requests
Download .txt
Showing preview only (399K chars total). Download the full file or copy to clipboard to get everything.
Repository: GustavoASantos/Noti
Branch: main
Commit: 7f4c1fbac4e0
Files: 136
Total size: 337.8 KB

Directory structure:
gitextract_91b8wrhl/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── gustavoas/
│           │           └── noti/
│           │               ├── ProgressBarAppsAdapter.kt
│           │               ├── ProgressBarAppsRepository.kt
│           │               ├── SettingsActivity.kt
│           │               ├── Utils.kt
│           │               ├── fragments/
│           │               │   ├── BasePreferenceFragment.kt
│           │               │   ├── CircularBarFragment.kt
│           │               │   ├── LinearBarFragment.kt
│           │               │   ├── PerAppSettingsFragment.kt
│           │               │   └── SettingsFragment.kt
│           │               ├── model/
│           │               │   ├── DeviceConfiguration.kt
│           │               │   ├── ProgressBarApp.kt
│           │               │   └── ProgressNotification.kt
│           │               ├── notifications/
│           │               │   ├── DownloadProgressBar.kt
│           │               │   ├── GoogleTimerProgressBar.kt
│           │               │   ├── MediaProgressBar.kt
│           │               │   ├── PercentageProgressBar.kt
│           │               │   ├── ProgressBarNotification.kt
│           │               │   └── TimedProgressBar.kt
│           │               ├── preferences/
│           │               │   ├── BannerPreference.kt
│           │               │   ├── BarStylesListPreference.kt
│           │               │   └── SeekBarPreference.kt
│           │               └── services/
│           │                   ├── AccessibilityService.kt
│           │                   ├── FullscreenDetectionService.kt
│           │                   └── NotificationListenerService.kt
│           └── res/
│               ├── color/
│               │   └── outlined_button_selector.xml
│               ├── drawable/
│               │   ├── circular_mask.xml
│               │   ├── ic_accessibility.xml
│               │   ├── ic_apps.xml
│               │   ├── ic_bug_report.xml
│               │   ├── ic_calentile.xml
│               │   ├── ic_circular_progress_bar.xml
│               │   ├── ic_close.xml
│               │   ├── ic_colors.xml
│               │   ├── ic_contrast.xml
│               │   ├── ic_download.xml
│               │   ├── ic_download_filled.xml
│               │   ├── ic_fullscreen.xml
│               │   ├── ic_height.xml
│               │   ├── ic_horizontal.xml
│               │   ├── ic_info.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── ic_linear_progress_bar.xml
│               │   ├── ic_lockscreen.xml
│               │   ├── ic_margin_top.xml
│               │   ├── ic_music.xml
│               │   ├── ic_music_filled.xml
│               │   ├── ic_notification.xml
│               │   ├── ic_notification_bar.xml
│               │   ├── ic_overlay.xml
│               │   ├── ic_palette.xml
│               │   ├── ic_preview.xml
│               │   ├── ic_progress_bar_style.xml
│               │   ├── ic_rounded_corners.xml
│               │   ├── ic_share.xml
│               │   ├── ic_size.xml
│               │   ├── ic_sumup.xml
│               │   ├── layout_bg.xml
│               │   ├── selector_download.xml
│               │   ├── selector_music.xml
│               │   └── splashscreen.xml
│               ├── drawable-v31/
│               │   └── splashscreen.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── advanced_style_dialog.xml
│               │   ├── app_item.xml
│               │   ├── banner_preference.xml
│               │   ├── button_toggle_group.xml
│               │   ├── fragment_per_app_settings.xml
│               │   ├── material_switch.xml
│               │   ├── per_app_settings_footer.xml
│               │   ├── progress_bar.xml
│               │   └── seekbar_preference.xml
│               ├── layout-land/
│               │   └── button_toggle_group.xml
│               ├── menu/
│               │   └── settings_menu.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── resources.properties
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── attrs.xml
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── strings.xml
│               │   └── themes.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-ar-rSA/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-es-rMX/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-fr-rCA/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ka/
│               │   └── strings.xml
│               ├── values-lt/
│               │   └── strings.xml
│               ├── values-ml/
│               │   └── strings.xml
│               ├── values-nb-rNO/
│               │   └── strings.xml
│               ├── values-night/
│               │   ├── colors.xml
│               │   └── themes.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt-rPT/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sl/
│               │   └── strings.xml
│               ├── values-ta/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-v31/
│               │   └── arrays.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rTW/
│               │   └── strings.xml
│               └── xml/
│                   ├── accessibility_service_config.xml
│                   ├── backup_rules.xml
│                   ├── circular_bar_preferences.xml
│                   ├── data_extraction_rules.xml
│                   ├── linear_bar_preferences.xml
│                   └── preferences.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── renovate.json
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties


================================================
FILE: README.md
================================================
# Noti Progress Bar

[<img alt="banner" src="Images/Banner.png" />](https://play.google.com/store/apps/details?id=com.gustavoas.noti)

> Noti lets you easily track the progress of downloads, music and more directly from your status bar.

## ✨ Features

+ Track downloads, music, and timers from Google Clock.
+ Customizable progress bar or progress circle.
+ Adjustable progress circle to fit your phone's hole punch camera.
+ Automatically hides when in full screen mode.
+ Display Noti in the lockscreen by enabling the accessibility service.
+ Filter which apps you want to track.
+ Customize Noti on a per-app basis.

## ⬇️ Download

[<img alt="Get it on Google Play" height="100" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" />](https://play.google.com/store/apps/details?id=com.gustavoas.noti)

## 💜 Help Translate

<a href="https://hosted.weblate.org/engage/noti-progress-bar/">
<img src="https://hosted.weblate.org/widget/noti-progress-bar/strings/287x66-white.png" alt="Translation status" />
</a>

## 🔑 License

The source code for Noti is available under the GPL-3.0 License. However, distributing the compiled application anywhere, including on Google Play, requires explicit written permission from the original author.

================================================
FILE: app/.gitignore
================================================
/build
/release/
/src/debug/
/google-services.json


================================================
FILE: app/build.gradle
================================================
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.gms.google-services'
    id 'kotlin-parcelize'
}

android {
    namespace 'com.gustavoas.noti'
    compileSdk 35

    defaultConfig {
        applicationId "com.gustavoas.noti"
        minSdk 21
        targetSdk 34
        versionCode 90
        versionName "2.0"
        versionCode 95
        versionName "2.2"

        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".dogfood"
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    packagingOptions {
        resources {
            excludes += '/META-INF/{AL2.0,LGPL2.1}'
        }
    }
    androidResources {
        generateLocaleConfig true
    }
}

dependencies {
    implementation platform('com.google.firebase:firebase-bom:33.13.0')

    implementation 'androidx.core:core-ktx:1.16.0'
    implementation 'androidx.appcompat:appcompat:1.7.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
    implementation 'com.google.android.material:material:1.12.0'
    implementation 'androidx.palette:palette-ktx:1.0.0'
    implementation 'androidx.preference:preference-ktx:1.2.1'
    implementation 'androidx.core:core-splashscreen:1.0.1'
    implementation 'androidx.recyclerview:recyclerview:1.4.0'
    implementation 'com.github.kizitonwose.colorpreference:support:1.1.0'
    implementation 'com.github.kizitonwose.colorpreference:core:1.1.0'
    implementation 'com.github.eltos:simpledialogfragments:v3.7'
    implementation 'com.google.firebase:firebase-storage'
}

================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
        android:minSdkVersion="30"/>
    <uses-permission android:name="android.permission.VIBRATE" />

    <uses-sdk tools:overrideLibrary="androidx.core.splashscreen"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:enableOnBackInvokedCallback="true"
        android:theme="@style/Theme.NotiProgressBar"
        tools:targetApi="tiramisu">
        <activity
            android:name=".SettingsActivity"
            android:exported="true"
            android:label="@string/app_name_short"
            android:theme="@style/SplashScreen">
            <intent-filter>
                <action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".services.AccessibilityService"
            android:label="@string/app_name"
            android:exported="false"
            android:theme="@style/Theme.NotiProgressBar"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>
        <service
            android:name=".services.NotificationListenerService"
            android:label="@string/notificationListenerDescription"
            android:exported="false"
            android:theme="@style/Theme.NotiProgressBar"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>
        <service
            android:name=".services.FullscreenDetectionService"
            android:exported="false" />
    </application>

</manifest>

================================================
FILE: app/src/main/java/com/gustavoas/noti/ProgressBarAppsAdapter.kt
================================================
package com.gustavoas.noti

import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.edit
import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButtonToggleGroup
import com.gustavoas.noti.Utils.dpToPx
import com.gustavoas.noti.Utils.getApplicationIcon
import com.gustavoas.noti.Utils.getApplicationName
import com.gustavoas.noti.Utils.getColorForApp
import com.gustavoas.noti.Utils.showColorDialog
import com.gustavoas.noti.model.ProgressBarApp
import com.gustavoas.noti.services.AccessibilityService

class ProgressBarAppsAdapter(
    private val fragment: Fragment,
    private val context: Context,
    private val apps: ArrayList<ProgressBarApp>,
    private val appsRepository: ProgressBarAppsRepository
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private val VIEW_TYPE_HEADER = 0
    private val VIEW_TYPE_ITEM = 1
    private val VIEW_TYPE_FOOTER = 2

    override fun getItemViewType(position: Int): Int {
        return when (position) {
            0 -> VIEW_TYPE_HEADER
            itemCount - 1 -> VIEW_TYPE_FOOTER
            else -> VIEW_TYPE_ITEM
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            VIEW_TYPE_HEADER -> {
                val view = LayoutInflater.from(context).inflate(R.layout.button_toggle_group, parent, false)
                HeaderViewHolder(view)
            }
            VIEW_TYPE_FOOTER -> {
                val view = LayoutInflater.from(context).inflate(R.layout.per_app_settings_footer, parent, false)
                FooterViewHolder(view)
            }
            else -> {
                val view = LayoutInflater.from(context).inflate(R.layout.app_item, parent, false)
                ItemViewHolder(view)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder !is ItemViewHolder) return

        with(apps[position - 1]) {
            holder.appName.text = getApplicationName(context, packageName) ?: packageName
            val appIcon = getApplicationIcon(context, packageName) ?: Color.TRANSPARENT.toDrawable()
            val appIconSize = dpToPx(context, 36)
            appIcon.setBounds(0, 0, appIconSize, appIconSize)
            holder.appName.setCompoundDrawables(appIcon, null, null, null)
            holder.toggle.isChecked = showProgressBar
            holder.toggle.setOnCheckedChangeListener { _, isChecked ->
                showProgressBar = isChecked
                appsRepository.updateApp(this)
                if (!isChecked) {
                    val intent = Intent(context, AccessibilityService::class.java)
                    intent.putExtra("packageName", packageName)
                    intent.putExtra("removal", true)
                    context.startService(intent)
                }
            }
            holder.background.setOnClickListener {
                holder.toggle.toggle()
            }

            val barColor = getColorForApp(context, this)
            holder.colorPicker.setBackgroundColor(barColor)
            holder.colorPicker.setOnClickListener {
                showColorDialog(
                    fragment,
                    barColor,
                    (position - 1).toString(),
                    !useDefaultColor,
                )
            }
        }
    }

    override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
        super.onViewRecycled(holder)

        if (holder !is ItemViewHolder) return
        holder.toggle.setOnCheckedChangeListener(null)
    }

    override fun getItemCount(): Int = apps.size + 2

    class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val toggleGroup: MaterialButtonToggleGroup = itemView.findViewById(R.id.toggleGroup)

        init {
            val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.itemView.context)
            val enableDownloads = sharedPrefs.getBoolean("showForDownloads", true)
            val enableMedia = sharedPrefs.getBoolean("showForMedia", true)

            if (enableDownloads) {
                toggleGroup.check(R.id.toggleDownloads)
            }

            if (enableMedia) {
                toggleGroup.check(R.id.toggleMedia)
            }

            toggleGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
                when (checkedId) {
                    R.id.toggleDownloads -> {
                        sharedPrefs.edit { putBoolean("showForDownloads", isChecked) }
                    }
                    R.id.toggleMedia -> {
                        sharedPrefs.edit { putBoolean("showForMedia", isChecked) }
                    }
                }
            }
        }
    }

    class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val appName: TextView = view.findViewById(R.id.app_name)
        val toggle: CheckBox = view.findViewById(R.id.checkbox)
        val background: LinearLayout = view.findViewById(R.id.item_container)
        val colorPicker: Button = view.findViewById(R.id.color_picker)
    }

    class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/ProgressBarAppsRepository.kt
================================================
package com.gustavoas.noti

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.gustavoas.noti.model.ProgressBarApp

class ProgressBarAppsRepository(context: Context) :
    SQLiteOpenHelper(context, DATABASE_NAME, null, 3) {
    companion object {
        const val DATABASE_NAME = "progressBarApps"
        const val TABLE_NAME = "apps"
        const val COLUMN_PACKAGE = "package_name"
        const val COLUMN_SHOW_PROGRESS = "show_progress"
        const val COLUMN_COLOR = "color"
        const val COLUMN_USE_DEFAULT = "default_color"
        const val COLUMN_USE_MATERIAL_YOU = "material_you_color"

        private val apps: MutableList<ProgressBarApp> = mutableListOf()
    }

    init {
        if (apps.isEmpty()) {
            apps.addAll(getAllApps())
        }
    }

    override fun onCreate(db: SQLiteDatabase?) {
        db?.execSQL(
            "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
                    "$COLUMN_PACKAGE TEXT PRIMARY KEY," +
                    "$COLUMN_SHOW_PROGRESS INTEGER NOT NULL DEFAULT 1," +
                    "$COLUMN_COLOR INTEGER DEFAULT NULL," +
                    "$COLUMN_USE_DEFAULT INTEGER NOT NULL DEFAULT 1," +
                    "$COLUMN_USE_MATERIAL_YOU INTEGER NOT NULL DEFAULT 0" +
                    ")"
        )

        val knownApps = listOf(
            "com.google.android.deskclock",
            "com.android.chrome",
            "com.duckduckgo.mobile.android",
            "com.android.vending",
            "com.epicgames.portal",
            "code.name.monkey.retromusic",
            "com.google.android.apps.youtube.music",
            "com.spotify.music",
        )

        knownApps.forEach { packageName ->
            db?.execSQL(
                "INSERT INTO $TABLE_NAME ($COLUMN_PACKAGE) VALUES ('$packageName')"
            )
        }
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        if (oldVersion < 2) {
            db?.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_COLOR INTEGER NOT NULL DEFAULT 1")
        }

        if (oldVersion < 3) {
            db?.execSQL("ALTER TABLE $TABLE_NAME RENAME TO ${TABLE_NAME}_old")

            db?.execSQL(
                "CREATE TABLE $TABLE_NAME (" +
                        "$COLUMN_PACKAGE TEXT PRIMARY KEY," +
                        "$COLUMN_SHOW_PROGRESS INTEGER NOT NULL DEFAULT 1," +
                        "$COLUMN_COLOR INTEGER DEFAULT NULL," +
                        "$COLUMN_USE_DEFAULT INTEGER NOT NULL DEFAULT 1," +
                        "$COLUMN_USE_MATERIAL_YOU INTEGER NOT NULL DEFAULT 0" +
                        ")"
            )

            db?.execSQL(
                "INSERT INTO $TABLE_NAME ($COLUMN_PACKAGE, $COLUMN_SHOW_PROGRESS, $COLUMN_COLOR) " +
                        "SELECT $COLUMN_PACKAGE, $COLUMN_SHOW_PROGRESS, $COLUMN_COLOR FROM ${TABLE_NAME}_old"
            )

            db?.execSQL("DROP TABLE ${TABLE_NAME}_old")

            db?.execSQL("UPDATE $TABLE_NAME SET " +
                    "$COLUMN_USE_DEFAULT = CASE WHEN $COLUMN_COLOR = 1 THEN 1 ELSE 0 END, " +
                    "$COLUMN_USE_MATERIAL_YOU = CASE WHEN $COLUMN_COLOR = 2 THEN 1 ELSE 0 END, " +
                    "$COLUMN_COLOR = CASE WHEN $COLUMN_COLOR IN (1, 2) THEN NULL ELSE $COLUMN_COLOR END"
            )
        }
    }

    fun addApp(app: ProgressBarApp): ProgressBarApp {
        writableDatabase.execSQL(
            "INSERT OR IGNORE INTO $TABLE_NAME ($COLUMN_PACKAGE) VALUES (?)",
            arrayOf(app.packageName)
        )

        apps.firstOrNull {
            it.packageName == app.packageName
        }?.let {
            return it
        }

        apps.add(app)

        return app
    }

    fun updateApp(app: ProgressBarApp) {
        writableDatabase.execSQL(
            "UPDATE $TABLE_NAME " +
                    "SET $COLUMN_SHOW_PROGRESS = ?, " +
                    "$COLUMN_COLOR = ?, " +
                    "$COLUMN_USE_DEFAULT = ?, " +
                    "$COLUMN_USE_MATERIAL_YOU = ? " +
                    "WHERE $COLUMN_PACKAGE = ?",
            arrayOf(
                if (app.showProgressBar) 1 else 0,
                app.color,
                if (app.useDefaultColor) 1 else 0,
                if (app.useMaterialYouColor) 1 else 0,
                app.packageName
            )
        )

        apps.indexOfFirst {
            it.packageName == app.packageName
        }.let { index ->
            if (index != -1) {
                apps[index] = app
            }
        }
    }

    fun showProgressForApp(packageName: String): Boolean? {
        apps.firstOrNull {
            it.packageName == packageName
        }?.let {
            return it.showProgressBar
        }

        readableDatabase.rawQuery(
            "SELECT $COLUMN_SHOW_PROGRESS FROM $TABLE_NAME WHERE $COLUMN_PACKAGE = ?",
            arrayOf(packageName)
        ).use { cursor ->
            if (cursor.moveToFirst()) {
                return cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_SHOW_PROGRESS)) == 1
            }
        }

        return null
    }

    fun getApp(packageName: String): ProgressBarApp? {
        apps.firstOrNull {
            it.packageName == packageName
        }?.let {
            return it
        }

        readableDatabase.rawQuery(
            "SELECT * FROM $TABLE_NAME WHERE $COLUMN_PACKAGE = ?",
            arrayOf(packageName)
        ).use { cursor ->
            if (cursor.moveToFirst()) {
                val showProgress = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_SHOW_PROGRESS))
                val colorIndex = cursor.getColumnIndexOrThrow(COLUMN_COLOR)
                val color = if (cursor.isNull(colorIndex)) {
                    null
                } else {
                    cursor.getInt(colorIndex)
                }
                val useDefault = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_USE_DEFAULT))
                val useMaterialYou = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_USE_MATERIAL_YOU))
                return ProgressBarApp(
                    packageName,
                    showProgress == 1,
                    color,
                    useDefault == 1,
                    useMaterialYou == 1
                )
            }
        }

        return null
    }

    private fun getAllApps(): MutableList<ProgressBarApp> {
        val apps = mutableListOf<ProgressBarApp>()
        readableDatabase.rawQuery(
            "SELECT * FROM $TABLE_NAME", null
        ).use { cursor ->
            val packageIndex = cursor.getColumnIndexOrThrow(COLUMN_PACKAGE)
            val showProgressIndex = cursor.getColumnIndexOrThrow(COLUMN_SHOW_PROGRESS)
            val colorIndex = cursor.getColumnIndexOrThrow(COLUMN_COLOR)
            val useDefaultIndex = cursor.getColumnIndexOrThrow(COLUMN_USE_DEFAULT)
            val useMaterialYouIndex = cursor.getColumnIndexOrThrow(COLUMN_USE_MATERIAL_YOU)

            while (cursor.moveToNext()) {
                val packageName = cursor.getString(packageIndex)
                val showProgress = cursor.getInt(showProgressIndex)
                val color = if (cursor.isNull(colorIndex)) {
                    null
                } else {
                    cursor.getInt(colorIndex)
                }
                val useDefault = cursor.getInt(useDefaultIndex)
                val useMaterialYou = cursor.getInt(useMaterialYouIndex)
                apps.add(
                    ProgressBarApp(
                        packageName,
                        showProgress == 1,
                        color,
                        useDefault == 1,
                        useMaterialYou == 1
                    )
                )
            }
        }

        return apps
    }

    fun getAll(): List<ProgressBarApp> {
        return apps.toList()
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/SettingsActivity.kt
================================================
package com.gustavoas.noti

import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Xml
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.gustavoas.noti.Utils.dpToPx
import com.gustavoas.noti.Utils.getFirebaseConfigStorageReference
import com.gustavoas.noti.Utils.getScreenLargeSide
import com.gustavoas.noti.Utils.getScreenSmallSide
import com.gustavoas.noti.Utils.hasAccessibilityPermission
import com.gustavoas.noti.Utils.hasNotificationListenerPermission
import com.gustavoas.noti.Utils.hasSystemAlertWindowPermission
import com.gustavoas.noti.fragments.CircularBarFragment
import com.gustavoas.noti.fragments.LinearBarFragment
import com.gustavoas.noti.fragments.PerAppSettingsFragment
import com.gustavoas.noti.fragments.SettingsFragment
import com.gustavoas.noti.model.DeviceConfiguration
import com.gustavoas.noti.model.ProgressBarApp
import com.gustavoas.noti.model.ProgressNotification
import com.gustavoas.noti.services.AccessibilityService
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import org.xmlpull.v1.XmlPullParser
import java.io.InputStream
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.sqrt

class SettingsActivity : AppCompatActivity(),
    PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
    SharedPreferences.OnSharedPreferenceChangeListener {
    private val previewFab by lazy { findViewById<ExtendedFloatingActionButton>(R.id.previewFab) }
    private val topAppBar by lazy { findViewById<MaterialToolbar>(R.id.topAppBar) }
    private val appBarLayout by lazy { findViewById<AppBarLayout>(R.id.appBarLayout) }
    private var offsetChangeListener: OnOffsetChangedListener? = null
    private val handler = Handler(Looper.getMainLooper())

    private val sizeDependentPrefs = arrayOf(
        Pair("advancedProgressBarStyle", false),
        Pair("progressBarStyle", "linear"),
        Pair("progressBarStylePortrait", "linear"),
        Pair("progressBarStyleLandscape", "linear"),
        Pair("circularProgressBarThickness", 15),
        Pair("circularProgressBarSize", 65),
        Pair("circularProgressBarTopOffset", 60),
        Pair("circularProgressBarHorizontalOffset", 0),
        Pair("linearProgressBarSize", 15),
        Pair("matchStatusBarHeight", false),
        Pair("linearProgressBarMarginTop", 0),
        Pair("showBelowNotch", false),
    )

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        if (sizeDependentPrefs.any { it.first == key }) {
            val defaultValue = sizeDependentPrefs.first { it.first == key }.second
            val width = getScreenSmallSide(this)
            val height = getScreenLargeSide(this)

            moveSharedPreferenceValue(key + height + "x" + width, key!!, defaultValue)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setTheme(R.style.SplashScreen)
            installSplashScreen()
        } else {
            setTheme(R.style.Theme_NotiProgressBar)
        }

        super.onCreate(savedInstanceState)

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

        if (!sharedPreferences.contains("progressBarStyle")) {
            runBlocking {
                setupDeviceConfiguration()
            }
        }

        if (!sharedPreferences.contains("progressBarColor")) {
            setMaterialYouAsDefault()
        }

        setupSizeDependentPrefs()

        sharedPreferences
            .registerOnSharedPreferenceChangeListener(this)

        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.settings, SettingsFragment())
                .commitNow()
        }

        updateUpNavigationVisibility()

        ViewCompat.setOnApplyWindowInsetsListener(window.decorView.rootView) { _, insets ->
            val keyboardInset = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
            if (keyboardInset > 0) {
                appBarLayout.setExpanded(false, true)
            }
            insets
        }
    }

    private fun setupSizeDependentPrefs() {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        val width = getScreenSmallSide(this)
        val height = getScreenLargeSide(this)

        for (pref in sizeDependentPrefs) {
            val relatedPrefs = sharedPreferences.all.filterKeys { it.startsWith(pref.first) }

            val sameRatio = relatedPrefs.filterKeys { it.length > pref.first.length && it[pref.first.length].isDigit() && it.drop(pref.first.length).split("x")[0].toInt() / it.drop(pref.first.length).split("x")[1].toInt() == height / width }

            if (sameRatio.isEmpty()) {
                continue
            }

            val closest = sameRatio.keys.minByOrNull { abs(it.drop(pref.first.length).split("x")[0].toInt() * it.drop(pref.first.length).split("x")[1].toInt() - height * width) }

            var reductionRatio = 1f

            if (pref.second is Int) {
                reductionRatio = sqrt((height * width).div(closest!!.drop(pref.first.length).split("x")[0].toFloat() * closest.drop(pref.first.length).split("x")[1].toFloat()))
            }

            moveSharedPreferenceValue(pref.first, closest!!, pref.second, reductionRatio)
        }
    }

    private fun moveSharedPreferenceValue(key: String, oldKey: String, defaultValue: Any, reductionRatio: Float = 1f) {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        sharedPreferences.edit {
            when (defaultValue) {
                is Boolean -> putBoolean(key, sharedPreferences.getBoolean(oldKey, defaultValue))
                is Int -> putInt(
                    key,
                    sharedPreferences.getInt(oldKey, defaultValue).times(reductionRatio)
                        .roundToInt()
                )
                is String -> putString(key, sharedPreferences.getString(oldKey, defaultValue))
            }
        }
    }

    override fun onStart() {
        super.onStart()

        val collapsingToolbarLayout = findViewById<CollapsingToolbarLayout>(R.id.collapsingToolbar)
        var isVisible = true
        var scrollRange = -1

        offsetChangeListener =
            OnOffsetChangedListener { barLayout, verticalOffset ->
                if (scrollRange == -1) {
                    scrollRange = barLayout?.totalScrollRange!!
                }
                if (scrollRange + verticalOffset < dpToPx(this, 25)) {
                    collapsingToolbarLayout.title = resources.getString(R.string.app_name_short)
                    isVisible = true
                } else if (isVisible) {
                    collapsingToolbarLayout.title = resources.getString(R.string.app_name)
                    isVisible = false
                }
            }

        appBarLayout.addOnOffsetChangedListener(offsetChangeListener)

        if (hasNotificationListenerPermission(this) && (hasAccessibilityPermission(this) || hasSystemAlertWindowPermission(this))) {
            previewFab.visibility = View.VISIBLE
        } else {
            previewFab.visibility = View.GONE
        }

        previewFab.setOnClickListener {
            simulateDownload()
        }

        supportFragmentManager.addOnBackStackChangedListener {
            updateUpNavigationVisibility()
        }

        topAppBar.setNavigationOnClickListener {
            supportFragmentManager.popBackStack()
        }

        topAppBar.setOnMenuItemClickListener { menuItem ->
            when (menuItem.itemId) {
                R.id.bug_report -> {
                    val sendEmail = Intent(Intent.ACTION_SENDTO).apply {
                        data =
                            ("mailto:gustavoasgas1+noti@gmail.com" + "?subject=" + Uri.encode("Noti")).toUri()
                    }
                    sendEmail.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    startActivity(sendEmail)
                    true
                }

                else -> false
            }
        }
    }

    override fun onStop() {
        super.onStop()

        if (offsetChangeListener != null) {
            appBarLayout.removeOnOffsetChangedListener(offsetChangeListener!!)
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        PreferenceManager.getDefaultSharedPreferences(this)
            .unregisterOnSharedPreferenceChangeListener(this)
    }

    override fun onPreferenceStartFragment(
        caller: PreferenceFragmentCompat,
        pref: Preference
    ): Boolean {
        val fragment = when (pref.key) {
            "CircularBarFragment" -> CircularBarFragment()
            "LinearBarFragment" -> LinearBarFragment()
            "PerAppSettingsFragment" -> PerAppSettingsFragment()
            else -> null
        }

        supportFragmentManager.beginTransaction()
            .replace(R.id.settings, fragment ?: return false)
            .addToBackStack(null)
            .commit()

        return true
    }

    private fun simulateDownload() {
        val intent = Intent(this, AccessibilityService::class.java)
        handler.removeCallbacksAndMessages(null)
        val maxProgress = this.resources.getInteger(R.integer.progress_bar_max)
        val numberOfSteps = 4
        val stepSize = maxProgress / numberOfSteps
        for (i in stepSize..(maxProgress + stepSize) step stepSize) {
            handler.postDelayed({
                intent.putExtra("removal", i > maxProgress)
                intent.putExtra("id", packageName)
                intent.putExtra(
                    "progressNotification",
                    ProgressNotification(
                        ProgressBarApp(
                            packageName,
                            true
                        ),
                        i,
                        10
                    )
                )
                startService(intent)
            }, ((i - stepSize) * 1000 / stepSize).toLong())
        }
    }

    private suspend fun setupDeviceConfiguration() {
        if (!Utils.isInternetAvailable(this)) {
            return
        }

        val configRef = getFirebaseConfigStorageReference()

        try {
            val taskSnapshot = configRef.stream.await()

            val inputStream: InputStream = taskSnapshot.stream

            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
            sharedPreferences.edit {
                putString("progressBarStyle", "circular")
            }

            parseDeviceConfiguration(inputStream)

        } catch (_: Exception) {
            val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
            sharedPreferences.edit {
                putString("progressBarStyle", "linear")
            }
        }
    }

    private fun parseDeviceConfiguration(input: InputStream) {
        val parser: XmlPullParser = Xml.newPullParser()
        parser.setInput(input, null)

        while (parser.eventType != XmlPullParser.END_DOCUMENT) {
            when (parser.eventType) {
                XmlPullParser.START_TAG -> {
                    if (parser.name == "display") {
                        val displayConfig = DeviceConfiguration()
                        displayConfig.deviceWidth = parser.getAttributeValue(null, "width")
                        displayConfig.deviceHeight = parser.getAttributeValue(null, "height")
                        displayConfig.configuration = parser.getAttributeValue(null, "configuration")
                        parseDisplay(displayConfig, parser)
                    }
                }
            }
            parser.next()
        }
    }

    private fun parseDisplay(config: DeviceConfiguration, parser: XmlPullParser) {
        while (parser.eventType != XmlPullParser.END_DOCUMENT) {
            when (parser.eventType) {
                XmlPullParser.START_TAG -> {
                    when (parser.name) {
                        "size" -> config.size = parser.nextText()
                        "marginTop" -> config.marginTop = parser.nextText()
                        "topOffset" -> config.topOffset = parser.nextText()
                        "offset" -> config.horizontalOffset = parser.nextText()
                    }
                }

                XmlPullParser.END_TAG -> {
                    if (parser.name == "display") {
                        break
                    }
                }
            }
            parser.next()
        }

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

        val currentWidth = getScreenSmallSide(this)
        val currentHeight = getScreenLargeSide(this)

        if (config.deviceWidth == "" || config.deviceHeight == "") {
            if ((config.deviceWidth != "" && currentWidth == config.deviceWidth?.toInt()) ||
                (config.deviceHeight != "" && currentHeight == config.deviceHeight?.toInt())) {
                config.deviceWidth = currentWidth.toString()
                config.deviceHeight = currentHeight.toString()
            }

            if (config.deviceWidth == "" && config.deviceHeight != "") {
                config.deviceWidth = (currentWidth * config.deviceHeight!!.toInt() / currentHeight).toString()
            } else if (config.deviceHeight == "" && config.deviceWidth != "") {
                config.deviceHeight = (currentHeight * config.deviceWidth!!.toInt() / currentWidth).toString()
            }

            if (config.deviceWidth == "" || config.deviceHeight == "") {
                config.deviceWidth = currentWidth.toString()
                config.deviceHeight = currentHeight.toString()
            }
        }

        val appendix = config.deviceHeight + "x" + config.deviceWidth

        if (config.configuration == "circular") {
            sharedPreferences.edit {
                putString("progressBarStyle$appendix", "circular")
                putBoolean("blackBackground", true)
                putInt("circularProgressBarSize$appendix", config.size?.toIntOrNull() ?: 65)
                putInt("circularProgressBarTopOffset$appendix", config.topOffset?.toIntOrNull() ?: 60)
                putInt("circularProgressBarHorizontalOffset$appendix", config.horizontalOffset?.toIntOrNull() ?: 0)
            }
        } else {
            sharedPreferences.edit {
                putString("progressBarStyle$appendix", "linear")
            }
        }
    }

    private fun setMaterialYouAsDefault() {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            sharedPreferences.edit {
                putInt(
                    "progressBarColor", ContextCompat.getColor(
                        this@SettingsActivity,
                        R.color.system_accent_color
                    )
                )
                putBoolean("usingMaterialYouColor", true)
            }
        } else {
            sharedPreferences.edit {
                putInt(
                    "progressBarColor", ContextCompat.getColor(
                        this@SettingsActivity,
                        R.color.purple_500
                    )
                )
            }
        }
    }

    private fun updateUpNavigationVisibility() {
        if (supportFragmentManager.backStackEntryCount > 0) {
            topAppBar.setNavigationIcon(androidx.appcompat.R.drawable.abc_ic_ab_back_material)
            topAppBar.setNavigationIconTint(ContextCompat.getColor(this, R.color.text))
            topAppBar.setTitleMargin(0, 0, dpToPx(this, 40), 0)
        } else {
            topAppBar.navigationIcon = null
            topAppBar.setTitleMargin(0, 0, 0, 0)
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/Utils.kt
================================================
package com.gustavoas.noti

import android.content.ComponentName
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager.NameNotFoundException
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.provider.Settings
import android.util.DisplayMetrics
import android.view.Display
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.StorageReference
import com.google.firebase.storage.ktx.storage
import com.gustavoas.noti.model.ProgressBarApp
import com.gustavoas.noti.services.AccessibilityService
import com.gustavoas.noti.services.NotificationListenerService
import eltos.simpledialogfragment.color.SimpleColorDialog
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.nio.charset.StandardCharsets

object Utils {
    fun hasAccessibilityPermission(context: Context): Boolean {
        val accessibilityServiceComponentName = ComponentName(context, AccessibilityService::class.java)
        val enabledServices = Settings.Secure.getString(context.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)

        return enabledServices.orEmpty().let {
            it.contains(accessibilityServiceComponentName.flattenToString()) ||
                    it.contains(accessibilityServiceComponentName.flattenToShortString())
        }
    }

    fun hasNotificationListenerPermission(context: Context): Boolean {
        val notificationListenerComponentName = ComponentName(context, NotificationListenerService::class.java)
        val enabledServices = Settings.Secure.getString(context.contentResolver, "enabled_notification_listeners")
        return enabledServices?.contains(notificationListenerComponentName.flattenToString()) ?: false
    }

    fun hasSystemAlertWindowPermission(context: Context): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Settings.canDrawOverlays(context)
        } else {
            true
        }
    }

    fun dpToPx(context: Context, dp: Int): Int {
        return (dp * context.resources.displayMetrics.density).toInt()
    }

    fun isInternetAvailable(context: Context): Boolean {
        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val network = connectivityManager.activeNetwork ?: return false
            val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
            return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        } else {
            val activeNetworkInfo = connectivityManager.activeNetworkInfo
            return activeNetworkInfo != null && activeNetworkInfo.isConnected
        }
    }

    fun getFirebaseConfigStorageReference(): StorageReference {
        val storage = Firebase.storage
        val storageRef = storage.reference

        val brand = Build.BRAND.lowercase()
        var model = Build.DEVICE.lowercase().replace("\\W".toRegex(), "")

        if (brand == "xiaomi" || brand == "redmi" || brand == "poco") {
            while (model.endsWith("in")) {
                model = model.dropLast(2)
            }
        }

        return storageRef.child("configs/$brand/$model.xml")
    }

    fun shareConfigToFirebase(context: Context) {
        if (!isInternetAvailable(context)) {
            Toast.makeText(context, context.getString(R.string.shareConfigNoInternetMessage), Toast.LENGTH_SHORT).show()
            return
        }

        val configRef = getFirebaseConfigStorageReference()

        configRef.stream.addOnSuccessListener { taskSnapshot ->
            val inputStream: InputStream = taskSnapshot.stream

            val outputStream = ByteArrayOutputStream()
            val buffer = ByteArray(1024)
            var length: Int
            while (inputStream.read(buffer).also { length = it } != -1) {
                outputStream.write(buffer, 0, length)
            }
            val existingFileContent = outputStream.toString(StandardCharsets.UTF_8.name())

            if (existingFileContent != buildConfig(context)) {
                uploadConfigToStorageRef(context, configRef)
            }

        }.addOnFailureListener {
            uploadConfigToStorageRef(context, configRef)
        }

        Toast.makeText(context, context.getString(R.string.shareConfigPositiveMessage), Toast.LENGTH_SHORT).show()
    }

    private fun uploadConfigToStorageRef(context: Context, storageRef: StorageReference) {
        val config = buildConfig(context)
        val xmlInputStream = ByteArrayInputStream(config.toByteArray(StandardCharsets.UTF_8))

        storageRef.putStream(xmlInputStream)
    }

    private fun buildConfig(context: Context): String {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)

        val stylePrefs = sharedPreferences.all.filterKeys { it.startsWith("progressBarStyle") && it.last().isDigit() }

        if (stylePrefs.isEmpty() || stylePrefs.none { it.value == "circular" }) {
            return ""
        }

        val config = StringBuilder()
        config.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
        config.append("<device>\n")

        val sizes = stylePrefs.keys.map { str -> str.dropWhile { !it.isDigit() } }.distinct()

        sizes.forEach {
            config.append(buildConfigForDisplay(context, it)).append("\n")
        }

        config.append("</device>")

        return config.toString()
    }

    private fun buildConfigForDisplay(context: Context, display: String): String {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
        val height = display.split("x")[0].toInt()
        val width = display.split("x")[1].toInt()
        val configuration = sharedPreferences.getString("progressBarStyle$display", "linear")

        val displayConfig = StringBuilder()
        displayConfig.append("\t<display width=\"$width\" height=\"$height\" configuration=\"$configuration\">\n")

        if (configuration == "circular") {
            val size = sharedPreferences.getInt("circularProgressBarSize$display", 65)
            val marginTop = sharedPreferences.getInt("circularProgressBarTopOffset$display", 60)
            val offset = sharedPreferences.getInt("circularProgressBarHorizontalOffset$display", 0)

            displayConfig.append("\t\t<size>$size</size>\n")
            displayConfig.append("\t\t<topOffset>$marginTop</topOffset>\n")
            displayConfig.append("\t\t<offset>$offset</offset>\n")
        }

        displayConfig.append("\t</display>")

        return displayConfig.toString()
    }

    fun showColorDialog(fragment: Fragment, color: Int, tag: String, reset: Boolean = false) {
        if (reset) {
            SimpleColorDialog.build()
                .colorPreset(color)
                .colors(fragment.context, R.array.colorsArrayValues)
                .allowCustom(true)
                .showOutline(0x46000000)
                .gridNumColumn(5)
                .choiceMode(SimpleColorDialog.SINGLE_CHOICE)
                .neg()
                .neut(R.string.reset)
                .show(fragment, tag)
        } else {
            SimpleColorDialog.build()
                .colorPreset(color)
                .colors(fragment.context, R.array.colorsArrayValues)
                .allowCustom(true)
                .showOutline(0x46000000)
                .gridNumColumn(5)
                .choiceMode(SimpleColorDialog.SINGLE_CHOICE)
                .neg()
                .show(fragment, tag)
        }
    }

    fun getRealDisplayHeight(context: Context): Int {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager
            windowManager.currentWindowMetrics.bounds.height()
        } else {
            val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as android.hardware.display.DisplayManager
            val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY)!!
            val metrics = DisplayMetrics()
            display.getRealMetrics(metrics)
            metrics.heightPixels
        }
    }

    private fun getRealDisplayWidth(context: Context): Int {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager
            windowManager.currentWindowMetrics.bounds.width()
        } else {
            val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as android.hardware.display.DisplayManager
            val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY)!!
            val metrics = DisplayMetrics()
            display.getRealMetrics(metrics)
            metrics.widthPixels
        }
    }

    fun getScreenSmallSide(context: Context): Int {
        return minOf(getRealDisplayHeight(context), getRealDisplayWidth(context))
    }

    fun getScreenLargeSide(context: Context): Int {
        return maxOf(getRealDisplayHeight(context), getRealDisplayWidth(context))
    }

    fun vibrate(context: Context) {
        val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val vibratorManager =
                ContextCompat.getSystemService(context, VibratorManager::class.java)
            vibratorManager?.defaultVibrator
        } else {
            @Suppress("DEPRECATION")
            context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        }

        if (vibrator == null) {
            return
        }

        vibrator.cancel()

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            vibrator.vibrate(VibrationEffect.createOneShot(1, 200))
        } else {
            @Suppress("DEPRECATION")
            vibrator.vibrate(1)
        }
    }

    fun getStatusBarHeight(context: Context): Int {
        return context.resources.getDimensionPixelSize(
            (context.resources.getIdentifier(
                "status_bar_height",
                "dimen",
                "android"
            )))
    }

    fun getApplicationInfo(context: Context, packageName: String): ApplicationInfo? {
        return try {
            context.packageManager.getApplicationInfo(packageName, 0)
        } catch (e: NameNotFoundException) {
            null
        }
    }

    fun getApplicationName(context: Context, packageName: String): String? {
        return context.packageManager.getApplicationLabel(
            getApplicationInfo(context, packageName) ?: return null
        ).toString()
    }

    fun getApplicationIcon(context: Context, packageName: String): Drawable? {
        return context.packageManager.getApplicationIcon(
            getApplicationInfo(context, packageName) ?: return null
        )
    }

    fun getColorForApp(context: Context, progressBarApp: ProgressBarApp): Int {
        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
        val useNotificationColor = sharedPrefs.getBoolean("useNotificationColor", false)
        val useMaterialYou = sharedPrefs.getBoolean("usingMaterialYouColor", false)

        if ((progressBarApp.useDefaultColor && useNotificationColor) ||
            (!progressBarApp.useDefaultColor && !progressBarApp.useMaterialYouColor)) {
            progressBarApp.color?.let { return it }
        }

        if ((progressBarApp.useMaterialYouColor) ||
            (progressBarApp.useDefaultColor && useMaterialYou)) {
            return ContextCompat.getColor(context, R.color.system_accent_color)
        }

        return sharedPrefs.getInt(
            "progressBarColor", ContextCompat.getColor(context, R.color.purple_500)
        )
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/fragments/BasePreferenceFragment.kt
================================================
package com.gustavoas.noti.fragments

import android.os.Bundle
import android.view.View
import androidx.preference.PreferenceFragmentCompat
import com.gustavoas.noti.Utils

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val preferencesView = listView
        preferencesView.setPadding(0, 0, 0, Utils.dpToPx(requireContext(), 100))
        preferencesView.isVerticalScrollBarEnabled = false
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/fragments/CircularBarFragment.kt
================================================
package com.gustavoas.noti.fragments

import android.content.SharedPreferences
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils

class CircularBarFragment : BasePreferenceFragment(),
    SharedPreferences.OnSharedPreferenceChangeListener {
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        // TODO: If offset is not zero and hole punch size changes toast suggesting a 0.5x change
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.circular_bar_preferences, rootKey)

        findPreference<Preference>("shareConfig")?.setOnPreferenceClickListener {
            Utils.shareConfigToFirebase(requireContext())
            true
        }

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())

        sharedPreferences.registerOnSharedPreferenceChangeListener(this)
    }

    override fun onDestroy() {
        super.onDestroy()

        PreferenceManager.getDefaultSharedPreferences(requireContext())
            .unregisterOnSharedPreferenceChangeListener(this)
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/fragments/LinearBarFragment.kt
================================================
package com.gustavoas.noti.fragments

import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.getStatusBarHeight

class LinearBarFragment : BasePreferenceFragment(),
    SharedPreferences.OnSharedPreferenceChangeListener {

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        if (key == "linearProgressBarSize") {
            val size = sharedPreferences?.getInt(key, 15) ?: 15
            sharedPreferences?.edit()
                ?.putBoolean("matchStatusBarHeight", size == getStatusBarHeight(context ?: return))
                ?.apply()
        }
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.linear_bar_preferences, rootKey)

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())

        findPreference<Preference>("statusBarHeightCard")?.summary =
            getString(R.string.prefsStatusBarHeightInfo, getStatusBarHeight(requireContext()))

        if (sharedPreferences.getBoolean("matchStatusBarHeight", false)) {
            sharedPreferences.edit()
                .putInt("linearProgressBarSize", getStatusBarHeight(requireContext())).apply()
        }

        sharedPreferences.registerOnSharedPreferenceChangeListener(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (savedInstanceState == null) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P || !hasDisplayCutout()) {
                findPreference<Preference>("showBelowNotch")?.isVisible = false
            }
        }
    }

    private fun hasDisplayCutout(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            val windowInsets = requireActivity().window?.decorView?.rootWindowInsets
            val displayCutout = windowInsets?.displayCutout
            displayCutout != null
        } else {
            false
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/fragments/PerAppSettingsFragment.kt
================================================
package com.gustavoas.noti.fragments

import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gustavoas.noti.ProgressBarAppsAdapter
import com.gustavoas.noti.ProgressBarAppsRepository
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.getApplicationInfo
import com.gustavoas.noti.Utils.getApplicationName
import com.gustavoas.noti.Utils.getColorForApp
import com.gustavoas.noti.model.ProgressBarApp
import eltos.simpledialogfragment.SimpleDialog
import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_NEUTRAL
import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE
import eltos.simpledialogfragment.color.SimpleColorDialog

class PerAppSettingsFragment : Fragment(), SimpleDialog.OnDialogResultListener {
    private val apps = ArrayList<ProgressBarApp>()
    private val appsRepository by lazy { ProgressBarAppsRepository(requireContext()) }
    private val recyclerView by lazy { requireView().findViewById<RecyclerView>(R.id.apps_recycler_view) }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View = inflater.inflate(R.layout.fragment_per_app_settings, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        updateAppsFromDatabase()

        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.adapter =
            ProgressBarAppsAdapter(this, requireContext(), apps, appsRepository)

        updateRecyclerViewVisibility()
    }

    override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
        val progressBarApp = apps[dialogTag.toInt()].copy()

        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
        val defaultColor = sharedPrefs.getInt(
            "progressBarColor", ContextCompat.getColor(requireContext(), R.color.purple_500)
        )
        val existingSelectedColor = getColorForApp(requireContext(), progressBarApp)
        val useMaterialYou = sharedPrefs.getBoolean("usingMaterialYouColor", false)

        val color = extras.getInt(SimpleColorDialog.COLOR)
        val selectedPosition = extras.getInt(SimpleColorDialog.SELECTED_SINGLE_POSITION)

        if (which == BUTTON_NEUTRAL || (color == defaultColor && (!useMaterialYou || selectedPosition != 19))) {
            progressBarApp.useDefaultColor = true
            progressBarApp.useMaterialYouColor = false
            progressBarApp.color = null
        } else if (which == BUTTON_POSITIVE) {
            if (
                Build.VERSION.SDK_INT >= VERSION_CODES.S && selectedPosition != 19 &&
                color == ContextCompat.getColor(requireContext(), R.color.system_accent_color)
            ) {
                progressBarApp.useDefaultColor = false
                progressBarApp.useMaterialYouColor = true
                progressBarApp.color = null
            } else if (color != existingSelectedColor || (progressBarApp.useMaterialYouColor && selectedPosition == 19)) {
                progressBarApp.useDefaultColor = false
                progressBarApp.useMaterialYouColor = false
                progressBarApp.color = color
            }
        }

        if (apps[dialogTag.toInt()] != progressBarApp) {
            apps[dialogTag.toInt()] = progressBarApp
            appsRepository.updateApp(apps[dialogTag.toInt()])
            recyclerView.adapter?.notifyItemChanged(dialogTag.toInt() + 1)
        }

        return true
    }

    private fun updateAppsFromDatabase() {
        apps.clear()
        apps.addAll(appsRepository.getAll())
        removeUnavailableApps()
        alphabetizeApps()
    }

    private fun removeUnavailableApps() {
        apps.removeAll { app ->
            getApplicationInfo(requireContext(), app.packageName)?.enabled != true
        }
    }

    private fun alphabetizeApps() {
        apps.sortBy { app ->
            (getApplicationName(requireContext(), app.packageName) ?: app.packageName).lowercase()
        }
    }

    private fun updateRecyclerViewVisibility() {
        val emptyRecyclerView = requireView().findViewById<TextView>(R.id.empty_view)
        if (apps.isEmpty()) {
            recyclerView.visibility = View.GONE
            emptyRecyclerView.visibility = View.VISIBLE
        } else {
            recyclerView.visibility = View.VISIBLE
            emptyRecyclerView.visibility = View.GONE
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        appsRepository.close()
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/fragments/SettingsFragment.kt
================================================
package com.gustavoas.noti.fragments

import android.content.ComponentName
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceManager
import androidx.preference.children
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.hasAccessibilityPermission
import com.gustavoas.noti.Utils.hasNotificationListenerPermission
import com.gustavoas.noti.Utils.hasSystemAlertWindowPermission
import com.gustavoas.noti.Utils.showColorDialog
import com.gustavoas.noti.preferences.BannerPreference
import com.gustavoas.noti.services.NotificationListenerService
import com.kizitonwose.colorpreferencecompat.ColorPreferenceCompat
import eltos.simpledialogfragment.SimpleDialog
import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE
import eltos.simpledialogfragment.color.SimpleColorDialog

class SettingsFragment : BasePreferenceFragment(),
    SharedPreferences.OnSharedPreferenceChangeListener, SimpleDialog.OnDialogResultListener {
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        if (key == "progressBarStyle" || key == "progressBarStylePortrait" || key == "progressBarStyleLandscape" || key == "advancedProgressBarStyle") {
            if (key != "advancedProgressBarStyle" && sharedPreferences?.getString(
                    key, "linear"
                ) == "circular" && sharedPreferences.getBoolean("showHolePunchInstruction", true)
            ) {
                Toast.makeText(
                    requireContext(), getString(R.string.holePunchInstruction), Toast.LENGTH_LONG
                ).show()
                sharedPreferences.edit { putBoolean("showHolePunchInstruction", false) }
            }
            updateProgressBarStyle()
        } else if (key == "progressBarColor") {
            updateColorPreferenceSummary()
        } else if (key == "useNotificationColor") {
            updateColorPreferenceState()
        }
    }

    private val myApps = listOf(
        Triple("CalenTile", "https://play.google.com/store/apps/details?id=com.gustavoas.calendarqst", R.drawable.ic_calentile),
        Triple("Sum Up", "https://play.google.com/store/apps/details?id=com.gustavoas.sumup", R.drawable.ic_sumup),
    )

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.preferences, rootKey)

        updateSetupVisibility()

        updateProgressBarStyle()

        updatePermissionDependentPreferences()

        updateColorPreferenceState()

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())

        sharedPreferences.registerOnSharedPreferenceChangeListener(this)

        val appIndex = myApps.indices.random()
        val appPromo = findPreference<Preference>("selfPromo")
        appPromo?.title = getString(R.string.prefsCalentileTitle, myApps[appIndex].first)
        appPromo?.intent = Intent(Intent.ACTION_VIEW, myApps[appIndex].second.toUri())
        appPromo?.icon = ContextCompat.getDrawable(requireContext(), myApps[appIndex].third)

        if (!sharedPreferences.contains("batteryOptimizations")) {
            sharedPreferences.edit {
                putBoolean(
                    "batteryOptimizations",
                    Build.BRAND.lowercase() != "google"
                )
            }
        }

        val batterOptimizationsBanner = findPreference<BannerPreference>("batteryOptimizations")
        if (!sharedPreferences.getBoolean("batteryOptimizations", true)) {
            batterOptimizationsBanner?.isVisible = false
        }

        findPreference<Preference>("accessibilityPermission")?.setOnPreferenceClickListener {
            showAccessibilityDialog()
            true
        }

        findPreference<Preference>("progressBarColor")?.setOnPreferenceClickListener {
            val color = PreferenceManager.getDefaultSharedPreferences(requireContext()).getInt(
                    "progressBarColor", ContextCompat.getColor(requireContext(), R.color.purple_500)
                )
            showColorDialog(this, color, "colorPicker")
            true
        }

        findPreference<Preference>("notificationPermission")?.setOnPreferenceClickListener {
            requestNotificationAccess()
            true
        }
    }

    override fun onStart() {
        super.onStart()

        updateSetupVisibility()

        updateColorPreferenceSummary()

        updatePermissionDependentPreferences()
    }

    override fun onDestroy() {
        super.onDestroy()

        PreferenceManager.getDefaultSharedPreferences(requireContext())
            .unregisterOnSharedPreferenceChangeListener(this)
    }

    override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
        if (which == BUTTON_POSITIVE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
                    putBoolean(
                        "usingMaterialYouColor",
                        extras.getInt(SimpleColorDialog.COLOR) == ContextCompat.getColor(
                            requireContext(), R.color.system_accent_color
                        ) && extras.getInt(SimpleColorDialog.SELECTED_SINGLE_POSITION) != 19
                    )
                }
            }

            findPreference<ColorPreferenceCompat>("progressBarColor")?.value = extras.getInt(
                SimpleColorDialog.COLOR,
                ContextCompat.getColor(requireContext(), R.color.purple_500)
            )
        }
        return true
    }

    private fun updateColorPreferenceState() {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
        val colorPreference = findPreference<ColorPreferenceCompat>("progressBarColor")
        val useNotificationColor = sharedPreferences.getBoolean("useNotificationColor", false)

        colorPreference?.isEnabled = !useNotificationColor
        colorPreference?.icon?.alpha = if (useNotificationColor) 80 else 255
    }

    private fun updateColorPreferenceSummary() {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
        val color = sharedPreferences.getInt(
            "progressBarColor", ContextCompat.getColor(requireContext(), R.color.purple_500)
        )
        val colorPosition = resources.getIntArray(R.array.colorsArrayValues).indexOf(color)
        var colorName = resources.getStringArray(R.array.colorsArray).getOrNull(colorPosition)
        val useMaterialYou = sharedPreferences.getBoolean("usingMaterialYouColor", false)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && colorName == null && useMaterialYou) {
            sharedPreferences.edit {
                putInt(
                    "progressBarColor", ContextCompat.getColor(
                        requireContext(), R.color.system_accent_color
                    )
                )
            }

            findPreference<ColorPreferenceCompat>("progressBarColor")?.value =
                ContextCompat.getColor(requireContext(), R.color.system_accent_color)
            colorName = resources.getString(R.string.colorMaterialYou)
        }

        findPreference<Preference>("progressBarColor")?.summary =
            colorName ?: "#${Integer.toHexString(color).drop(2).uppercase()}"
    }

    private fun updateProgressBarStyle() {
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
        val advancedStyleOptions = sharedPreferences.getBoolean("advancedProgressBarStyle", false)
        val progressBarStyle = sharedPreferences.getString("progressBarStyle", "linear")
        val progressBarStylePortrait =
            sharedPreferences.getString("progressBarStylePortrait", "linear")
        val progressBarStyleLandscape =
            sharedPreferences.getString("progressBarStyleLandscape", "linear")

        val anyLinear =
            (!advancedStyleOptions && progressBarStyle == "linear") || (advancedStyleOptions && (progressBarStylePortrait == "linear" || progressBarStyleLandscape == "linear"))
        val anyCircular =
            (!advancedStyleOptions && progressBarStyle == "circular") || (advancedStyleOptions && (progressBarStylePortrait == "circular" || progressBarStyleLandscape == "circular"))

        findPreference<Preference>("CircularBarFragment")?.isVisible = anyCircular
        findPreference<Preference>("LinearBarFragment")?.isVisible = anyLinear

        findPreference<Preference>("progressBarStyle")?.summary = if (advancedStyleOptions) {
            if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
                resources.getStringArray(R.array.progressBarStyle).getOrNull(
                    resources.getStringArray(R.array.progressBarStyleValues)
                        .indexOf(progressBarStyleLandscape)
                )
            } else {
                resources.getStringArray(R.array.progressBarStyle).getOrNull(
                    resources.getStringArray(R.array.progressBarStyleValues)
                        .indexOf(progressBarStylePortrait)
                )
            }
        } else {
            resources.getStringArray(R.array.progressBarStyle).getOrNull(
                resources.getStringArray(R.array.progressBarStyleValues).indexOf(progressBarStyle)
            )
        }
    }

    private fun updatePermissionDependentPreferences() {
        findPreference<Preference>("showInLockScreen")?.isVisible =
            (hasAccessibilityPermission(requireContext()) || Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
        findPreference<Preference>("disableInFullScreen")?.isVisible =
            hasSystemAlertWindowPermission(requireContext())
    }

    private fun updateSetupVisibility() {
        val hasAccessibilityPermission = hasAccessibilityPermission(requireContext())
        val hasNotificationListenerPermission = hasNotificationListenerPermission(requireContext())
        val hasSystemAlertWindowPermission = hasSystemAlertWindowPermission(requireContext())

        findPreference<Preference>("accessibilityPermission")?.isVisible =
            !hasAccessibilityPermission
        findPreference<Preference>("notificationPermission")?.isVisible =
            !hasNotificationListenerPermission
        findPreference<Preference>("systemAlertWindowPermission")?.isVisible =
            !hasSystemAlertWindowPermission
        findPreference<PreferenceCategory>("setup")?.isVisible = findPreference<PreferenceCategory>("setup")?.children?.any { it.isVisible } == true
    }

    private fun showAccessibilityDialog() {
        AlertDialog.Builder(requireContext())
            .setTitle(R.string.prefsAccessibilityPermissionTitle)
            .setMessage(R.string.prefsAccessibilityPermissionSummary)
            .setPositiveButton(android.R.string.ok) { _, _ ->
                val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
                startActivity(intent)
            }
            .setNegativeButton(android.R.string.cancel, null)
            .show()
    }

    private fun requestNotificationAccess() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS)
            intent.putExtra(
                Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME, ComponentName(
                requireContext(), NotificationListenerService::class.java
            ).flattenToString()
            )
            try {
                startActivity(intent)
            } catch (e: Exception) {
                startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
            }
        } else {
            startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/model/DeviceConfiguration.kt
================================================
package com.gustavoas.noti.model

data class DeviceConfiguration (
    var configuration: String? = null,
    var deviceWidth: String? = null,
    var deviceHeight: String? = null,
    var size: String? = null,
    var marginTop: String? = null,
    var topOffset: String? = null,
    var horizontalOffset: String? = null,
)

================================================
FILE: app/src/main/java/com/gustavoas/noti/model/ProgressBarApp.kt
================================================
package com.gustavoas.noti.model

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class ProgressBarApp(
    val packageName: String,
    var showProgressBar: Boolean = true,
    var color: Int? = null,
    var useDefaultColor: Boolean = true,
    var useMaterialYouColor: Boolean = false
) : Parcelable

================================================
FILE: app/src/main/java/com/gustavoas/noti/model/ProgressNotification.kt
================================================
package com.gustavoas.noti.model

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class ProgressNotification (
    val progressBarApp: ProgressBarApp,
    val progress: Int,
    val priority: Int
) : Parcelable

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/DownloadProgressBar.kt
================================================
package com.gustavoas.noti.notifications

import android.app.Notification
import android.content.Context
import android.service.notification.StatusBarNotification
import androidx.preference.PreferenceManager
import com.gustavoas.noti.ProgressBarAppsRepository

class DownloadProgressBar(
    ctx: Context,
    sbn: StatusBarNotification,
    appsRepository: ProgressBarAppsRepository
): ProgressBarNotification(ctx, sbn, appsRepository) {
    override val priorityLevel: Int = 2

    init {
        updateNotification(sbn)
    }

    override fun updateNotification(sbn: StatusBarNotification) {
        super.updateNotification(sbn)

        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(ctx)
        val enabledForDownloads = sharedPrefs.getBoolean("showForDownloads", true)

        if (!enabledForDownloads) {
            cancel()
            return
        }

        val (progress, progressMax) = getProgressBarValues(sbn)

        if (progress == 0 && progressMax == 0) {
            cancel()
            return
        }

        sendProgressToAccessibilityService(progress, progressMax)
    }

    private fun getProgressBarValues(sbn: StatusBarNotification): Pair<Int, Int> {
        val progress = sbn.notification.extras.getInt(Notification.EXTRA_PROGRESS)
        val progressMax = sbn.notification.extras.getInt(Notification.EXTRA_PROGRESS_MAX)
        return Pair(progress, progressMax)
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/GoogleTimerProgressBar.kt
================================================
package com.gustavoas.noti.notifications

import android.content.Context
import android.service.notification.StatusBarNotification
import com.gustavoas.noti.ProgressBarAppsRepository

class GoogleTimerProgressBar(
    ctx: Context,
    sbn: StatusBarNotification,
    appsRepository: ProgressBarAppsRepository
): TimedProgressBar(ctx, sbn, appsRepository) {
    override val priorityLevel: Int = 1

    init {
        updateNotification(sbn)
    }

    override fun updateNotification(sbn: StatusBarNotification) {
        super.updateNotification(sbn)

        val sortKey = sbn.notification.sortKey

        if (sortKey == null || !sortKey.contains("RUNNING")) {
            cancel()
            return
        }

        val splitKey = sortKey.split("|")
        val timeLeft = splitKey.firstOrNull { it.contains("⏳") } ?: return
        val totalTime = splitKey.firstOrNull { it.contains("Σ") } ?: return
        val timeLeftMillis = parseTimeStringToMillis(timeLeft.substringAfter("⏳"))
        val totalTimeMillis = parseTimeStringToMillis(totalTime.substringAfter("Σ"))

        startUpdatingTimedPosition(timeLeftMillis, totalTimeMillis, -1f)
    }

    private fun parseTimeStringToMillis(time: String): Long {
        val timeSplit = time.trim().split(":")
        val hours = timeSplit.getOrNull(0)?.toLongOrNull() ?: 0
        val minutes = timeSplit.getOrNull(1)?.toLongOrNull() ?: 0
        val seconds = timeSplit.getOrNull(2)?.toLongOrNull() ?: 0
        return (hours * 3600 + minutes * 60 + seconds) * 1000
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/MediaProgressBar.kt
================================================
package com.gustavoas.noti.notifications

import android.content.Context
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.service.notification.StatusBarNotification
import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager
import com.gustavoas.noti.ProgressBarAppsRepository

class MediaProgressBar(
    ctx: Context,
    sbn: StatusBarNotification,
    appsRepository: ProgressBarAppsRepository
): TimedProgressBar(ctx, sbn, appsRepository) {
    override val priorityLevel: Int = 0

    private var mediaController: MediaController? = null
    private var mediaCallback: MediaController.Callback? = null

    init {
        updateNotification(sbn)
    }

    override fun updateNotification(sbn: StatusBarNotification) {
        super.updateNotification(sbn)

        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(ctx)
        val enabledForMedia = sharedPrefs.getBoolean("showForMedia", true)

        if (!enabledForMedia) {
            cancel()
            return
        }

        val mediaSession =
            sbn.notification.extras.get(NotificationCompat.EXTRA_MEDIA_SESSION) as? MediaSession.Token

        if (mediaSession == null) {
            cancel()
            return
        }

        val newMediaController = MediaController(ctx, mediaSession)
        if (mediaController == null) {
            createProgressBarFromMedia(newMediaController)
        }
    }

    private fun createProgressBarFromMedia(newMediaController: MediaController) {
        mediaController = newMediaController

        if (mediaController?.playbackState?.state == PlaybackState.STATE_PLAYING) {
            startTrackingMediaPosition()
        }

        mediaCallback = object : MediaController.Callback() {
            override fun onPlaybackStateChanged(state: PlaybackState?) {
                super.onPlaybackStateChanged(state)

                when (state?.state) {
                    PlaybackState.STATE_PLAYING -> {
                        startTrackingMediaPosition()
                    }
                    PlaybackState.STATE_NONE, PlaybackState.STATE_STOPPED,
                    PlaybackState.STATE_PAUSED, PlaybackState.STATE_ERROR -> {
                        cancel()
                    }
                    else -> {
                        stopUpdatingTimedPosition()
                    }
                }
            }
        }

        mediaController?.registerCallback(mediaCallback as MediaController.Callback)
    }

    private fun startTrackingMediaPosition() {
        val artwork = mediaController?.metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
        artwork?.let {
            getColorFromBitmap(it)?.let { color ->
                notificationColor = color
            }
        }

        startUpdatingTimedPosition(
            mediaController?.playbackState?.position ?: 0,
            mediaController?.metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0,
            mediaController?.playbackState?.playbackSpeed ?: 1f
        )
    }

    override fun cancel() {
        super.cancel()

        if (mediaCallback != null) {
            mediaController?.unregisterCallback(mediaCallback as MediaController.Callback)
            mediaCallback = null
        }
        mediaController = null
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/PercentageProgressBar.kt
================================================
package com.gustavoas.noti.notifications

import android.app.Notification
import android.content.Context
import android.service.notification.StatusBarNotification
import androidx.preference.PreferenceManager
import com.gustavoas.noti.ProgressBarAppsRepository
import kotlin.math.roundToInt

class PercentageProgressBar(
    ctx: Context,
    sbn: StatusBarNotification,
    appsRepository: ProgressBarAppsRepository
): ProgressBarNotification(ctx, sbn, appsRepository) {
    override val priorityLevel: Int = 2

    private var initialValue: Int? = null

    init {
        updateNotification(sbn)
    }

    override fun updateNotification(sbn: StatusBarNotification) {
        super.updateNotification(sbn)

        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(ctx)
        val enabledForDownloads = sharedPrefs.getBoolean("showForDownloads", true)

        if (!enabledForDownloads) {
            cancel()
            return
        }

        val percentageProgress = getProgressFromPercentage(sbn)

        if (percentageProgress == 0) {
            cancel()
            return
        }

        if (initialValue != null && initialValue != percentageProgress) {
            sendProgressToAccessibilityService(percentageProgress, 100)
        }

        if (initialValue == null) {
            initialValue = percentageProgress
        }
    }

    private fun getProgressFromPercentage(sbn: StatusBarNotification): Int {
        val extras = sbn.notification.extras
        val title = extras.getCharSequence(Notification.EXTRA_TITLE)?.toString() ?: ""
        val text = extras.getCharSequence(Notification.EXTRA_TEXT)?.toString() ?: ""
        val subText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT)?.toString() ?: ""
        val bigText = extras.getCharSequence(Notification.EXTRA_BIG_TEXT)?.toString() ?: ""
        val textLines = extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES)

        val percentageProgress = title.substringBefore("%").toFloatOrNull() ?:
        text.substringBefore("%").toFloatOrNull() ?:
        subText.substringBefore("%").toFloatOrNull() ?:
        bigText.split("\n").firstOrNull { it.contains("%") }?.toString()
            ?.substringBefore("%")?.toFloatOrNull() ?:
        textLines?.firstOrNull { it.contains("%") }?.toString()
            ?.substringBefore("%")?.toFloatOrNull()

        if (percentageProgress == null || percentageProgress.isNaN()) {
            return 0
        }

        return percentageProgress.roundToInt()
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/ProgressBarNotification.kt
================================================
package com.gustavoas.noti.notifications

import android.app.Notification
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Looper
import android.service.notification.StatusBarNotification
import androidx.core.graphics.createBitmap
import androidx.palette.graphics.Palette
import androidx.preference.PreferenceManager
import com.gustavoas.noti.ProgressBarAppsRepository
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.getApplicationIcon
import com.gustavoas.noti.model.ProgressBarApp
import com.gustavoas.noti.model.ProgressNotification
import com.gustavoas.noti.services.AccessibilityService
import kotlin.math.roundToInt

abstract class ProgressBarNotification(
    protected val ctx: Context,
    private var sbn: StatusBarNotification,
    private val appsRepository: ProgressBarAppsRepository
) {
    protected abstract val priorityLevel: Int

    protected var notificationColor = sbn.notification.color

    private val removalHandler = Handler(Looper.getMainLooper())

    open fun updateNotification(sbn: StatusBarNotification) {
        this.sbn = sbn
    }

    open fun cancel() {
        removalHandler.removeCallbacksAndMessages(null)
        sendRemovalRequestToAccessibilityService()
    }

    protected fun sendProgressToAccessibilityService(
        progress: Int = 0,
        progressMax: Int = 0
    ) {
        if (progressMax <= 0 || progress !in 0..progressMax) {
            return
        }

        val appInDatabase = getOrCreateAppInDatabase()
        updateProgressBarColor(appInDatabase)
        if (!appInDatabase.showProgressBar) {
            cancel()
            return
        }

        val progressBarMax = ctx.resources.getInteger(R.integer.progress_bar_max)
        val progressNormalized = if (progress == 0) {
            0
        } else {
            (progress.toFloat() / progressMax.toFloat() * progressBarMax).roundToInt()
        }

        val intent = Intent(ctx, AccessibilityService::class.java)
        intent.putExtra("id", sbn.key ?: "")
        intent.putExtra(
            "progressNotification",
            ProgressNotification(
                appInDatabase,
                progressNormalized,
                priorityLevel
            )
        )
        ctx.startService(intent)

        removalHandler.removeCallbacksAndMessages(null)

        removalHandler.postDelayed({
            sendRemovalRequestToAccessibilityService()
        }, 10000)
    }

    private fun sendRemovalRequestToAccessibilityService() {
        val intent = Intent(ctx, AccessibilityService::class.java)
        intent.putExtra("id", sbn.key ?: "")
        intent.putExtra("removal", true)
        ctx.startService(intent)
    }

    private fun updateProgressBarColor(progressBarApp: ProgressBarApp) {
        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(ctx)
        val useNotificationColor = sharedPrefs.getBoolean("useNotificationColor", false)

        if (!progressBarApp.useDefaultColor || !useNotificationColor) {
            return
        }

        if (notificationColor == Notification.COLOR_DEFAULT) {
            val appIcon = getApplicationIcon(ctx, sbn.packageName)

            appIcon?.let {
                getColorFromBitmap(drawableToBitmap(it))?.let { color ->
                    notificationColor = color
                }
            }
        }

        if (notificationColor != Notification.COLOR_DEFAULT && progressBarApp.color != notificationColor) {
            progressBarApp.color = notificationColor
            appsRepository.updateApp(progressBarApp)
        }
    }

    protected fun getColorFromBitmap(bitmap: Bitmap): Int? {
        val palette = Palette.from(bitmap).generate()
        val swatch = palette.lightMutedSwatch
            ?: palette.vibrantSwatch
            ?: palette.dominantSwatch

        return swatch?.rgb
    }

    private fun drawableToBitmap(drawable: Drawable): Bitmap {
        if (drawable is BitmapDrawable) return drawable.bitmap

        val width = drawable.intrinsicWidth.takeIf { it > 0 } ?: 1
        val height = drawable.intrinsicHeight.takeIf { it > 0 } ?: 1
        val bitmap = createBitmap(width, height)
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)

        return bitmap
    }

    private fun getOrCreateAppInDatabase(): ProgressBarApp {
        return appsRepository.let {
            it.getApp(sbn.packageName ?: "") ?: it.addApp(ProgressBarApp(sbn.packageName, true))
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/notifications/TimedProgressBar.kt
================================================
package com.gustavoas.noti.notifications

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.service.notification.StatusBarNotification
import com.gustavoas.noti.ProgressBarAppsRepository
import kotlin.math.abs

abstract class TimedProgressBar(
    ctx: Context,
    sbn: StatusBarNotification,
    appsRepository: ProgressBarAppsRepository
): ProgressBarNotification(ctx, sbn, appsRepository) {
    private val updateInterval = 1000

    private val handler = Handler(Looper.getMainLooper())

    private var updatesRunnable: Runnable? = null

    protected fun startUpdatingTimedPosition(
        initialPosition: Long,
        duration: Long,
        speed: Float
    ) {
        stopUpdatingTimedPosition()

        val initialTime = System.currentTimeMillis()
        updatesRunnable = object : Runnable {
            override fun run() {
                val currTime = System.currentTimeMillis()
                var currProgress = (initialPosition + (currTime - initialTime) * speed).toLong()

                if (currProgress !in 0..duration) {
                    return
                }

                val updateStep = abs(updateInterval * speed).toInt()
                if (duration - currProgress < updateStep) {
                    currProgress = duration
                } else if (currProgress < updateStep) {
                    currProgress = 0
                }

                sendProgressToAccessibilityService(
                    currProgress.toInt(),
                    duration.toInt(),
                )

                updatesRunnable?.let { handler.postDelayed(it, updateInterval.toLong()) }
            }
        }

        updatesRunnable?.let { handler.post(it) }
    }

    protected fun stopUpdatingTimedPosition() {
        handler.removeCallbacksAndMessages(null)
        updatesRunnable = null
    }

    override fun cancel() {
        super.cancel()

        stopUpdatingTimedPosition()
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/preferences/BannerPreference.kt
================================================
package com.gustavoas.noti.preferences

import android.content.Context
import android.util.AttributeSet
import android.widget.Button
import android.widget.ImageButton
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.gustavoas.noti.R

class BannerPreference(context: Context, attrs: AttributeSet): Preference(context, attrs) {

    var onBtnClick = { intent?.let { context.startActivity(intent) } }

    init {
        layoutResource = R.layout.banner_preference
        isSelectable = false
    }

    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)

        val persistedValue = getPersistedBoolean(true)
        val preferenceHolder = holder.itemView

        if (!persistedValue) {
            this.isVisible = false
            return
        }

        val btn = preferenceHolder.findViewById<Button>(R.id.bannerBtn)
        btn.text = summary

        btn.setOnClickListener {
            onBtnClick()
        }

        val closeBtn = preferenceHolder.findViewById<ImageButton>(R.id.closeBtn)

        closeBtn.setOnClickListener {
            this.isVisible = false
            persistBoolean(false)
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/preferences/BarStylesListPreference.kt
================================================
package com.gustavoas.noti.preferences

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AlertDialog
import androidx.preference.ListPreference
import com.gustavoas.noti.R

class BarStylesListPreference(context: Context, attrs: AttributeSet?) : ListPreference(context, attrs) {

    override fun onClick() {
        val sharedPreferences = preferenceManager.sharedPreferences ?: return

        if (!sharedPreferences.getBoolean("advancedProgressBarStyle", false)) {
            AlertDialog.Builder(context)
                .setSingleChoiceItems(entries, getValueIndex()) { dialog, index ->
                    if (callChangeListener(entryValues[index].toString())) {
                        setValueIndex(index)
                    }
                    dialog.dismiss()
                }.setNeutralButton(context.resources.getString(R.string.advancedOptions)) { _, _ ->
                    showAdvancedDialog()
                }.setTitle(title).show()

        } else {
            showAdvancedDialog()
        }
    }

    private fun showAdvancedDialog() {
        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val dialogView = layoutInflater.inflate(R.layout.advanced_style_dialog, null)
        val portraitStyleListview = dialogView.findViewById<ListView>(R.id.portraitStyleOptions)
        val landscapeStyleListview = dialogView.findViewById<ListView>(R.id.landscapeStyleOptions)

        val options = context.resources.getStringArray(R.array.progressBarStyle)
        val adapter = ArrayAdapter(context, androidx.appcompat.R.layout.select_dialog_singlechoice_material, options)
        portraitStyleListview.adapter = adapter
        landscapeStyleListview.adapter = adapter

        var viewHeight = 0

        for (i in 0 until adapter.count) {
            val view = adapter.getView(i, null, portraitStyleListview)
            view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
            viewHeight += view.measuredHeight
        }

        portraitStyleListview.layoutParams.height = viewHeight
        landscapeStyleListview.layoutParams.height = viewHeight

        val sharedPreferences = preferenceManager.sharedPreferences ?: return

        if (!sharedPreferences.contains("progressBarStylePortrait") && !sharedPreferences.contains("progressBarStyleLandscape")) {
            sharedPreferences.edit()
                .putString(
                    "progressBarStylePortrait",
                    sharedPreferences.getString("progressBarStyle", "linear")
                )
                .putString(
                    "progressBarStyleLandscape",
                    sharedPreferences.getString("progressBarStyle", "linear")
                )
                .apply()
        }

        portraitStyleListview.setItemChecked(
            context.resources.getStringArray(R.array.progressBarStyleValues).indexOf(
                sharedPreferences.getString("progressBarStylePortrait", "linear")
            ), true
        )

        landscapeStyleListview.setItemChecked(
            context.resources.getStringArray(R.array.progressBarStyleValues).indexOf(
                sharedPreferences.getString("progressBarStyleLandscape", "linear")
            ), true
        )

        AlertDialog.Builder(context)
            .setView(dialogView)
            .setPositiveButton(android.R.string.ok) { _, _ ->
                sharedPreferences.edit()?.putBoolean("advancedProgressBarStyle", true)?.apply()
                sharedPreferences.edit()
                    .putString(
                        "progressBarStylePortrait",
                        context.resources.getStringArray(R.array.progressBarStyleValues)[portraitStyleListview.checkedItemPosition]
                    )
                    .putString(
                        "progressBarStyleLandscape",
                        context.resources.getStringArray(R.array.progressBarStyleValues)[landscapeStyleListview.checkedItemPosition]
                    )
                    .apply()
            }
            .setNegativeButton(android.R.string.cancel, null)
            .setNeutralButton(context.resources.getString(R.string.reset)) { _, _ ->
                sharedPreferences.edit()?.putBoolean("advancedProgressBarStyle", false)?.apply()
            }
            .show()
    }

    private fun getValueIndex() = entryValues.indexOf(value)
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/preferences/SeekBarPreference.kt
================================================
package com.gustavoas.noti.preferences

import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.google.android.material.slider.Slider
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.vibrate

class SeekBarPreference(context: Context, attrs: AttributeSet): Preference(context, attrs) {
    private var preferenceHolder: View? = null

    private var minNumber: Int = -50
    private var maxNumber: Int = 50
    private var defaultValue: Int = 0

    private var currentValue: Int = defaultValue

    init {
        layoutResource = R.layout.seekbar_preference

        val attributes = context.obtainStyledAttributes(attrs, R.styleable.HorizontalNumberPicker)
        minNumber = attributes.getInteger(R.styleable.HorizontalNumberPicker_min_number, minNumber)
        maxNumber = attributes.getInteger(R.styleable.HorizontalNumberPicker_max_number, maxNumber)
        defaultValue = attributes.getInteger(R.styleable.HorizontalNumberPicker_default_value, defaultValue)
        attributes.recycle()
    }

    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)

        preferenceHolder = holder.itemView
        val seekBar = preferenceHolder?.findViewById<Slider>(R.id.slider)

        currentValue = getPersistedInt(defaultValue)
        seekBar?.value = currentValue.toFloat()
        seekBar?.valueFrom = minNumber.toFloat()
        seekBar?.valueTo = maxNumber.toFloat()

        seekBar?.addOnChangeListener { _, value, _ ->
            if (value.toInt() != currentValue) {
                currentValue = value.toInt()
                persistInt(currentValue)
                vibrate(context)
            }
        }
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/services/AccessibilityService.kt
================================================
package com.gustavoas.noti.services

import android.accessibilityservice.AccessibilityService
import android.animation.ObjectAnimator
import android.app.KeyguardManager
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.PixelFormat
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Handler
import android.os.Looper
import android.view.Display
import android.view.Gravity
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
import android.view.accessibility.AccessibilityEvent
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import androidx.core.content.ContextCompat
import androidx.core.hardware.display.DisplayManagerCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.getColorForApp
import com.gustavoas.noti.Utils.getScreenLargeSide
import com.gustavoas.noti.Utils.getScreenSmallSide
import com.gustavoas.noti.Utils.getStatusBarHeight
import com.gustavoas.noti.Utils.hasAccessibilityPermission
import com.gustavoas.noti.Utils.hasSystemAlertWindowPermission
import com.gustavoas.noti.model.ProgressBarApp
import com.gustavoas.noti.model.ProgressNotification
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.sqrt

class AccessibilityService : AccessibilityService() {
    private val windowManager by lazy { getSystemService(WINDOW_SERVICE) as WindowManager }
    private val displayManager by lazy { DisplayManagerCompat.getInstance(this) }
    private val keyguardManager by lazy { getSystemService(KEYGUARD_SERVICE) as KeyguardManager }

    private lateinit var overlayView: View
    private lateinit var progressBar: LinearProgressIndicator
    private lateinit var circularProgressBar: CircularProgressIndicator
    private val handler = Handler(Looper.getMainLooper())

    private val activeProgressBars = mutableMapOf<String, ProgressNotification>()

    private val fullscreenDetectionService by lazy {
        Intent(this, FullscreenDetectionService::class.java)
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}

    override fun onInterrupt() {}

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (!hasAccessibilityPermission(this) && !hasSystemAlertWindowPermission(this)) {
            return super.onStartCommand(intent, flags, startId)
        }

        val id = intent?.getStringExtra("id") ?: ""
        val removal = intent?.getBooleanExtra("removal", false) ?: false

        if (removal) {
            activeProgressBars.remove(id)
        } else {
            val newProgressNotification = if (SDK_INT > Build.VERSION_CODES.TIRAMISU) {
                intent?.getParcelableExtra("progressNotification", ProgressNotification::class.java)
            } else {
                intent?.getParcelableExtra("progressNotification")
            }

            newProgressNotification?.let {
                activeProgressBars[id] = it
            }
        }

        updateProgressOverlay(1000)

        return super.onStartCommand(intent, flags, startId)
    }

    private fun updateProgressOverlay(hideDelay: Long = 0) {
        val showInLockScreen = PreferenceManager.getDefaultSharedPreferences(this)
            .getBoolean("showInLockScreen", true)

        if (activeProgressBars.isEmpty() || (isLocked() && !showInLockScreen)) {
            if (!this::overlayView.isInitialized || !overlayView.isShown) {
                return
            }
            handler.postDelayed({
                hideProgressBarIn(hideDelay / 2)
            }, hideDelay)
        } else {
            getActiveNotification()?.let {
                showOverlayWithProgress(it)
            }
        }
    }

    private fun getActiveNotification(): ProgressNotification? {
        val highestPriority = activeProgressBars.values.maxOfOrNull { it.priority } ?: 0
        return activeProgressBars.values
            .filter { it.priority >= highestPriority }
            .maxByOrNull { it.progress }
    }

    private fun showOverlayWithProgress(notification: ProgressNotification) {
        handler.removeCallbacksAndMessages(null)

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

        val progressBarStyle =
            if (sharedPreferences.getBoolean(getSizeDependentPreferenceKey("advancedProgressBarStyle").first, false)) {
                sharedPreferences.getString(
                    if (isInPortraitMode()) {
                        getSizeDependentPreferenceKey("progressBarStylePortrait").first
                    } else {
                        getSizeDependentPreferenceKey("progressBarStyleLandscape").first
                    }, "linear"
                )
            } else {
                sharedPreferences.getString(getSizeDependentPreferenceKey("progressBarStyle").first, "linear")
            }

        if(progressBarStyle == "none") {
            if (this::overlayView.isInitialized && overlayView.isShown) {
                hideProgressBarIn()
            }
            return
        }

        if (!this::overlayView.isInitialized || !overlayView.isShown) {
            inflateOverlay()
        }

        progressBar = overlayView.findViewById(R.id.progressBar)
        circularProgressBar = overlayView.findViewById(R.id.circularProgressBar)


        if (progressBarStyle == "circular") {
            progressBar.visibility = View.GONE
            circularProgressBar.visibility = View.VISIBLE

            circularProgressBarCustomizations(sharedPreferences)
        } else {
            progressBar.visibility = View.VISIBLE
            circularProgressBar.visibility = View.GONE

            linearProgressBarCustomizations(sharedPreferences)
        }

        applyCommonProgressBarCustomizations(sharedPreferences, notification.progressBarApp)

        animateProgressBarTo(notification.progress, progressBarStyle == "circular")

        // TODO: record timestamp and deprioritize after a while
//        handler.postDelayed({
//            updateProgressOverlay()
//        }, 3000)
    }

    private fun circularProgressBarCustomizations(sharedPrefs: SharedPreferences) {
        val trackThickness = max(sharedPrefs.getSizeDependentInt("circularProgressBarThickness", 15), 0)
        val cutoutSize = max(sharedPrefs.getSizeDependentInt("circularProgressBarSize", 65), 0)

        circularProgressBar.indicatorSize = cutoutSize + 2 * trackThickness

        circularProgressBar.trackThickness = trackThickness

        val marginTop = sharedPrefs.getSizeDependentInt("circularProgressBarTopOffset", 60) - trackThickness - cutoutSize / 2
        val horizontalOffset = sharedPrefs.getSizeDependentInt("circularProgressBarHorizontalOffset", 0)

        val overlayParams = overlayView.layoutParams as WindowManager.LayoutParams

        overlayParams.width = circularProgressBar.indicatorSize
        overlayParams.height = circularProgressBar.indicatorSize

        val displayRotation = displayManager.getDisplay(Display.DEFAULT_DISPLAY)!!.rotation

        circularProgressBar.rotation = (360 - displayRotation.toFloat() * 90) % 360

        val progressParams = circularProgressBar.layoutParams as FrameLayout.LayoutParams

        var paddingTop = 0
        var paddingRight = 0
        var paddingLeft = 0

        if (marginTop < 0) {
            paddingTop = marginTop
        } else if (marginTop + circularProgressBar.indicatorSize > getScreenLargeSide(this))  {
            paddingTop = marginTop + circularProgressBar.indicatorSize - getScreenLargeSide(this)
        }

        val halfWidth = getScreenSmallSide(this) / 2
        val halfIndicatorSize = circularProgressBar.indicatorSize / 2

        if (abs(horizontalOffset) + halfIndicatorSize > halfWidth) {
            if (horizontalOffset - halfIndicatorSize < -halfWidth) {
                paddingLeft = halfWidth + horizontalOffset - halfIndicatorSize
            } else {
                paddingLeft = -halfWidth + horizontalOffset + halfIndicatorSize
            }

            if (horizontalOffset + halfIndicatorSize > halfWidth) {
                paddingRight = halfWidth - horizontalOffset - halfIndicatorSize
            } else {
                paddingRight = -halfWidth - horizontalOffset + halfIndicatorSize
            }
        }

        when(displayRotation) {
            Surface.ROTATION_0 -> {
                overlayParams.gravity = Gravity.CENTER_HORIZONTAL or Gravity.TOP
                overlayParams.x = horizontalOffset
                overlayParams.y = marginTop
                progressParams.setMargins(paddingLeft, paddingTop, -paddingLeft, -paddingTop)
            }
            Surface.ROTATION_180 -> {
                overlayParams.gravity = Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
                overlayParams.x = -horizontalOffset
                overlayParams.y = marginTop
                progressParams.setMargins(paddingRight, -paddingTop, -paddingRight, paddingTop)
            }
            Surface.ROTATION_90 -> {
                // landscape, device top is on the left
                overlayParams.gravity = Gravity.CENTER_VERTICAL or Gravity.LEFT
                overlayParams.x = marginTop
                overlayParams.y = -horizontalOffset
                progressParams.setMargins(paddingTop, paddingRight, -paddingTop, -paddingRight)
            }
            Surface.ROTATION_270 -> {
                // landscape, device top is on the right
                overlayParams.gravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT
                overlayParams.x = marginTop
                overlayParams.y = horizontalOffset
                progressParams.setMargins(-paddingTop, paddingLeft, paddingTop, -paddingLeft)
            }
        }

        if (SDK_INT >= Build.VERSION_CODES.P) {
            overlayParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
        }

        circularProgressBar.layoutParams = progressParams

        try {
            windowManager.updateViewLayout(overlayView, overlayParams)
        } catch (_: Exception) {}
    }

    private fun linearProgressBarCustomizations(sharedPrefs: SharedPreferences) {
        if (sharedPrefs.getBoolean(getSizeDependentPreferenceKey("matchStatusBarHeight").first, false)) {
            progressBar.trackThickness = getStatusBarHeight(this)
        } else {
            progressBar.trackThickness = sharedPrefs.getSizeDependentInt("linearProgressBarSize", 15)
        }

        val paddingTop = sharedPrefs.getSizeDependentInt("linearProgressBarMarginTop", 0)

        val overlayParams = overlayView.layoutParams as WindowManager.LayoutParams

        if (SDK_INT >= Build.VERSION_CODES.P) {
            if (sharedPrefs.getBoolean(getSizeDependentPreferenceKey("showBelowNotch").first, false)) {
                overlayParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
            } else {
                overlayParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
            }
        }

        overlayParams.gravity = Gravity.TOP
        overlayParams.width = ViewGroup.LayoutParams.MATCH_PARENT
        overlayParams.height = progressBar.trackThickness + paddingTop
        overlayParams.y = paddingTop

        try {
            windowManager.updateViewLayout(overlayView, overlayParams)
        } catch (_: Exception) {}
    }

    private fun applyCommonProgressBarCustomizations(sharedPrefs: SharedPreferences, progressBarApp: ProgressBarApp) {
        val progressBarColor = getColorForApp(this, progressBarApp)
        progressBar.setIndicatorColor(progressBarColor)
        circularProgressBar.setIndicatorColor(progressBarColor)

        val blackBackground = sharedPrefs.getBoolean("blackBackground", false)
        val backgroundColor = if (blackBackground) {
            ContextCompat.getColor(this, android.R.color.black)
        } else {
            ContextCompat.getColor(this, android.R.color.transparent)
        }
        progressBar.trackColor = backgroundColor
        circularProgressBar.trackColor = backgroundColor

        val useRoundedCorners = sharedPrefs.getBoolean("useRoundedCorners", false)
        if (useRoundedCorners) {
            progressBar.trackCornerRadius = 100
            circularProgressBar.trackCornerRadius = 100
        } else {
            progressBar.trackCornerRadius = 0
            circularProgressBar.trackCornerRadius = 0
        }
    }

    private fun SharedPreferences.getSizeDependentInt(key: String, defaultValue: Int): Int {
        val sizeDependentPreference = getSizeDependentPreferenceKey(key)
        return this.getInt(sizeDependentPreference.first, defaultValue).times(sizeDependentPreference.second).roundToInt()
    }

    private fun getSizeDependentPreferenceKey(key: String): Pair<String, Float> {
        val width = getScreenSmallSide(this)
        val height = getScreenLargeSide(this)

        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)

        val relatedPrefs = sharedPreferences.all.filterKeys { it.startsWith(key) }

        val sameRatio = relatedPrefs.filterKeys { it.length > key.length && it[key.length].isDigit() && it.drop(key.length).split("x")[0].toInt() / it.drop(key.length).split("x")[1].toInt() == height / width }

        if (sameRatio.isEmpty()) {
            return Pair(key, 1f)
        }

        val closest = sameRatio.keys.minByOrNull { abs(it.drop(key.length).split("x")[0].toInt() * it.drop(key.length).split("x")[1].toInt() - height * width) }

        val reductionRatio = sqrt((height * width).div(closest!!.drop(key.length).split("x")[0].toFloat() * closest.drop(key.length).split("x")[1].toFloat()))

        return Pair(closest, reductionRatio)
    }

    private fun isInPortraitMode(): Boolean {
        return resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
    }

    private fun isLocked(): Boolean {
        return keyguardManager.isKeyguardLocked
    }

    private fun setProgressToZero() {
        progressBar.progress = 0
        circularProgressBar.progress = 0
    }

    private fun hideProgressBarIn(delay: Long = 500) {
        progressBar.hide()
        circularProgressBar.hide()
        handler.postDelayed({
            setProgressToZero()
            hideOverlay()
        }, delay)
    }

    private fun animateProgressBarTo(progress: Int, animateCircularProgressBar: Boolean = false) {
        if (animateCircularProgressBar) {
            circularProgressBar.show()
            val circularProgressAnimation =
                ObjectAnimator.ofInt(circularProgressBar, "progress", progress)
            circularProgressAnimation.duration = 250
            circularProgressAnimation.interpolator = DecelerateInterpolator()
            circularProgressAnimation.start()
            progressBar.progress = progress
        } else {
            progressBar.show()
            val progressAnimation = ObjectAnimator.ofInt(progressBar, "progress", progress)
            progressAnimation.duration = 250
            progressAnimation.interpolator = DecelerateInterpolator()
            progressAnimation.start()
            circularProgressBar.progress = progress
        }
    }

    private fun inflateOverlay() {
        if (!this::overlayView.isInitialized) {
            overlayView = View.inflate(this, R.layout.progress_bar, null)
        }

        val hasSAWPermission = hasSystemAlertWindowPermission(this)
        val hasAccessibilityPermission = hasAccessibilityPermission(this)

        val showInLockscreen = PreferenceManager.getDefaultSharedPreferences(this)
            .getBoolean("showInLockScreen", true) && hasAccessibilityPermission

        val windowType = if (SDK_INT >= Build.VERSION_CODES.O && hasSAWPermission && !showInLockscreen) {
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else if (SDK_INT >= Build.VERSION_CODES.M && hasAccessibilityPermission) {
            WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
        } else if (SDK_INT < Build.VERSION_CODES.O && hasSAWPermission) {
            @Suppress("DEPRECATION")
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
        } else {
            return
        }

        val params = WindowManager.LayoutParams (
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            windowType,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION or
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
            PixelFormat.TRANSLUCENT
        )

        if (SDK_INT >= Build.VERSION_CODES.S && hasSAWPermission && !showInLockscreen) {
            params.alpha = 0.8f
        }

        if (!overlayView.isShown) {
            try {
                windowManager.addView(overlayView, params)
            } catch (_: Exception) {
                // TODO
                return
            }

            if (hasSAWPermission) {
                overlayView.alpha = 0f
                LocalBroadcastManager.getInstance(this).registerReceiver(
                    fullscreenDetectionReceiver,
                    IntentFilter(FullscreenDetectionService::class.java.simpleName)
                )
                startService(fullscreenDetectionService)
            }
        }
    }

    private val fullscreenDetectionReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: android.content.Context?, intent: Intent?) {
            val disableInFullscreen = PreferenceManager.getDefaultSharedPreferences(this@AccessibilityService)
                .getBoolean("disableInFullScreen", true)
            val isFullscreen = intent?.getBooleanExtra("isFullscreen", false) ?: false
            val alpha = if (disableInFullscreen && isFullscreen) 0f else 1f

            overlayView.animate()
                .alpha(alpha)
                .setDuration(300)
                .start()
        }
    }

    private fun hideOverlay() {
        if (this::overlayView.isInitialized && overlayView.isShown) {
            windowManager.removeView(overlayView)
        }
        stopService(fullscreenDetectionService)
        LocalBroadcastManager.getInstance(this).unregisterReceiver(fullscreenDetectionReceiver)
        stopSelf()
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)

        updateProgressOverlay(0)
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/services/FullscreenDetectionService.kt
================================================
package com.gustavoas.noti.services

import android.app.KeyguardManager
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.PixelFormat
import android.os.Build
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowManager
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.gustavoas.noti.R
import com.gustavoas.noti.Utils.getRealDisplayHeight
import com.gustavoas.noti.Utils.hasSystemAlertWindowPermission

class FullscreenDetectionService : Service() {
    private val windowManager by lazy { getSystemService(WINDOW_SERVICE) as WindowManager }
    private lateinit var fullscreenDetectionView: View
    private lateinit var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener

    private var displayOffReceiver: BroadcastReceiver? = null

    override fun onBind(intent: Intent?) = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (!hasSystemAlertWindowPermission(this)) {
            return super.onStartCommand(intent, flags, startId)
        }

        if (!this::fullscreenDetectionView.isInitialized || !fullscreenDetectionView.isShown) {
            inflateFullscreenDetectionOverlay()
        }

        registerDisplayOffReceiver()

        return super.onStartCommand(intent, flags, startId)
    }

    private fun inflateFullscreenDetectionOverlay() {
        if (!this::fullscreenDetectionView.isInitialized) {
            fullscreenDetectionView = View.inflate(this, R.layout.progress_bar, null)
        }

        val windowType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            @Suppress("DEPRECATION")
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
        }

        val fullscreenDetectorParams = WindowManager.LayoutParams(
            0,
            WindowManager.LayoutParams.MATCH_PARENT,
            windowType,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
            PixelFormat.TRANSLUCENT
        )

        fullscreenDetectorParams.alpha = 0f
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            fullscreenDetectorParams.layoutInDisplayCutoutMode =
                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
        }

        windowManager.addView(fullscreenDetectionView, fullscreenDetectorParams)

        globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
            val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
            val isKeyguardShown = keyguardManager.isKeyguardLocked

            broadcastFullscreenState(
                if (!isKeyguardShown) {
                    isFullScreen()
                } else {
                    false
                }
            )
        }

        fullscreenDetectionView.viewTreeObserver.addOnGlobalLayoutListener(
            globalLayoutListener
        )
    }

    private fun isFullScreen(): Boolean {
        val displayHeight = getRealDisplayHeight(this)

        return fullscreenDetectionView.height == displayHeight
    }

    private fun broadcastFullscreenState(isFullscreen: Boolean) {
        val intent = Intent(this.javaClass.simpleName)
        intent.putExtra("isFullscreen", isFullscreen)
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    }

    private fun registerDisplayOffReceiver() {
        if (displayOffReceiver != null) {
            return
        }

        displayOffReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == Intent.ACTION_SCREEN_OFF) {
                    broadcastFullscreenState(false)
                }
            }
        }

        registerReceiver(displayOffReceiver, IntentFilter(Intent.ACTION_SCREEN_OFF))
    }

    private fun unregisterDisplayOffReceiver() {
        displayOffReceiver?.let {
            try {
                unregisterReceiver(it)
            } catch (_: IllegalArgumentException) {}
            displayOffReceiver = null
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        if (this::fullscreenDetectionView.isInitialized && fullscreenDetectionView.isShown) {
            windowManager.removeView(fullscreenDetectionView)
            if (fullscreenDetectionView.viewTreeObserver.isAlive) {
                fullscreenDetectionView.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
            }
        }

        unregisterDisplayOffReceiver()
    }
}

================================================
FILE: app/src/main/java/com/gustavoas/noti/services/NotificationListenerService.kt
================================================
package com.gustavoas.noti.services

import android.app.Notification
import android.os.Process
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager
import com.gustavoas.noti.ProgressBarAppsRepository
import com.gustavoas.noti.notifications.DownloadProgressBar
import com.gustavoas.noti.notifications.GoogleTimerProgressBar
import com.gustavoas.noti.notifications.MediaProgressBar
import com.gustavoas.noti.notifications.PercentageProgressBar
import com.gustavoas.noti.notifications.ProgressBarNotification
import kotlin.math.roundToInt

class NotificationListenerService : NotificationListenerService() {
    private val appsRepository by lazy { ProgressBarAppsRepository(this) }

    private val activeProgressBars = mutableMapOf<String, ProgressBarNotification>()

    private fun isMediaNotification(sbn: StatusBarNotification): Boolean {
        val extras = sbn.notification.extras

        val template = extras.getString(Notification.EXTRA_TEMPLATE)
        val templateMatch = Notification.MediaStyle::class.java.name

        if (template != templateMatch) {
            return false
        }

        return sbn.notification.extras.containsKey(NotificationCompat.EXTRA_MEDIA_SESSION)
    }

    private fun isDownloadNotification(sbn: StatusBarNotification): Boolean {
        val extras = sbn.notification.extras

        val hasProgress = extras.getInt(NotificationCompat.EXTRA_PROGRESS) > 0
        val hasProgressMax = extras.getInt(NotificationCompat.EXTRA_PROGRESS_MAX) > 0
        val isIndeterminate = extras.getBoolean(NotificationCompat.EXTRA_PROGRESS_INDETERMINATE)

        return hasProgress && hasProgressMax && !isIndeterminate
    }

    // TODO Do this differently
    private fun getProgressFromPercentage(sbn: StatusBarNotification): Int {
        val extras = sbn.notification.extras
        val title = extras.getCharSequence(Notification.EXTRA_TITLE)?.toString() ?: ""
        val text = extras.getCharSequence(Notification.EXTRA_TEXT)?.toString() ?: ""
        val subText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT)?.toString() ?: ""
        val bigText = extras.getCharSequence(Notification.EXTRA_BIG_TEXT)?.toString() ?: ""
        val textLines = extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES)

        val percentageProgress = title.substringBefore("%").toFloatOrNull() ?:
        text.substringBefore("%").toFloatOrNull() ?:
        subText.substringBefore("%").toFloatOrNull() ?:
        bigText.split("\n").firstOrNull { it.contains("%") }?.toString()
            ?.substringBefore("%")?.toFloatOrNull() ?:
        textLines?.firstOrNull { it.contains("%") }?.toString()
            ?.substringBefore("%")?.toFloatOrNull()

        if (percentageProgress == null || percentageProgress.isNaN()) {
            return 0
        }

        return percentageProgress.roundToInt()
    }

    private fun isGoogleTimerNotification(sbn: StatusBarNotification): Boolean {
        if (sbn.packageName != "com.google.android.deskclock") {
            return false
        }

        val sortKey = sbn.notification.sortKey
        return !sortKey.isNullOrEmpty()
    }

    private fun shouldShowProgressForNotification(sbn: StatusBarNotification): Boolean {
        if (sbn.packageName == this.packageName) {
            return false
        }

        val uniqueIdentifier = sbn.key ?: return false

        if (activeProgressBars.containsKey(uniqueIdentifier)) {
            return true
        }

        if (!showForApp(sbn.packageName ?: "")) {
            return false
        }

        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
        if (isMediaNotification(sbn)) {
            val enabledForMedia = sharedPrefs.getBoolean("showForMedia", true)
            return enabledForMedia
        }

        if (isDownloadNotification(sbn) || getProgressFromPercentage(sbn) > 0) {
            val enabledForDownloads = sharedPrefs.getBoolean("showForDownloads", true)
            return enabledForDownloads
        }

        return isGoogleTimerNotification(sbn)
    }

    override fun onNotificationPosted(sbn: StatusBarNotification) {
        super.onNotificationPosted(sbn)

        val notificationUser = sbn.user
        val packageUser = Process.myUserHandle()

        if (notificationUser != packageUser) {
            return
        }

        if (shouldShowProgressForNotification(sbn)) {
            getProgressFromNotification(sbn)
        }
    }

    private fun getProgressFromNotification(sbn: StatusBarNotification) {
        val uniqueIdentifier = sbn.key.toString()

        if (activeProgressBars.containsKey(uniqueIdentifier)) {
            val notification = activeProgressBars[uniqueIdentifier]

            notification?.updateNotification(sbn)

            return
        }

        activeProgressBars[uniqueIdentifier] = if (isMediaNotification(sbn)) {
            MediaProgressBar(this, sbn, appsRepository)
        } else if (isGoogleTimerNotification(sbn)) {
            GoogleTimerProgressBar(this, sbn, appsRepository)
        } else if (isDownloadNotification(sbn)) {
            DownloadProgressBar(this, sbn, appsRepository)
        }  else if (getProgressFromPercentage(sbn) > 0) {
            PercentageProgressBar(this, sbn, appsRepository)
        } else {
            return
        }
    }

    override fun onNotificationRemoved(sbn: StatusBarNotification) {
        super.onNotificationRemoved(sbn)

        val activeProgressBar = activeProgressBars.remove(sbn.key.toString())
        activeProgressBar?.cancel()
    }

    private fun showForApp(packageName: String): Boolean {
        return packageName.isNotEmpty() && appsRepository.showProgressForApp(packageName) != false
    }

    override fun onDestroy() {
        super.onDestroy()

        activeProgressBars.values.forEach { it.cancel() }
        activeProgressBars.clear()
        appsRepository.close()
    }
}

================================================
FILE: app/src/main/res/color/outlined_button_selector.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="?attr/colorPrimaryContainer" android:state_checked="true" />
    <item android:color="@android:color/transparent" />
</selector>

================================================
FILE: app/src/main/res/drawable/circular_mask.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:colorControlHighlight">

    <item android:id="@android:id/mask">
        <shape android:shape="oval">
            <solid android:color="@color/app_accent_color" />
        </shape>
    </item>

</ripple>

================================================
FILE: app/src/main/res/drawable/ic_accessibility.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M480,240Q447,240 424,217Q400,193 400,160Q400,127 424,104Q447,80 480,80Q513,80 537,104Q560,127 560,160Q560,193 537,217Q513,240 480,240ZM360,880L360,360Q300,355 238,345Q176,335 120,320L140,240Q218,261 306,271Q394,280 480,280Q566,280 654,271Q742,261 820,240L840,320Q784,335 722,345Q660,355 600,360L600,880L520,880L520,640L440,640L440,880L360,880Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_apps.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M240,800Q207,800 184,777Q160,753 160,720Q160,687 184,664Q207,640 240,640Q273,640 297,664Q320,687 320,720Q320,753 297,777Q273,800 240,800ZM480,800Q447,800 424,777Q400,753 400,720Q400,687 424,664Q447,640 480,640Q513,640 537,664Q560,687 560,720Q560,753 537,777Q513,800 480,800ZM720,800Q687,800 664,777Q640,753 640,720Q640,687 664,664Q687,640 720,640Q753,640 777,664Q800,687 800,720Q800,753 777,777Q753,800 720,800ZM240,560Q207,560 184,537Q160,513 160,480Q160,447 184,424Q207,400 240,400Q273,400 297,424Q320,447 320,480Q320,513 297,537Q273,560 240,560ZM480,560Q447,560 424,537Q400,513 400,480Q400,447 424,424Q447,400 480,400Q513,400 537,424Q560,447 560,480Q560,513 537,537Q513,560 480,560ZM720,560Q687,560 664,537Q640,513 640,480Q640,447 664,424Q687,400 720,400Q753,400 777,424Q800,447 800,480Q800,513 777,537Q753,560 720,560ZM240,320Q207,320 184,297Q160,273 160,240Q160,207 184,184Q207,160 240,160Q273,160 297,184Q320,207 320,240Q320,273 297,297Q273,320 240,320ZM480,320Q447,320 424,297Q400,273 400,240Q400,207 424,184Q447,160 480,160Q513,160 537,184Q560,207 560,240Q560,273 537,297Q513,320 480,320ZM720,320Q687,320 664,297Q640,273 640,240Q640,207 664,184Q687,160 720,160Q753,160 777,184Q800,207 800,240Q800,273 777,297Q753,320 720,320Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_bug_report.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="48"
    android:viewportHeight="48"
    android:tint="@color/text">
  <path
      android:fillColor="#000000"
      android:pathData="M36.8,17.6h-4.5c-0.7,-1.2 -1.7,-2.3 -2.9,-3.1l2.6,-2.6l-2.2,-2.2l-3.5,3.5c-0.7,-0.2 -1.5,-0.3 -2.3,-0.3s-1.5,0.1 -2.2,0.3l-3.5,-3.5L16,11.9l2.6,2.6c-1.2,0.8 -2.2,1.9 -2.9,3.1h-4.5v3.2h3.3c-0.1,0.5 -0.1,1.1 -0.1,1.6V24h-3.2v3.2h3.2v1.6c0,0.5 0.1,1.1 0.1,1.6h-3.3v3.2h4.5c1.7,2.9 4.7,4.8 8.3,4.8s6.6,-1.9 8.3,-4.8h4.5v-3.2h-3.3c0.1,-0.5 0.1,-1.1 0.1,-1.6v-1.6h3.2V24h-3.2v-1.6c0,-0.5 -0.1,-1.1 -0.1,-1.6h3.3V17.6zM27.2,30.4h-6.4v-3.2h6.4V30.4zM27.2,24h-6.4v-3.2h6.4V24z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_calentile.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="28dp"
    android:width="28dp"
    android:viewportHeight="48"
    android:viewportWidth="48"
    android:tint="@color/text">
    <path android:fillColor="#000000" android:pathData="M20.8,29a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M20.8,21.1a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M20.8,36.9a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M29.7,21.1a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M11.9,21.1a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M11.9,29a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M11.9,36.9a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M29.7,36.9a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
    <path android:fillColor="#000000" android:pathData="M40,6h-2V2h-4v4H14V2h-4v4H8c-2.2,0 -4,1.8 -4,4v32c0,2.2 1.8,4 4,4h32c2.2,0 4,-1.8 4,-4V10C44,7.8 42.2,6 40,6zM40,42H8V16h32V42z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_circular_progress_bar.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M480,905Q390,905 312,872Q235,840 178,782Q120,725 88,648Q55,570 55,480Q55,390 88,313Q120,235 177,178Q235,120 312,87Q390,54 480,54L480,149Q342,149 245,245Q149,342 149,480Q149,618 245,715Q342,811 480,811Q618,811 715,715Q811,618 811,480L906,480Q906,570 873,648Q840,725 783,783Q725,840 648,873Q570,905 480,905Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_close.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M256,760L200,704L424,480L200,256L256,200L480,424L704,200L760,256L536,480L760,704L704,760L480,536L256,760Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_colors.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M18,4L18,3c0,-0.6 -0.5,-1 -1,-1L5,2c-0.6,0 -1,0.5 -1,1v4c0,0.6 0.5,1 1,1h12c0.6,0 1,-0.5 1,-1L18,6h1v4L9,10v11c0,0.6 0.5,1 1,1h2c0.6,0 1,-0.5 1,-1v-9h8L21,4h-3zM16,6L6,6L6,4h10v2z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_contrast.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text"
    android:autoMirrored="true">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M480,899Q393,899 317,866Q240,833 183,777Q127,720 94,643Q61,567 61,480Q61,393 94,317Q127,240 183,183Q240,127 317,94Q393,61 480,61Q567,61 643,94Q720,127 777,183Q833,240 866,317Q899,393 899,480Q899,567 866,643Q833,720 777,777Q720,833 643,866Q567,899 480,899ZM527,789Q641,772 717,686Q793,600 793,480Q793,361 717,275Q641,189 527,172L527,789Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_download.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M280,680H680V600H280ZM480,560L640,400L584,344L520,406V240H440V406L376,344L320,400ZM480,880Q397,880,324,848.5Q251,817,197,763Q143,709,111.5,636Q80,563,80,480Q80,397,111.5,324Q143,251,197,197Q251,143,324,111.5Q397,80,480,80Q563,80,636,111.5Q709,143,763,197Q817,251,848.5,324Q880,397,880,480Q880,563,848.5,636Q817,709,763,763Q709,817,636,848.5Q563,880,480,880ZM480,800Q614,800,707,707Q800,614,800,480Q800,346,707,253Q614,160,480,160Q346,160,253,253Q160,346,160,480Q160,614,253,707Q346,800,480,800Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_download_filled.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M280,680H680V600H280ZM480,560L640,400L584,344L520,406V240H440V406L376,344L320,400ZM480,880Q397,880,324,848.5Q251,817,197,763Q143,709,111.5,636Q80,563,80,480Q80,397,111.5,324Q143,251,197,197Q251,143,324,111.5Q397,80,480,80Q563,80,636,111.5Q709,143,763,197Q817,251,848.5,324Q880,397,880,480Q880,563,848.5,636Q817,709,763,763Q709,817,636,848.5Q563,880,480,880Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_fullscreen.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M120,840L120,640L200,640L200,760L320,760L320,840L120,840ZM640,840L640,760L760,760L760,640L840,640L840,840L640,840ZM120,320L120,120L320,120L320,200L200,200L200,320L120,320ZM760,320L760,200L640,200L640,120L840,120L840,320L760,320Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_height.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="30dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M480,854L298,672L354,617L440,702L439,258L354,344L297,288L480,105L663,288L606,344L520,258L520,702L607,617L663,672L480,854Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_horizontal.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M280,840L80,640L280,440L337,496L233,600L840,600L840,680L233,680L337,784L280,840ZM680,520L623,464L727,360L120,360L120,280L727,280L623,176L680,120L880,320L680,520Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_info.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M440,680L520,680L520,440L440,440L440,680ZM480,360Q497,360 509,349Q520,337 520,320Q520,303 509,292Q497,280 480,280Q463,280 452,292Q440,303 440,320Q440,337 452,349Q463,360 480,360ZM480,880Q397,880 324,849Q251,817 197,763Q143,709 112,636Q80,563 80,480Q80,397 112,324Q143,251 197,197Q251,143 324,112Q397,80 480,80Q563,80 636,112Q709,143 763,197Q817,251 849,324Q880,397 880,480Q880,563 849,636Q817,709 763,763Q709,817 636,849Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="480"
    android:viewportHeight="480"
    android:tint="@color/deep_purple_800">
  <group android:scaleX="0.72"
      android:scaleY="0.72"
      android:translateX="67.2"
      android:translateY="67.2">
    <path
        android:pathData="M290.5,119.3c-25.8,0 -46.7,20.9 -46.7,46.7c0,25.8 20.9,46.7 46.7,46.7c25.8,0 46.7,-20.9 46.7,-46.7C337.2,140.2 316.3,119.3 290.5,119.3zM290.5,196.3c-16.7,0 -30.3,-13.6 -30.3,-30.3c0,-16.7 13.6,-30.3 30.3,-30.3s30.3,13.6 30.3,30.3C320.8,182.7 307.3,196.3 290.5,196.3z"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M142.7,122.6H158v11.8h0.7c2.4,-4.2 6.2,-7.6 11.2,-10.4c5,-2.8 10.3,-4.2 15.7,-4.2c10.4,0 18.4,3 24,8.9c5.6,6 8.4,14.4 8.4,25.4v53.5h-16v-52.4c-0.3,-13.9 -7.4,-20.8 -21,-20.8c-6.4,0 -11.7,2.6 -16,7.7c-4.3,5.2 -6.4,11.3 -6.4,18.5v47.1h-16V122.6z"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M186.9,360.6c-7,0 -12.9,-2.2 -17.5,-6.5c-4.6,-4.3 -7,-10.4 -7.1,-18.1v-48.2h-15.1v-14.8h15.1v-26.4h16.2v26.4h21.1v14.8h-21.1v42.9c0,5.8 1.1,9.7 3.3,11.7c2.2,2.1 4.8,3.1 7.6,3.1c1.3,0 2.6,-0.1 3.8,-0.4c1.2,-0.3 2.4,-0.7 3.4,-1.1l5.1,14.4C197.4,359.8 192.5,360.6 186.9,360.6z"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M288,253.3a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M308.3,361.4h-16c-0.6,0 -1,-0.4 -1,-1v-77.7c0,-0.6 0.4,-1 1,-1h16c0.6,0 1,0.4 1,1v77.7C309.3,361 308.9,361.4 308.3,361.4z"
        android:fillColor="@android:color/black"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_linear_progress_bar.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M66,589L66,510L288,510L288,589L66,589ZM348,589L348,510L573,510L573,589L348,589ZM633,589L633,510L855,510L855,589L633,589ZM66,450L66,370L855,370L855,450L66,450Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_lockscreen.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M19,1L9,1c-1.1,0 -2,0.9 -2,2v3h2L9,4h10v16L9,20v-2L7,18v3c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L21,3c0,-1.1 -0.9,-2 -2,-2zM10.8,11L10.8,9.5C10.8,8.1 9.4,7 8,7S5.2,8.1 5.2,9.5L5.2,11c-0.6,0 -1.2,0.6 -1.2,1.2v3.5c0,0.7 0.6,1.3 1.2,1.3h5.5c0.7,0 1.3,-0.6 1.3,-1.2v-3.5c0,-0.7 -0.6,-1.3 -1.2,-1.3zM9.5,11h-3L6.5,9.5c0,-0.8 0.7,-1.3 1.5,-1.3s1.5,0.5 1.5,1.3L9.5,11z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_margin_top.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M440,643L520,643L520,468L590,539L646,483L480,317L314,483L370,539L440,468L440,643ZM480,894Q395,894 319,862Q244,829 187,773Q131,717 98,641Q66,566 66,480Q66,394 98,318Q131,243 187,187Q243,131 319,98Q394,65 480,65Q566,65 642,98Q718,131 774,186Q830,242 862,318Q895,393 895,480Q895,566 862,641Q829,717 773,773Q718,829 642,862Q567,894 480,894ZM480,815Q620,815 717,717Q815,620 815,480Q815,340 717,243Q620,145 480,145Q341,145 243,243Q145,340 145,480Q145,619 243,717Q341,815 480,815Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_music.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M12,3l0.01,10.55c-0.59-0.34-1.27-0.55-2-0.55C7.79,13,6,14.79,6,17s1.79,4,4.01,4S14,19.21,14,17V7h4V3zM10.01,19c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2-0.9,2-2,2z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_music_filled.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M12,3v10.55c-0.59-0.34-1.27-0.55-2-0.55-2.21,0-4,1.79-4,4s1.79,4,4,4s4-1.79,4-4V7h4V3z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_notification.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="27dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M91,404Q91,306 134,225Q176,143 250,89L295,150Q236,193 201,259Q166,325 166,404L91,404ZM795,404Q795,325 759,259Q724,193 666,150L711,89Q784,144 827,225Q870,306 870,404L795,404ZM146,774L146,695L221,695L221,405Q221,316 272,245Q324,173 410,154L410,136Q410,106 431,86Q451,65 480,65Q509,65 529,86Q550,106 550,136L550,154Q636,172 688,244Q740,316 740,405L740,695L815,695L815,774L146,774ZM480,899Q445,899 420,874Q396,849 396,814L565,814Q565,849 540,874Q515,899 480,899ZM300,695L660,695L660,405Q660,331 608,278Q555,225 480,225Q405,225 353,278Q300,331 300,405L300,695Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_notification_bar.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="27dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M120,840L120,510L840,510L840,840L120,840ZM120,450L120,120L840,120L840,450L120,450Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_overlay.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M479,868L96,569L162,520L479,768L798,520L864,569ZM479,692L96,394L479,95L863,394ZM479,591L733,394L479,196L226,394Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_palette.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M480,880Q398,880,325,848.5Q252,817,197.5,762.5Q143,708,111.5,635Q80,562,80,480Q80,397,112.5,324Q145,251,200.5,197Q256,143,330,111.5Q404,80,488,80Q568,80,639,107.5Q710,135,763.5,183.5Q817,232,848.5,298.5Q880,365,880,442Q880,557,810,618.5Q740,680,640,680H566Q557,680,553.5,685Q550,690,550,696Q550,708,565,730.5Q580,753,580,782Q580,832,552.5,856Q525,880,480,880ZM260,520Q286,520,303,503Q320,486,320,460Q320,434,303,417Q286,400,260,400Q234,400,217,417Q200,434,200,460Q200,486,217,503Q234,520,260,520ZM380,360Q406,360,423,343Q440,326,440,300Q440,274,423,257Q406,240,380,240Q354,240,337,257Q320,274,320,300Q320,326,337,343Q354,360,380,360ZM580,360Q606,360,623,343Q640,326,640,300Q640,274,623,257Q606,240,580,240Q554,240,537,257Q520,274,520,300Q520,326,537,343Q554,360,580,360ZM700,520Q726,520,743,503Q760,486,760,460Q760,434,743,417Q726,400,700,400Q674,400,657,417Q640,434,640,460Q640,486,657,503Q674,520,700,520ZM480,800Q489,800,494.5,795Q500,790,500,782Q500,768,485,749Q470,730,470,692Q470,650,499,625Q528,600,570,600H640Q706,600,753,561.5Q800,523,800,442Q800,321,707.5,240.5Q615,160,488,160Q352,160,256,253Q160,346,160,480Q160,613,253.5,706.5Q347,800,480,800Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_preview.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M19.8,5.7c0.6,0.7 1.1,1.5 1.5,2.4S21.9,10 22,11h-2c-0.1,-0.7 -0.3,-1.4 -0.6,-2.1c-0.3,-0.7 -0.6,-1.3 -1,-1.8L19.8,5.7zM22,13c-0.1,1 -0.4,1.9 -0.8,2.8c-0.4,0.9 -0.9,1.7 -1.5,2.4l-1.4,-1.4c0.4,-0.5 0.8,-1.2 1,-1.8c0.3,-0.7 0.5,-1.4 0.6,-2.1H22zM13.1,2c1,0.1 1.9,0.3 2.8,0.7c0.9,0.4 1.7,0.9 2.5,1.5L17,5.7c-0.6,-0.4 -1.2,-0.8 -1.8,-1.1c-0.6,-0.3 -1.3,-0.5 -2.1,-0.6V2zM16.9,18.3l1.5,1.5c-0.8,0.6 -1.6,1.1 -2.5,1.5C15,21.6 14,21.9 13,22v-2c0.8,-0.1 1.4,-0.3 2.1,-0.6C15.8,19.1 16.4,18.7 16.9,18.3zM9.5,16.5v-9l7,4.5L9.5,16.5zM11,2v2C9,4.3 7.3,5.2 6,6.7S4,10 4,12s0.7,3.8 2,5.3s3,2.4 5,2.7v2c-2.6,-0.3 -4.7,-1.4 -6.4,-3.2C2.9,16.8 2,14.6 2,12s0.9,-4.8 2.6,-6.7S8.4,2.3 11,2z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_progress_bar_style.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M190,180L190,100L770,100L770,180L190,180ZM480,859Q357,859 272,774Q187,689 187,566Q187,444 272,359Q358,274 480,274Q603,274 688,359Q773,444 773,567Q773,689 688,774Q602,859 480,859ZM480,754Q557,754 612,699Q667,645 667,567Q667,489 613,434Q558,379 480,379Q403,379 348,434Q293,488 293,566Q293,644 347,699Q402,754 480,754Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_rounded_corners.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M95,865L95,783L177,783L177,865L95,865ZM95,693L95,610L177,610L177,693L95,693ZM95,521L95,439L177,439L177,521L95,521ZM95,349L95,266L177,266L177,349L95,349ZM95,177L95,94L177,94L177,177L95,177ZM267,865L267,783L350,783L350,865L267,865ZM267,177L267,94L350,94L350,177L267,177ZM439,865L439,783L521,783L521,865L439,865ZM611,865L611,783L694,783L694,865L611,865ZM783,865L783,783L866,783L866,865L783,865ZM783,693L783,610L866,610L866,693L783,693ZM866,521L783,521L783,307Q783,254 745,215Q707,177 653,177L439,177L439,94L653,94Q742,94 804,156Q866,218 866,307L866,521Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_share.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M720,880Q670,880 635,845Q600,810 600,760Q600,753 601,746Q602,738 604,732L322,568Q305,583 284,592Q263,600 240,600Q190,600 155,565Q120,530 120,480Q120,430 155,395Q190,360 240,360Q263,360 284,369Q305,377 322,392L604,228Q602,222 601,215Q600,207 600,200Q600,150 635,115Q670,80 720,80Q770,80 805,115Q840,150 840,200Q840,250 805,285Q770,320 720,320Q697,320 676,312Q655,303 638,288L356,452Q358,458 359,466Q360,473 360,480Q360,487 359,495Q358,502 356,508L638,672Q655,657 676,649Q697,640 720,640Q770,640 805,675Q840,710 840,760Q840,810 805,845Q770,880 720,880Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_size.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="960"
    android:viewportHeight="960"
    android:tint="@color/text">
  <path
      android:fillColor="@android:color/black"
      android:pathData="M106,854L106,598L185,598L185,718L324,580L380,637L242,775L362,775L362,854L106,854ZM598,854L598,775L718,775L581,638L638,581L775,718L775,598L855,598L855,854L598,854ZM323,379L185,242L185,362L106,362L106,105L362,105L362,185L242,185L379,323L323,379ZM638,379L581,323L718,185L598,185L598,105L855,105L855,362L775,362L775,242L638,379Z"/>
</vector>


================================================
FILE: app/src/main/res/drawable/ic_sumup.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="921.6"
    android:viewportHeight="921.6"
    android:tint="@color/text">
  <group android:scaleX="1.3"
      android:scaleY="1.3"
      android:translateX="-138.24"
      android:translateY="-138.24">
    <path
        android:pathData="M680.8,699h-440c-27.6,0-50-22.4-50-50V532.4c0-27.6,22.4-50,50-50h440c27.6,0,50,22.4,50,50V649C730.8,676.6,708.4,699,680.8,699z"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M700.5,353.3c-18.1,0-34.1-10.2-41.7-26.7l-20.6-45.4L593,260.7c-15.5-7-25.5-21.4-26.8-38.1H240.8c-27.6,0-50,22.4-50,50v116.6c0,27.6,22.4,50,50,50h440c27.6,0,50-22.4,50-50v-47.4C722.6,349.1,711.9,353.3,700.5,353.3z"
        android:fillColor="@android:color/black"/>
    <path
        android:pathData="M795.5,204.6l-55.5-25.2-25.2-55.5c-5.5-12.4-23.3-12.4-28.8,0l-25.2,55.5-55.5,25.2c-12.4,5.7-12.4,23.3,0,28.8l55.5,25.2L686,314c5.7,12.4,23.3,12.4,28.8,0l25.2-55.5l55.5-25.2C807.9,227.7,807.9,210.1,795.5,204.6z"
        android:fillColor="@android:color/black"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable/layout_bg.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="12dp" />
    <solid android:color="?attr/colorBackgroundFloating" />
</shape>


================================================
FILE: app/src/main/res/drawable/selector_download.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_download_filled" android:state_checked="true"/>
    <item android:drawable="@drawable/ic_download"/>
</selector>

================================================
FILE: app/src/main/res/drawable/selector_music.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_music_filled" android:state_checked="true"/>
    <item android:drawable="@drawable/ic_music"/>
</selector>

================================================
FILE: app/src/main/res/drawable/splashscreen.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="30dp"
    android:height="30dp"
    android:viewportWidth="30"
    android:viewportHeight="30"
    android:tint="@color/deep_purple_800">
  <group
      android:name="splashscreen_group"
      android:translateX="3"
      android:translateY="3">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M3.4,10.5h1v0.7h0c0.2,-0.3 0.4,-0.5 0.7,-0.7c0.3,-0.2 0.7,-0.3 1,-0.3c0.7,0 1.2,0.2 1.5,0.6c0.4,0.4 0.5,0.9 0.5,1.6v3.4h-1v-3.3c0,-0.9 -0.5,-1.3 -1.3,-1.3c-0.4,0 -0.7,0.2 -1,0.5c-0.3,0.3 -0.4,0.7 -0.4,1.2v3h-1V10.5z"/>
    <path
        android:fillColor="@android:color/black"
        android:pathData="M11.8,10.3c-1.6,0 -2.9,1.3 -2.9,2.9c0,1.6 1.3,2.9 2.9,2.9c1.6,0 2.9,-1.3 2.9,-2.9C14.7,11.6 13.4,10.3 11.8,10.3zM11.8,15.2c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2c1.1,0 2,0.9 2,2C13.8,14.3 12.9,15.2 11.8,15.2z"/>
    <path
        android:fillColor="@android:color/black"
        android:pathData="M17.5,16c-0.4,0 -0.8,-0.1 -1.1,-0.4s-0.4,-0.6 -0.4,-1.1v-3H15v-0.9h0.9V8.9h1v1.6h1.3v0.9H17v2.7c0,0.4 0.1,0.6 0.2,0.7c0.1,0.1 0.3,0.2 0.5,0.2c0.1,0 0.2,0 0.2,0s0.1,0 0.2,-0.1l0.3,0.9C18.1,15.9 17.8,16 17.5,16z"/>
    <path
        android:fillColor="@android:color/black"
        android:pathData="M20.6,8.6c0,0.2 -0.1,0.4 -0.2,0.5c-0.1,0.1 -0.3,0.2 -0.5,0.2s-0.4,-0.1 -0.5,-0.2c-0.1,-0.1 -0.2,-0.3 -0.2,-0.5c0,-0.2 0.1,-0.4 0.2,-0.5c0.1,-0.1 0.3,-0.2 0.5,-0.2s0.4,0.1 0.5,0.2S20.6,8.4 20.6,8.6zM20.4,10.5v5.4h-1v-5.4H20.4z"/>
  </group>
</vector>


================================================
FILE: app/src/main/res/drawable-v31/splashscreen.xml
================================================
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector
            android:name="noti_splashscreen"
            android:width="30dp"
            android:height="30dp"
            android:viewportWidth="30"
            android:viewportHeight="30"
            android:tint="@color/deep_purple_800">
            <group
                android:name="pb_group"
                android:translateX="14.85"
                android:translateY="22.2"
                android:scaleX="0.45"
                android:scaleY="0.45"
                android:rotation="-135">
                <path
                    android:name="progress_bar"
                    android:pathData="M 13.4 13.4 C 15.5 11.2 15.5 7.8 13.4 5.6 C 11.2 3.5 7.8 3.5 5.6 5.6 C 3.5 7.8 3.5 11.2 5.6 13.4 C 7.8 15.5 11.2 15.5 13.4 13.4 Z"
                    android:strokeColor="@android:color/black"
                    android:strokeWidth="2.1"/>
            </group>
            <group
                android:name="letters_group"
                android:translateX="3"
                android:translateY="3">
                <path
                    android:name="letters"
                    android:pathData="M 3.4 10.5 L 4.4 10.5 L 4.4 11.2 C 4.6 10.9 4.8 10.7 5.1 10.5 C 5.4 10.3 5.8 10.2 6.1 10.2 C 6.8 10.2 7.3 10.4 7.6 10.8 C 8 11.2 8.1 11.7 8.1 12.4 L 8.1 15.8 L 7.1 15.8 L 7.1 12.5 C 7.1 11.6 6.6 11.2 5.8 11.2 C 5.4 11.2 5.1 11.4 4.8 11.7 C 4.5 12 4.4 12.4 4.4 12.9 L 4.4 15.9 L 3.4 15.9 L 3.4 10.5 Z M 17.5 16 C 17.1 16 16.7 15.9 16.4 15.6 C 16.1 15.3 16 15 16 14.5 L 16 11.5 L 15 11.5 L 15 10.6 L 15.9 10.6 L 15.9 8.9 L 16.9 8.9 L 16.9 10.5 L 18.2 10.5 L 18.2 11.4 L 17 11.4 L 17 14.1 C 17 14.5 17.1 14.7 17.2 14.8 C 17.3 14.9 17.5 15 17.7 15 L 17.9 15 C 17.9 15 18 15 18.1 14.9 L 18.4 15.8 C 18.1 15.9 17.8 16 17.5 16 Z M 20.6 8.6 C 20.6 8.8 20.5 9 20.4 9.1 C 20.3 9.2 20.1 9.3 19.9 9.3 C 19.7 9.3 19.5 9.2 19.4 9.1 C 19.3 9 19.2 8.8 19.2 8.6 C 19.2 8.4 19.3 8.2 19.4 8.1 C 19.5 8 19.7 7.9 19.9 7.9 C 20.1 7.9 20.3 8 20.4 8.1 C 20.5 8.2 20.6 8.4 20.6 8.6 Z M 20.4 10.5 L 20.4 15.9 L 19.4 15.9 L 19.4 10.5 L 20.4 10.5 Z"
                    android:fillColor="@android:color/black"
                    android:fillAlpha="0"/>
            </group>
        </vector>
    </aapt:attr>
    <target android:name="progress_bar">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="trimPathStart"
                android:startOffset="25"
                android:duration="400"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType"
                android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
        </aapt:attr>
    </target>
    <target android:name="letters">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:propertyName="fillAlpha"
                android:startOffset="225"
                android:duration="200"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType"
                android:interpolator="@android:anim/decelerate_interpolator"/>
        </aapt:attr>
    </target>
</animated-vector>


================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:elevation="0dp">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            style="?attr/collapsingToolbarLayoutLargeStyle"
            android:id="@+id/collapsingToolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
            app:collapsedTitleGravity="center"
            android:background="?android:attr/colorBackground"
            app:contentScrim="?android:attr/colorBackground"
            app:expandedTitleMarginStart="30dp"
            app:expandedTitleMarginBottom="28dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/topAppBar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:title="@string/app_name"
                app:menu="@menu/settings_menu" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <FrameLayout
        android:id="@+id/settings"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/previewFab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:layout_gravity="bottom|end"
        app:icon="@drawable/ic_preview"
        android:text="@string/settingsPreviewFab"
        android:textSize="18sp"
        app:iconSize="26dp"
        android:layout_marginEnd="30dp"
        android:layout_marginBottom="30dp" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

================================================
FILE: app/src/main/res/layout/advanced_style_dialog.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            style="?android:attr/windowTitleStyle"
            android:id="@+id/portraitStyleTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/prefsProgressBarStylePortraitTitle"
            android:singleLine="false"
            android:paddingStart="?dialogPreferredPadding"
            android:paddingEnd="?dialogPreferredPadding"
            android:paddingTop="18dp"
            android:paddingBottom="8dp" />

        <ListView
            android:id="@+id/portraitStyleOptions"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:divider="@null"
            android:choiceMode="singleChoice" />

        <TextView
            style="?android:attr/windowTitleStyle"
            android:id="@+id/landscapeStyleTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/prefsProgressBarStyleLandscapeTitle"
            android:singleLine="false"
            android:paddingStart="?dialogPreferredPadding"
            android:paddingEnd="?dialogPreferredPadding"
            android:paddingTop="8dp"
            android:paddingBottom="8dp"/>

        <ListView
            android:id="@+id/landscapeStyleOptions"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:divider="@null"
            android:choiceMode="singleChoice"/>

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

================================================
FILE: app/src/main/res/layout/app_item.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/item_container"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingHorizontal="16dp"
    android:paddingVertical="10dp"
    android:background="?android:attr/selectableItemBackground">

    <TextView
        android:id="@+id/app_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="14dp"
        android:layout_marginEnd="12dp"
        android:drawablePadding="19dp"
        android:gravity="center_vertical"
        android:textSize="22sp"
        android:textColor="?android:attr/textColorPrimary"
        android:layout_weight="1"/>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/color_picker"
        android:layout_width="41dp"
        android:layout_height="41dp"
        android:insetTop="5dp"
        android:insetBottom="5dp"
        android:insetLeft="5dp"
        android:insetRight="5dp"
        android:scrollbars="none"
        android:foreground="?actionBarItemBackground"
        app:strokeColor="#46000000"
        app:strokeWidth="2dp"/>

    <com.google.android.material.checkbox.MaterialCheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

================================================
FILE: app/src/main/res/layout/banner_preference.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    style="@style/Widget.Material3.CardView.Filled"
    app:cardCornerRadius="18dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="?android:attr/listPreferredItemPaddingStart">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="8dp"
        android:layout_marginStart="24dp"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@android:id/title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginTop="16dp"
                android:textAppearance="?android:attr/textAppearanceListItem" />

            <ImageButton
                android:id="@+id/closeBtn"
                android:layout_width="38dp"
                android:layout_height="38dp"
                android:padding="6dp"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginStart="6dp"
                android:background="@drawable/circular_mask"
                android:scaleType="fitCenter"
                android:src="@drawable/ic_close"
                app:tint="@color/text" />

        </LinearLayout>

        <Button
            android:id="@+id/bannerBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/Widget.Material3.Button.TextButton"
            android:layout_marginTop="4dp"
            android:textColor="?attr/colorOnPrimaryContainer"/>

    </LinearLayout>

</com.google.android.material.card.MaterialCardView>

================================================
FILE: app/src/main/res/layout/button_toggle_group.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="?android:attr/listPreferredItemPaddingStart">

    <com.google.android.material.card.MaterialCardView
        style="@style/Widget.Material3.CardView.Filled"
        app:cardCornerRadius="18dp"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        app:cardBackgroundColor="?attr/colorBackgroundFloating"
        app:layout_constraintWidth_max="400dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingVertical="4dp"
            android:paddingHorizontal="8dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/activateToggle"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textSize="18sp"
                android:textStyle="bold"
                android:layout_marginTop="4dp"
                android:layout_marginHorizontal="14dp" />

            <com.google.android.material.button.MaterialButtonToggleGroup
                android:id="@+id/toggleGroup"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/toggleDownloads"
                    style="@style/ToggleGroupButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    app:icon="@drawable/selector_download"
                    app:iconSize="28dp"
                    app:iconGravity="top"
                    android:text="@string/downloads"
                    android:paddingVertical="16dp"/>

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/toggleMedia"
                    style="@style/ToggleGroupButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    app:icon="@drawable/selector_music"
                    app:iconSize="28dp"
                    app:iconGravity="top"
                    android:text="@string/media"
                    android:paddingVertical="16dp"/>

            </com.google.android.material.button.MaterialButtonToggleGroup>

        </LinearLayout>

    </com.google.android.material.card.MaterialCardView>

</androidx.constraintlayout.widget.ConstraintLayout>


================================================
FILE: app/src/main/res/layout/fragment_per_app_settings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/apps_recycler_view"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:paddingBottom="100dp"
        android:scrollbars="none"
        android:visibility="gone"
        android:clipToPadding="false"/>

    <TextView
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="30dp"
        android:gravity="center_horizontal"
        android:text="@string/perAppSettingsEmptyView"
        android:textColor="?android:attr/textColorPrimary"
        android:textSize="20sp"
        android:layout_marginBottom="100dp"/>

</LinearLayout>

================================================
FILE: app/src/main/res/layout/material_switch.xml
================================================
<?xml version="1.0" encoding="utf-8"?>

<!-- https://stackoverflow.com/a/76270479 -->
<com.google.android.material.materialswitch.MaterialSwitch
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/switchWidget"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@null" />

================================================
FILE: app/src/main/res/layout/per_app_settings_footer.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="14dp"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:textColor="?android:attr/textColorSecondary"
    android:maxLines="4"
    android:ellipsize="end"
    android:gravity="center_vertical"
    android:text="@string/perAppSettingsEmptyView"
    android:layout_marginVertical="6dp"
    android:lineSpacingMultiplier="1.2"
    android:letterSpacing="0.018"
    android:paddingVertical="10dp"
    app:drawableLeftCompat="@drawable/ic_info"
    android:drawablePadding="28dp"/>

================================================
FILE: app/src/main/res/layout/progress_bar.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container_parent"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">
                <com.google.android.material.progressindicator.LinearProgressIndicator
                    android:id="@+id/progressBar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:max="@integer/progress_bar_max"
                    android:progress="0"
                    android:theme="@style/Theme.NotiProgressBar"
                    android:visibility="gone"
                    app:trackStopIndicatorSize="0dp"
                    app:indicatorTrackGapSize="0dp"
                    app:hideAnimationBehavior="outward" />
                <com.google.android.material.progressindicator.CircularProgressIndicator
                    android:id="@+id/circularProgressBar"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:max="@integer/progress_bar_max"
                    android:progress="0"
                    android:theme="@style/Theme.NotiProgressBar"
                    android:visibility="visible"
                    app:hideAnimationBehavior="inward"
                    app:indicatorInset="0dp"
                    app:indicatorTrackGapSize="0dp"
                    app:showAnimationBehavior="outward"/>
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: app/src/main/res/layout/seekbar_preference.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:baselineAligned="false">

    <ImageView
        android:id="@android:id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:layout_marginStart="12dp"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="4dp">

        <TextView
            android:id="@android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceListItem"
            android:ellipsize="marquee"
            android:layout_marginStart="16dp"
            android:fadingEdge="horizontal"/>

        <TextView
            android:id="@android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary"
            android:maxLines="4"/>

        <com.google.android.material.slider.Slider
            android:id="@+id/slider"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:labelBehavior="gone"
            android:paddingTop="4dp"
            android:stepSize="1"/>

    </LinearLayout>

</LinearLayout>

================================================
FILE: app/src/main/res/layout-land/button_toggle_group.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingHorizontal="28dp"
    android:layout_marginVertical="?android:attr/listPreferredItemPaddingStart">

    <com.google.android.material.card.MaterialCardView
        style="@style/Widget.Material3.CardView.Filled"
        app:cardCornerRadius="18dp"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:cardBackgroundColor="?attr/colorBackgroundFloating"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:paddingVertical="4dp"
            android:paddingHorizontal="8dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/activateToggle"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textSize="18sp"
                android:textStyle="bold"
                android:layout_marginHorizontal="14dp"/>

            <com.google.android.material.button.MaterialButtonToggleGroup
                android:id="@+id/toggleGroup"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/toggleDownloads"
                    style="@style/ToggleGroupButtonStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:paddingVertical="8dp"
                    android:paddingHorizontal="48dp"
                    app:icon="@drawable/selector_download"
                    app:iconSize="28dp"
                    app:iconPadding="10dp"
                    android:text="@string/downloads"/>

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/toggleMedia"
                    style="@style/ToggleGroupButtonStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:paddingVertical="8dp"
                    android:paddingHorizontal="48dp"
                    app:icon="@drawable/selector_music"
                    app:iconSize="28dp"
                    app:iconPadding="10dp"
                    android:text="@string/media"/>

            </com.google.android.material.button.MaterialButtonToggleGroup>

        </LinearLayout>

    </com.google.android.material.card.MaterialCardView>

</androidx.constraintlayout.widget.ConstraintLayout>


================================================
FILE: app/src/main/res/menu/settings_menu.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/bug_report"
        android:icon="@drawable/ic_bug_report"
        android:title="@string/menuReportBug"
        app:showAsAction="ifRoom"
        android:visible="false"/>
</menu>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
    <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
    <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: app/src/main/res/resources.properties
================================================
unqualifiedResLocale=en

================================================
FILE: app/src/main/res/values/arrays.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="progressBarStyle">
        <item>@string/prefsProgressBarStyleEntryNone</item>
        <item>@string/prefsProgressBarStyleEntryCircular</item>
        <item>@string/prefsProgressBarStyleEntryLinear</item>
    </string-array>
    <string-array name="progressBarStyleValues">
        <item>none</item>
        <item>circular</item>
        <item>linear</item>
    </string-array>

    <string-array name="colorsArray">
        <item>@string/colorRed</item>
        <item>@string/colorPink</item>
        <item>@string/colorPurple</item>
        <item>@string/colorDeepPurple</item>
        <item>@string/colorIndigo</item>
        <item>@string/colorBlue</item>
        <item>@string/colorLightBlue</item>
        <item>@string/colorCyan</item>
        <item>@string/colorTeal</item>
        <item>@string/colorGreen</item>
        <item>@string/colorLightGreen</item>
        <item>@string/colorLime</item>
        <item>@string/colorYellow</item>
        <item>@string/colorAmber</item>
        <item>@string/colorOrange</item>
        <item>@string/colorDeepOrange</item>
        <item>@string/colorBrown</item>
        <item>@string/colorGrey</item>
        <item>@string/colorWhite</item>
    </string-array>
    <array name="colorsArrayValues">
        <item>#D32F2F</item>
        <item>#D81B60</item>
        <item>#8E24AA</item>
        <item>#6200EE</item>
        <item>#303F9F</item>
        <item>#1A73E9</item>
        <item>#03A9F4</item>
        <item>#00BCD4</item>
        <item>#009688</item>
        <item>#4CAF50</item>
        <item>#8BC34A</item>
        <item>#CDDC39</item>
        <item>#FFEB3B</item>
        <item>#FFC107</item>
        <item>#FF9800</item>
        <item>#FF5722</item>
        <item>#795548</item>
        <item>#9E9E9E</item>
        <item>#FFFFFF</item>
    </array>
</resources>

================================================
FILE: app/src/main/res/values/attrs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="HorizontalNumberPicker">
        <attr name="min_number" format="integer"/>
        <attr name="max_number" format="integer"/>
        <attr name="default_value" format="integer"/>
    </declare-styleable>
</resources>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_500">#FF6200EE</color>
    <color name="deep_purple_100">#D1C4E9</color>
    <color name="deep_purple_800">#4527A0</color>
    <color name="text">#000000</color>
    <color name="system_accent_color">@color/material_dynamic_primary40</color>
    <color name="app_accent_color">#EADDFF</color>
</resources>

================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="progress_bar_max">10000</integer>
</resources>

================================================
FILE: app/src/main/res/values/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ic_launcher_background">@color/deep_purple_100</color>
</resources>

================================================
FILE: app/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name" translatable="false">Noti Progress Bar</string>
    <string name="app_name_short" translatable="false">noti</string>
    <string name="menuReportBug">Report Bug</string>
    <string name="settingsPreviewFab">Preview</string>
    <string name="notificationListenerDescription">Reads notifications with a progress bar</string>
    <string name="perAppSettingsEmptyView">Noti will identify more compatible apps as notifications are received</string>
    <string name="activateToggle">Enabled for</string>
    <string name="downloads">Downloads</string>
    <string name="media">Media</string>
    <string name="reset">Reset</string>
    <string name="holePunchInstruction">Change alignment in circular progress bar tweaks</string>
    <string name="prefsBatteryBannerSummary">You may need to disable battery optimizations and allow autostart for Noti to work in the background. A reboot after doing this may be necessary</string>
    <string name="prefsBatteryBannerButton">Learn more</string>
    <string name="prefsCategorySetup">setup</string>
    <string name="prefsCategoryGeneral">general</string>
    <string name="prefsNotificationPermissionTitle">Allow notification access</string>
    <string name="prefsNotificationPermissionSummary">Required to read notifications with a progress bar</string>
    <string name="prefsAccessibilityPermissionTitle">Grant accessibility permission</string>
    <string name="prefsAccessibilityPermissionSummary">Required to display in the lock screen and system apps</string>
    <string name="prefsSAWPermissionTitle">Allow displaying over other apps</string>
    <string name="prefsSAWPermissionSummary">Required to display the progress bar overlay</string>
    <string name="prefsCategoryCustomization">customization</string>
    <string name="prefsProgressBarStyleTitle">Select progress bar style</string>
    <string name="prefsProgressBarStylePortraitTitle">Portrait progress bar style</string>
    <string name="prefsProgressBarStyleLandscapeTitle">Landscape progress bar style</string>
    <string name="advancedOptions">Advanced Options</string>
    <string name="prefsProgressBarStyleEntryNone">No progress bar</string>
    <string name="prefsProgressBarStyleEntryLinear">Linear progress bar</string>
    <string name="prefsProgressBarStyleEntryCircular">Circular progress bar</string>
    <string name="prefsCategoryTweaks">tweaks</string>
    <string name="prefsLinearProgressBarTweaks">Linear progress bar tweaks</string>
    <string name="prefsProgressBarThicknessTitle">Progress bar thickness</string>
    <string name="prefsProgressBarThicknessSummary">Change size of the progress bar\'s track</string>
    <string name="prefsShowBelowNotchTitle">Show below display cutout</string>
    <string name="prefsShowBelowNotchSummary">Display progress bar below any notch or hole punch</string>
    <string name="prefsStatusBarHeightInfo">Your status bar is %d pixels tall</string>
    <string name="prefsCircularProgressBarTweaks">Circular progress bar tweaks</string>
    <string name="prefsProgressBarOffsetTitle">Horizontal offset</string>
    <string name="prefsProgressBarOffsetSummary">Change progress bar alignment</string>
    <string name="prefsCutoutSizeTitle">Cutout size</string>
    <string name="prefsCutoutSizeSummary">Change size of the inner diameter</string>
    <string name="prefsProgressBarMarginTopTitle">Margin from the top</string>
    <string name="prefsProgressBarMarginTopSummary">Change spacing from top of the screen</string>
    <string name="prefsCircularBarShareConfigTitle">Perfect fit? Share configuration</string>
    <string name="prefsCircularBarShareConfigSummary">Let others with the same device use your configuration</string>
    <string name="prefsMultiScreenInfoCard">For multiple screens, access the settings through each display to adjust the bar for that setup</string>
    <string name="prefsBlackBackgroundTitle">Use black background</string>
    <string name="prefsBlackBackgroundSummary">Switch to progress bar with black track</string>
    <string name="prefsPerAppSettingsTitle">Per-app settings</string>
    <string name="prefsPerAppSettingsSummary">Toggle noti on per-app basis</string>
    <string name="prefsRoundedCornersTitle">Use progress bar with rounded corners</string>
    <string name="prefsRoundedCornersSummary">Switch to progress bar with rounded ends</string>
    <string name="prefsShowInLockScreenTitle">Show progress bar in lock screen</string>
    <string name="prefsShowInLockScreenSummary">Will also show in the always on display</string>
    <string name="prefsDisableInFullScreenTitle">Disable in fullscreen mode</string>
    <string name="prefsDisableInFullScreenSummary">Hide progress bar in fullscreen apps</string>
    <string name="prefsNotificationColorTitle">Match notification color</string>
    <string name="prefsNotificationColorSummary">Use the notification\'s color for the progress bar</string>
    <string name="prefsChangeColorTitle">Change progress bar color</string>
    <string name="prefsCategoryMore">more</string>
    <string name="prefsCalentileTitle">Try %s on Google Play</string>
    <string name="prefsCalentileSummary">Support the developer</string>
    <string name="shareConfigPositiveMessage">Thanks for sharing your configuration!</string>
    <string name="shareConfigNoInternetMessage">Enable internet to upload configuration</string>
    <string name="colorRed">Red</string>
    <string name="colorPink">Pink</string>
    <string name="colorPurple">Purple</string>
    <string name="colorDeepPurple">Deep purple</string>
    <string name="colorIndigo">Indigo</string>
    <string name="colorBlue">Blue</string>
    <string name="colorLightBlue">Light blue</string>
    <string name="colorCyan">Cyan</string>
    <string name="colorTeal">Teal</string>
    <string name="colorGreen">Green</string>
    <string name="colorLightGreen">Light green</string>
    <string name="colorLime">Lime</string>
    <string name="colorYellow">Yellow</string>
    <string name="colorAmber">Amber</string>
    <string name="colorOrange">Orange</string>
    <string name="colorDeepOrange">Deep orange</string>
    <string name="colorBrown">Brown</string>
    <string name="colorBlack">Black</string>
    <string name="colorGrey">Grey</string>
    <string name="colorWhite">White</s
Download .txt
gitextract_91b8wrhl/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── gustavoas/
│           │           └── noti/
│           │               ├── ProgressBarAppsAdapter.kt
│           │               ├── ProgressBarAppsRepository.kt
│           │               ├── SettingsActivity.kt
│           │               ├── Utils.kt
│           │               ├── fragments/
│           │               │   ├── BasePreferenceFragment.kt
│           │               │   ├── CircularBarFragment.kt
│           │               │   ├── LinearBarFragment.kt
│           │               │   ├── PerAppSettingsFragment.kt
│           │               │   └── SettingsFragment.kt
│           │               ├── model/
│           │               │   ├── DeviceConfiguration.kt
│           │               │   ├── ProgressBarApp.kt
│           │               │   └── ProgressNotification.kt
│           │               ├── notifications/
│           │               │   ├── DownloadProgressBar.kt
│           │               │   ├── GoogleTimerProgressBar.kt
│           │               │   ├── MediaProgressBar.kt
│           │               │   ├── PercentageProgressBar.kt
│           │               │   ├── ProgressBarNotification.kt
│           │               │   └── TimedProgressBar.kt
│           │               ├── preferences/
│           │               │   ├── BannerPreference.kt
│           │               │   ├── BarStylesListPreference.kt
│           │               │   └── SeekBarPreference.kt
│           │               └── services/
│           │                   ├── AccessibilityService.kt
│           │                   ├── FullscreenDetectionService.kt
│           │                   └── NotificationListenerService.kt
│           └── res/
│               ├── color/
│               │   └── outlined_button_selector.xml
│               ├── drawable/
│               │   ├── circular_mask.xml
│               │   ├── ic_accessibility.xml
│               │   ├── ic_apps.xml
│               │   ├── ic_bug_report.xml
│               │   ├── ic_calentile.xml
│               │   ├── ic_circular_progress_bar.xml
│               │   ├── ic_close.xml
│               │   ├── ic_colors.xml
│               │   ├── ic_contrast.xml
│               │   ├── ic_download.xml
│               │   ├── ic_download_filled.xml
│               │   ├── ic_fullscreen.xml
│               │   ├── ic_height.xml
│               │   ├── ic_horizontal.xml
│               │   ├── ic_info.xml
│               │   ├── ic_launcher_foreground.xml
│               │   ├── ic_linear_progress_bar.xml
│               │   ├── ic_lockscreen.xml
│               │   ├── ic_margin_top.xml
│               │   ├── ic_music.xml
│               │   ├── ic_music_filled.xml
│               │   ├── ic_notification.xml
│               │   ├── ic_notification_bar.xml
│               │   ├── ic_overlay.xml
│               │   ├── ic_palette.xml
│               │   ├── ic_preview.xml
│               │   ├── ic_progress_bar_style.xml
│               │   ├── ic_rounded_corners.xml
│               │   ├── ic_share.xml
│               │   ├── ic_size.xml
│               │   ├── ic_sumup.xml
│               │   ├── layout_bg.xml
│               │   ├── selector_download.xml
│               │   ├── selector_music.xml
│               │   └── splashscreen.xml
│               ├── drawable-v31/
│               │   └── splashscreen.xml
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── advanced_style_dialog.xml
│               │   ├── app_item.xml
│               │   ├── banner_preference.xml
│               │   ├── button_toggle_group.xml
│               │   ├── fragment_per_app_settings.xml
│               │   ├── material_switch.xml
│               │   ├── per_app_settings_footer.xml
│               │   ├── progress_bar.xml
│               │   └── seekbar_preference.xml
│               ├── layout-land/
│               │   └── button_toggle_group.xml
│               ├── menu/
│               │   └── settings_menu.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               ├── resources.properties
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── attrs.xml
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── ic_launcher_background.xml
│               │   ├── strings.xml
│               │   └── themes.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-ar-rSA/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-es-rMX/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-fr-rCA/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ka/
│               │   └── strings.xml
│               ├── values-lt/
│               │   └── strings.xml
│               ├── values-ml/
│               │   └── strings.xml
│               ├── values-nb-rNO/
│               │   └── strings.xml
│               ├── values-night/
│               │   ├── colors.xml
│               │   └── themes.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt-rPT/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sl/
│               │   └── strings.xml
│               ├── values-ta/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-v31/
│               │   └── arrays.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rTW/
│               │   └── strings.xml
│               └── xml/
│                   ├── accessibility_service_config.xml
│                   ├── backup_rules.xml
│                   ├── circular_bar_preferences.xml
│                   ├── data_extraction_rules.xml
│                   ├── linear_bar_preferences.xml
│                   └── preferences.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── renovate.json
└── settings.gradle
Condensed preview — 136 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (392K chars).
[
  {
    "path": ".gitignore",
    "chars": 109,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n"
  },
  {
    "path": "README.md",
    "chars": 1281,
    "preview": "# Noti Progress Bar\n\n[<img alt=\"banner\" src=\"Images/Banner.png\" />](https://play.google.com/store/apps/details?id=com.gu"
  },
  {
    "path": "app/.gitignore",
    "chars": 51,
    "preview": "/build\n/release/\n/src/debug/\n/google-services.json\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 1980,
    "preview": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n    id 'com.google.gms.google-services'"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 750,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 2828,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/ProgressBarAppsAdapter.kt",
    "chars": 5669,
    "preview": "package com.gustavoas.noti\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\ni"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/ProgressBarAppsRepository.kt",
    "chars": 7945,
    "preview": "package com.gustavoas.noti\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport android."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/SettingsActivity.kt",
    "chars": 16889,
    "preview": "package com.gustavoas.noti\n\nimport android.content.Intent\nimport android.content.SharedPreferences\nimport android.net.Ur"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/Utils.kt",
    "chars": 12502,
    "preview": "package com.gustavoas.noti\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.p"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/fragments/BasePreferenceFragment.kt",
    "chars": 557,
    "preview": "package com.gustavoas.noti.fragments\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.preference.Prefe"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/fragments/CircularBarFragment.kt",
    "chars": 1269,
    "preview": "package com.gustavoas.noti.fragments\n\nimport android.content.SharedPreferences\nimport android.os.Bundle\nimport androidx."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/fragments/LinearBarFragment.kt",
    "chars": 2199,
    "preview": "package com.gustavoas.noti.fragments\n\nimport android.content.SharedPreferences\nimport android.os.Build\nimport android.os"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/fragments/PerAppSettingsFragment.kt",
    "chars": 5009,
    "preview": "package com.gustavoas.noti.fragments\n\nimport android.os.Build\nimport android.os.Build.VERSION_CODES\nimport android.os.Bu"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/fragments/SettingsFragment.kt",
    "chars": 12375,
    "preview": "package com.gustavoas.noti.fragments\n\nimport android.content.ComponentName\nimport android.content.Intent\nimport android."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/model/DeviceConfiguration.kt",
    "chars": 324,
    "preview": "package com.gustavoas.noti.model\n\ndata class DeviceConfiguration (\n    var configuration: String? = null,\n    var device"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/model/ProgressBarApp.kt",
    "chars": 335,
    "preview": "package com.gustavoas.noti.model\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ndata class"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/model/ProgressNotification.kt",
    "chars": 243,
    "preview": "package com.gustavoas.noti.model\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ndata class"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/DownloadProgressBar.kt",
    "chars": 1427,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.app.Notification\nimport android.content.Context\nimport android."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/GoogleTimerProgressBar.kt",
    "chars": 1532,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.content.Context\nimport android.service.notification.StatusBarNo"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/MediaProgressBar.kt",
    "chars": 3439,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.content.Context\nimport android.media.MediaMetadata\nimport andro"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/PercentageProgressBar.kt",
    "chars": 2522,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.app.Notification\nimport android.content.Context\nimport android."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/ProgressBarNotification.kt",
    "chars": 4735,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.app.Notification\nimport android.content.Context\nimport android."
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/notifications/TimedProgressBar.kt",
    "chars": 1982,
    "preview": "package com.gustavoas.noti.notifications\n\nimport android.content.Context\nimport android.os.Handler\nimport android.os.Loo"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/preferences/BannerPreference.kt",
    "chars": 1226,
    "preview": "package com.gustavoas.noti.preferences\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.w"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/preferences/BarStylesListPreference.kt",
    "chars": 4585,
    "preview": "package com.gustavoas.noti.preferences\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.v"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/preferences/SeekBarPreference.kt",
    "chars": 1820,
    "preview": "package com.gustavoas.noti.preferences\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.v"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/services/AccessibilityService.kt",
    "chars": 19342,
    "preview": "package com.gustavoas.noti.services\n\nimport android.accessibilityservice.AccessibilityService\nimport android.animation.O"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/services/FullscreenDetectionService.kt",
    "chars": 4832,
    "preview": "package com.gustavoas.noti.services\n\nimport android.app.KeyguardManager\nimport android.app.Service\nimport android.conten"
  },
  {
    "path": "app/src/main/java/com/gustavoas/noti/services/NotificationListenerService.kt",
    "chars": 6059,
    "preview": "package com.gustavoas.noti.services\n\nimport android.app.Notification\nimport android.os.Process\nimport android.service.no"
  },
  {
    "path": "app/src/main/res/color/outlined_button_selector.xml",
    "chars": 262,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "app/src/main/res/drawable/circular_mask.xml",
    "chars": 338,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:co"
  },
  {
    "path": "app/src/main/res/drawable/ic_accessibility.xml",
    "chars": 651,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_apps.xml",
    "chars": 1541,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_bug_report.xml",
    "chars": 760,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"48dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_calentile.xml",
    "chars": 1294,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:height=\"28dp\"\n    android:width=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_circular_progress_bar.xml",
    "chars": 613,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_close.xml",
    "chars": 413,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_colors.xml",
    "chars": 485,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_contrast.xml",
    "chars": 676,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_download.xml",
    "chars": 802,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_download_filled.xml",
    "chars": 665,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_fullscreen.xml",
    "chars": 536,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_height.xml",
    "chars": 429,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"30dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_horizontal.xml",
    "chars": 469,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_info.xml",
    "chars": 895,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 1778,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_linear_progress_bar.xml",
    "chars": 466,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_lockscreen.xml",
    "chars": 665,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_margin_top.xml",
    "chars": 781,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_music.xml",
    "chars": 464,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_music_filled.xml",
    "chars": 392,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_notification.xml",
    "chars": 863,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"27dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_notification_bar.xml",
    "chars": 390,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"27dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_overlay.xml",
    "chars": 420,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_palette.xml",
    "chars": 1464,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_preview.xml",
    "chars": 975,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"48dp\"\n    android:height=\"48dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_progress_bar_style.xml",
    "chars": 623,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_rounded_corners.xml",
    "chars": 858,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_share.xml",
    "chars": 858,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_size.xml",
    "chars": 632,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/ic_sumup.xml",
    "chars": 1182,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"28dp\"\n    android:height=\"28dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/layout_bg.xml",
    "chars": 213,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners a"
  },
  {
    "path": "app/src/main/res/drawable/selector_download.xml",
    "chars": 262,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "app/src/main/res/drawable/selector_music.xml",
    "chars": 256,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "app/src/main/res/drawable/splashscreen.xml",
    "chars": 1567,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"30dp\"\n    android:height=\"30dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable-v31/splashscreen.xml",
    "chars": 3373,
    "preview": "<animated-vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.c"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 2463,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    xmlns:android=\"http://sc"
  },
  {
    "path": "app/src/main/res/layout/advanced_style_dialog.xml",
    "chars": 2013,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView\n    xmlns:android=\"http://schemas.android."
  },
  {
    "path": "app/src/main/res/layout/app_item.xml",
    "chars": 1578,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    "
  },
  {
    "path": "app/src/main/res/layout/banner_preference.xml",
    "chars": 2090,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.google.android.material.card.MaterialCardView\n    xmlns:android=\"http://sche"
  },
  {
    "path": "app/src/main/res/layout/button_toggle_group.xml",
    "chars": 3133,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://sche"
  },
  {
    "path": "app/src/main/res/layout/fragment_per_app_settings.xml",
    "chars": 1024,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    "
  },
  {
    "path": "app/src/main/res/layout/material_switch.xml",
    "chars": 357,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!-- https://stackoverflow.com/a/76270479 -->\n<com.google.android.material.mater"
  },
  {
    "path": "app/src/main/res/layout/per_app_settings_footer.xml",
    "chars": 924,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/progress_bar.xml",
    "chars": 2008,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "app/src/main/res/layout/seekbar_preference.xml",
    "chars": 2193,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    "
  },
  {
    "path": "app/src/main/res/layout-land/button_toggle_group.xml",
    "chars": 3127,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://sche"
  },
  {
    "path": "app/src/main/res/menu/settings_menu.xml",
    "chars": 376,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"h"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 337,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 337,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/resources.properties",
    "chars": 23,
    "preview": "unqualifiedResLocale=en"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "chars": 1900,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"progressBarStyle\">\n        <item>@string/pref"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "chars": 298,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"HorizontalNumberPicker\">\n        <attr n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 380,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"deep_"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 116,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"progress_bar_max\">10000</integer>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/ic_launcher_background.xml",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">@color/deep_purple_100</colo"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 6497,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\" translatable=\"false\">Noti Progress Bar</s"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 1805,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <style name=\"Them"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "chars": 4989,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">تقرير الأخطاء</string>\n    <string n"
  },
  {
    "path": "app/src/main/res/values-ar-rSA/strings.xml",
    "chars": 4481,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">تقرير الأخطاء</string>\n    <string n"
  },
  {
    "path": "app/src/main/res/values-bg/strings.xml",
    "chars": 4733,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Докладвай за грешка</string>\n    <st"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "chars": 6318,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Nahlásit chybu</string>\n    <string "
  },
  {
    "path": "app/src/main/res/values-de/strings.xml",
    "chars": 6586,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"notificationListenerDescription\">Liest Benachrichti"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "chars": 6543,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"perAppSettingsEmptyView\">Aún no se han encontrado b"
  },
  {
    "path": "app/src/main/res/values-es-rMX/strings.xml",
    "chars": 4779,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Informar error</string>\n    <string "
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "chars": 7044,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"colorWhite\">Blanc</string>\n    <string name=\"reset\""
  },
  {
    "path": "app/src/main/res/values-fr-rCA/strings.xml",
    "chars": 62,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-hu/strings.xml",
    "chars": 1902,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Hiba jelentése</string>\n    <string "
  },
  {
    "path": "app/src/main/res/values-in/strings.xml",
    "chars": 3886,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Pelaporan Bug</string>\n    <string n"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "chars": 5169,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"prefsPerAppSettingsSummary\">Attiva noti in base all"
  },
  {
    "path": "app/src/main/res/values-iw/strings.xml",
    "chars": 4372,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">דווח על באג</string>\n    <string nam"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "chars": 5015,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"notificationListenerDescription\">プログレスバー付きの通知を読み取りま"
  },
  {
    "path": "app/src/main/res/values-ka/strings.xml",
    "chars": 2314,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"reset\">საწყის პარამეტრებზე დაბრუნება</string>\n    <"
  },
  {
    "path": "app/src/main/res/values-lt/strings.xml",
    "chars": 62,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>"
  },
  {
    "path": "app/src/main/res/values-ml/strings.xml",
    "chars": 6828,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">റിപ്പോർട്ട് ബഗ്ഗ്‌</string>\n    <str"
  },
  {
    "path": "app/src/main/res/values-nb-rNO/strings.xml",
    "chars": 4642,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"colorOrange\">Oransje</string>\n    <string name=\"col"
  },
  {
    "path": "app/src/main/res/values-night/colors.xml",
    "chars": 233,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"text\">#FFFFFF</color>\n    <color name=\"system_accent"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "chars": 559,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <style name=\"Theme"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "chars": 6224,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Zgłoś błąd</string>\n    <string name"
  },
  {
    "path": "app/src/main/res/values-pt-rPT/strings.xml",
    "chars": 6683,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Reportar Erro</string>\n    <string n"
  },
  {
    "path": "app/src/main/res/values-ro/strings.xml",
    "chars": 5074,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"reset\">Resetează</string>\n    <string name=\"perAppS"
  },
  {
    "path": "app/src/main/res/values-ru/strings.xml",
    "chars": 181,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"settingsPreviewFab\">Предпросмотр</string>\n    <stri"
  },
  {
    "path": "app/src/main/res/values-sk/strings.xml",
    "chars": 2679,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"settingsPreviewFab\">Náhľad</string>\n    <string nam"
  },
  {
    "path": "app/src/main/res/values-sl/strings.xml",
    "chars": 993,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Prijavi napako</string>\n    <string "
  },
  {
    "path": "app/src/main/res/values-ta/strings.xml",
    "chars": 6365,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"prefsNotificationPermissionSummary\">முன்னேற்றப் பட்"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "chars": 5278,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Hata Bildir</string>\n    <string nam"
  },
  {
    "path": "app/src/main/res/values-uk/strings.xml",
    "chars": 6794,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"notificationListenerDescription\">Читає сповіщення з"
  },
  {
    "path": "app/src/main/res/values-v31/arrays.xml",
    "chars": 1522,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"colorsArray\">\n        <item>@string/colorRed<"
  },
  {
    "path": "app/src/main/res/values-vi/strings.xml",
    "chars": 4637,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">Báo cáo lỗi</string>\n    <string nam"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "chars": 4020,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"settingsPreviewFab\">预览</string>\n    <string name=\"p"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "chars": 4669,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"menuReportBug\">回報錯誤</string>\n    <string name=\"sett"
  },
  {
    "path": "app/src/main/res/xml/accessibility_service_config.xml",
    "chars": 196,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<accessibility-service\n    xmlns:android=\"http://schemas.android.com/apk/res/andr"
  },
  {
    "path": "app/src/main/res/xml/backup_rules.xml",
    "chars": 116,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<full-backup-content>\n    <include domain=\"database\"/>\n</full-backup-content>"
  },
  {
    "path": "app/src/main/res/xml/circular_bar_preferences.xml",
    "chars": 2681,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
  },
  {
    "path": "app/src/main/res/xml/data_extraction_rules.xml",
    "chars": 208,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<data-extraction-rules>\n    <device-transfer>\n        <include domain=\"database\"/"
  },
  {
    "path": "app/src/main/res/xml/linear_bar_preferences.xml",
    "chars": 1804,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
  },
  {
    "path": "app/src/main/res/xml/preferences.xml",
    "chars": 6150,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
  },
  {
    "path": "build.gradle",
    "chars": 364,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'co"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 234,
    "preview": "#Tue Jul 18 16:04:19 WEST 2023\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\:/"
  },
  {
    "path": "gradle.properties",
    "chars": 1358,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5766,
    "preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
  },
  {
    "path": "gradlew.bat",
    "chars": 2674,
    "preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": "renovate.json",
    "chars": 114,
    "preview": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:recommended\"\n  ]\n}\n"
  },
  {
    "path": "settings.gradle",
    "chars": 377,
    "preview": "pluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\ndepen"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the GustavoASantos/Noti GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 136 files (337.8 KB), approximately 93.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!