[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea\n.DS_Store\n/build\n/captures\n/keystore\n.externalNativeBuild\n.cxx\nlocal.properties\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n}\n\ndef keystoreProps = new Properties()\ndef keystorePropsFile = rootProject.file('keystore/r0s.properties')\nif (keystorePropsFile.exists()) {\n    keystoreProps.load(new FileInputStream(keystorePropsFile))\n}\n\nandroid {\n    namespace 'com.rosan.accounts'\n    compileSdk 33\n\n    defaultConfig {\n        applicationId \"com.rosan.accounts\"\n        minSdk 21\n        targetSdk 33\n        versionCode 6\n        versionName \"1.5\"\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        vectorDrawables {\n            useSupportLibrary true\n        }\n    }\n\n    signingConfigs {\n        debug {\n            keyAlias keystoreProps['keyAlias']\n            keyPassword keystoreProps['keyPassword']\n            storeFile keystoreProps['storeFile'] ? file(keystoreProps['storeFile']) : null\n            storePassword keystoreProps['storePassword']\n            v1SigningEnabled true\n            v2SigningEnabled true\n        }\n\n        release {\n            keyAlias keystoreProps['keyAlias']\n            storePassword keystoreProps['storePassword']\n            keyPassword keystoreProps['keyPassword']\n            storeFile keystoreProps['storeFile'] ? file(new File(keystoreProps['storeFile'])) : null\n            v1SigningEnabled true\n            v2SigningEnabled true\n        }\n    }\n\n    buildTypes {\n        debug {\n            signingConfig signingConfigs.debug\n            minifyEnabled false\n            zipAlignEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n\n        release {\n            signingConfig signingConfigs.release\n            minifyEnabled true\n            zipAlignEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n    buildFeatures {\n        buildConfig true\n        compose true\n    }\n    composeOptions {\n        kotlinCompilerExtensionVersion '1.4.5'\n    }\n    packagingOptions {\n        resources {\n            excludes += '/META-INF/{AL2.0,LGPL2.1}'\n        }\n    }\n}\n\ndependencies {\n    implementation 'androidx.core:core-ktx:1.10.1'\n    implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')\n    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'\n    implementation 'androidx.activity:activity-compose:1.7.2'\n    implementation platform('androidx.compose:compose-bom:2022.10.00')\n    implementation 'androidx.compose.ui:ui'\n    implementation 'androidx.compose.ui:ui-graphics'\n    implementation 'androidx.compose.ui:ui-tooling-preview'\n    implementation 'androidx.compose.material:material'\n    implementation 'androidx.compose.material:material-icons-extended'\n    implementation 'androidx.compose.material3:material3'\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.5'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'\n    androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00')\n    androidTestImplementation 'androidx.compose.ui:ui-test-junit4'\n    debugImplementation 'androidx.compose.ui:ui-tooling'\n    debugImplementation 'androidx.compose.ui:ui-test-manifest'\n\n    compileOnly project(':hidden-api')\n\n    implementation 'androidx.navigation:navigation-compose:2.6.0'\n\n    implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'\n\n    def accompanist_version = '0.30.1'\n    implementation \"com.google.accompanist:accompanist-drawablepainter:$accompanist_version\"\n    implementation \"com.google.accompanist:accompanist-navigation-animation:$accompanist_version\"\n    implementation \"com.google.accompanist:accompanist-insets:$accompanist_version\"\n    implementation \"com.google.accompanist:accompanist-insets-ui:$accompanist_version\"\n    implementation \"com.google.accompanist:accompanist-systemuicontroller:$accompanist_version\"\n\n    implementation 'io.insert-koin:koin-core:3.4.2'\n    implementation 'io.insert-koin:koin-android:3.4.2'\n    implementation 'io.insert-koin:koin-androidx-compose:3.4.5'\n\n    def shizuku_version = \"13.1.4\"\n    implementation \"dev.rikka.shizuku:api:$shizuku_version\"\n    implementation \"dev.rikka.shizuku:provider:$shizuku_version\"\n}"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n\n-keep public interface ** extends android.os.IInterface {*;}\n-keep public class ** extends android.app.Activity\n\n-dontwarn **\n"
  },
  {
    "path": "app/src/androidTest/java/com/rosan/accounts/ExampleInstrumentedTest.kt",
    "content": "package com.rosan.accounts\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.rosan.accounts\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-permission\n        android:name=\"android.permission.GET_ACCOUNTS\"\n        android:maxSdkVersion=\"22\" />\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n\n    <uses-sdk tools:overrideLibrary=\"rikka.shizuku.api, rikka.shizuku.provider, rikka.shizuku.shared, rikka.shizuku.aidl\" />\n\n    <application\n        android:name=\".App\"\n        android:allowBackup=\"true\"\n        android:icon=\"@android:drawable/sym_def_app_icon\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@android:drawable/sym_def_app_icon\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.Accounts\">\n        <activity\n            android:name=\".ui.activity.MainActivity\"\n            android:exported=\"true\"\n            android:theme=\"@style/Theme.Accounts\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <provider\n            android:name=\"rikka.shizuku.ShizukuProvider\"\n            android:authorities=\"${applicationId}.shizuku\"\n            android:enabled=\"true\"\n            android:exported=\"true\"\n            android:multiprocess=\"false\"\n            android:permission=\"android.permission.INTERACT_ACROSS_USERS_FULL\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/App.kt",
    "content": "package com.rosan.accounts\n\nimport android.app.Application\nimport android.os.Build\nimport com.rosan.accounts.di.init.appModules\nimport org.koin.android.ext.koin.androidContext\nimport org.koin.android.ext.koin.androidLogger\nimport org.koin.core.context.startKoin\nimport org.lsposed.hiddenapibypass.HiddenApiBypass\n\nclass App : Application() {\n    override fun onCreate() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)\n            HiddenApiBypass.addHiddenApiExemptions(\"\")\n        super.onCreate()\n        startKoin {\n            androidLogger()\n            androidContext(this@App)\n            modules(appModules)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/cause/ShizukuNotWorkException.kt",
    "content": "package com.rosan.accounts.data.common.cause\n\ndata class ShizukuNotWorkException(\n    override val message: String? = null,\n    override val cause: Throwable? = null\n) : RuntimeException(message, cause)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/BinderUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport android.os.Binder\nimport android.os.IBinder\nimport android.os.Parcel\nimport android.os.ParcelFileDescriptor\nimport java.io.FileDescriptor\n\nfun IBinder.transactDump(fd: FileDescriptor, args: Array<String>? = null) {\n    val data = Parcel.obtain()\n    val reply = Parcel.obtain()\n    try {\n        data.writeFileDescriptor(fd)\n        data.writeStringArray(args)\n        this.transact(Binder.DUMP_TRANSACTION, data, reply, 0)\n        reply.readException()\n    } finally {\n        data.recycle()\n        reply.recycle()\n    }\n}\n\nfun IBinder.dumpBytes(args: Array<String>? = null): ByteArray {\n    val pipe = ParcelFileDescriptor.createPipe()\n    val readFD = pipe[0]\n    val writeFD = pipe[1]\n    writeFD.use {\n        this.transactDump(it.fileDescriptor, args)\n    }\n    return readFD.use {\n        ParcelFileDescriptor.AutoCloseInputStream(it)\n            .readBytes()\n    }\n}\n\nfun IBinder.dumpText(args: Array<String>? = null): String =\n    dumpBytes(args).decodeToString()"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/CauseUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport android.content.Context\nimport com.rosan.accounts.R\nimport com.rosan.accounts.data.common.cause.ShizukuNotWorkException\n\nfun Throwable.help(): String? {\n    val context = defaultKoin().get<Context>()\n    return when (this) {\n        is ShizukuNotWorkException -> context.getString(R.string.shizuku_not_working)\n        else -> null\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/ContextUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Handler\nimport android.os.Looper\nimport android.widget.Toast\nimport androidx.annotation.StringRes\n\nfun runOnUiThread(action: () -> Unit) {\n    Handler(Looper.getMainLooper()).post {\n        action.invoke()\n    }\n}\n\nfun Context.copy(text: CharSequence) {\n    runOnUiThread {\n        val manager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        manager.setPrimaryClip(ClipData.newPlainText(\"Label\", text))\n    }\n}\n\nfun Context.openUrlInBrowser(url: String) {\n    runOnUiThread {\n        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))\n            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n        startActivity(intent)\n    }\n}\n\nfun Context.toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {\n    runOnUiThread {\n        Toast.makeText(this, text, duration).show()\n    }\n}\n\nfun Context.toast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT) {\n    runOnUiThread {\n        toast(getString(resId), duration)\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/KoinUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport org.koin.core.Koin\nimport org.koin.mp.KoinPlatformTools\n\nfun defaultKoin(): Koin = KoinPlatformTools.defaultContext().get()"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/MapsUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\ninline fun <K, V> MutableMap<K, V>.replace(key: K, action: (value: V?) -> V): V {\n    val newValue = this[key].let(action)\n    this[key] = newValue\n    return newValue\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/ProcessUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nfun Process.isActive(): Boolean {\n    return try {\n        exitValue()\n        false\n    } catch (e: IllegalArgumentException) {\n        true\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/ShizukuUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.IBinder\nimport android.os.ServiceManager\nimport com.rosan.accounts.data.common.cause.ShizukuNotWorkException\nimport kotlinx.coroutines.channels.awaitClose\nimport kotlinx.coroutines.flow.callbackFlow\nimport kotlinx.coroutines.flow.catch\nimport kotlinx.coroutines.flow.first\nimport rikka.shizuku.Shizuku\nimport rikka.shizuku.ShizukuBinderWrapper\nimport rikka.sui.Sui\n\nprivate suspend fun blockingRequestShizukuPermission() = callbackFlow {\n    val requestCode = (Int.MIN_VALUE..Int.MAX_VALUE).random()\n    val listener =\n        Shizuku.OnRequestPermissionResultListener { _requestCode, grantResult ->\n            if (_requestCode != requestCode) return@OnRequestPermissionResultListener\n            if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED)\n                trySend(Unit)\n            else close(ShizukuNotWorkException(\"sui/shizuku permission denied\"))\n        }\n    Shizuku.addRequestPermissionResultListener(listener)\n    Shizuku.requestPermission(requestCode)\n    awaitClose { Shizuku.removeRequestPermissionResultListener(listener) }\n}.catch {\n    throw if (it !is ShizukuNotWorkException) ShizukuNotWorkException(cause = it)\n    else it\n}.first()\n\nsuspend fun <T> requireShizukuPermissionGranted(context: Context, action: suspend () -> T): T {\n    Sui.init(context.packageName)\n    val binder = Shizuku.getBinder()\n    if (binder == null || !binder.pingBinder())\n        throw ShizukuNotWorkException(\"sui/shizuku isn't activated\")\n    if (Shizuku.checkSelfPermission() != PackageManager.PERMISSION_GRANTED)\n        blockingRequestShizukuPermission()\n    return action()\n}\n\nfun shizukuBinder(name: String): IBinder =\n    shizukuBinder(ServiceManager.getService(name))\n\nfun shizukuBinder(binder: IBinder): IBinder = ShizukuBinderWrapper(binder)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/common/utils/UserHandleUtil.kt",
    "content": "package com.rosan.accounts.data.common.utils\n\nimport android.os.UserHandle\n\nval UserHandle.id: Int\n    get() = this.hashCode()"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/service/entity/AccountAuthenticatorEntity.kt",
    "content": "package com.rosan.accounts.data.service.entity\n\nimport android.graphics.drawable.Drawable\n\ndata class AccountAuthenticatorEntity(\n    val userId: Int,\n    val type: String,\n    val packageName: String,\n    val label: String,\n    val icon: Drawable\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/service/entity/AccountEntity.kt",
    "content": "package com.rosan.accounts.data.service.entity\n\ndata class AccountEntity(\n    val userId: Int,\n    val type: String,\n    val name: String\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/service/entity/UserEntity.kt",
    "content": "package com.rosan.accounts.data.service.entity\n\ndata class UserEntity(\n    val id: Int,\n    val name: String?\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/service/model/ShizukuUserService.kt",
    "content": "package com.rosan.accounts.data.service.model\n\nimport android.accounts.IAccountManager\nimport android.content.Context\nimport android.content.pm.IPackageManager\nimport android.os.Build\nimport android.os.IUserManager\nimport android.util.Log\nimport com.rosan.accounts.data.common.utils.dumpText\nimport com.rosan.accounts.data.common.utils.requireShizukuPermissionGranted\nimport com.rosan.accounts.data.common.utils.shizukuBinder\nimport com.rosan.accounts.data.service.entity.AccountAuthenticatorEntity\nimport com.rosan.accounts.data.service.entity.AccountEntity\nimport com.rosan.accounts.data.service.entity.UserEntity\nimport com.rosan.accounts.data.service.repo.UserService\nimport rikka.shizuku.Shizuku\n\nclass ShizukuUserService(private val context: Context) : UserService {\n    private val basePackageManager by lazy { context.packageManager }\n\n    private val userManager by lazy { IUserManager.Stub.asInterface(shizukuBinder(Context.USER_SERVICE)) }\n\n    private val accountManager by lazy { IAccountManager.Stub.asInterface(shizukuBinder(Context.ACCOUNT_SERVICE)) }\n\n    private val packageManager by lazy { IPackageManager.Stub.asInterface(shizukuBinder(\"package\")) }\n    override suspend fun removeUser(user: UserEntity): Boolean = removeUser(user.id)\n\n    override suspend fun removeUser(userId: Int): Boolean = userManager.removeUser(userId)\n\n    override suspend fun getUsers(): List<UserEntity> = requireShizukuPermissionGranted(context) {\n        (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)\n            userManager.getUsers(false, false, false)\n        else userManager.getUsers(false)).map {\n            UserEntity(id = it.id, name = it.name)\n        }\n    }\n\n    override suspend fun getAccountAuthenticators(userId: Int): List<AccountAuthenticatorEntity> =\n        requireShizukuPermissionGranted(context) {\n            accountManager.getAuthenticatorTypes(userId).map { description ->\n                val packageName = description.packageName\n                val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)\n                    packageManager.getPackageInfo(packageName, 0L, userId)\n                else packageManager.getPackageInfo(packageName, 0, userId)\n                val applicationInfo = packageInfo.applicationInfo\n\n                val name =\n                    basePackageManager.getText(packageName, description.labelId, applicationInfo)\n                val label = basePackageManager.getApplicationLabel(applicationInfo).toString().let {\n                    if (it == name) it\n                    else \"$it - $name\"\n                }\n\n                val icon = basePackageManager.getApplicationIcon(applicationInfo)\n\n                AccountAuthenticatorEntity(\n                    userId = userId,\n                    type = description.type,\n                    packageName = description.packageName,\n                    label = label,\n                    icon = icon\n                )\n            }\n        }\n\n    override suspend fun getAccounts(userId: Int): List<AccountEntity> =\n        requireShizukuPermissionGranted(context) {\n            if (Shizuku.getUid() == 0) getAccountsByManager(userId)\n            else getAccountsByDump(userId)\n        }\n\n    private fun getAccountsByManager(userId: Int): List<AccountEntity> {\n        return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            val packageName =\n                basePackageManager.getPackagesForUid(Shizuku.getUid())?.firstOrNull() ?: \"android\"\n            accountManager.getAccountsAsUser(null, userId, packageName)\n        } else accountManager.getAccountsAsUser(null, userId)).map {\n            AccountEntity(\n                userId = userId,\n                type = it.type,\n                name = it.name\n            )\n        }\n    }\n\n    private fun getAccountsByDump(userId: Int): List<AccountEntity> {\n        val text = accountManager.asBinder().dumpText()\n        fun getUserIds() =\n            \"User UserInfo\\\\{(\\\\d+):.*?\\\\}\".toRegex()\n                .findAll(text)\n                .toList()\n                .map {\n                    it.groupValues[1].toInt()\n                }\n\n        fun getLengthsOfAccounts() =\n            \"Accounts: (\\\\d+)\".toRegex()\n                .findAll(text)\n                .toList()\n                .map {\n                    it.groupValues[1].toInt()\n                }\n\n        val userIds = getUserIds()\n        val index = userIds.indexOf(userId)\n\n        val lengthsOfAccounts = getLengthsOfAccounts()\n        val skipLength = lengthsOfAccounts.slice(0 until index).fold(0) { cur, len ->\n            cur + len\n        }\n        val length = lengthsOfAccounts[index]\n\n        return \"Account \\\\{name=(.*), type=(.*)\\\\}\".toRegex()\n            .findAll(text)\n            .toList()\n            .slice(skipLength until skipLength + length)\n            .map {\n                val name = it.groupValues[1]\n                val type = it.groupValues[2]\n\n                AccountEntity(\n                    userId = userId,\n                    type = type,\n                    name = name\n                )\n            }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/data/service/repo/UserService.kt",
    "content": "package com.rosan.accounts.data.service.repo\n\nimport com.rosan.accounts.data.service.entity.AccountAuthenticatorEntity\nimport com.rosan.accounts.data.service.entity.AccountEntity\nimport com.rosan.accounts.data.service.entity.UserEntity\n\ninterface UserService {\n    suspend fun removeUser(user: UserEntity): Boolean\n\n    suspend fun removeUser(userId: Int): Boolean\n\n    suspend fun getUsers(): List<UserEntity>\n\n    suspend fun getAccountAuthenticators(userId: Int): List<AccountAuthenticatorEntity>\n\n    suspend fun getAccounts(userId: Int): List<AccountEntity>\n}\n"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/di/init/app_modules.kt",
    "content": "package com.rosan.accounts.di.init\n\nimport com.rosan.accounts.di.serviceModule\nimport com.rosan.accounts.di.viewModelModule\n\nval appModules = listOf(\n    viewModelModule,\n    serviceModule\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/di/service_module.kt",
    "content": "package com.rosan.accounts.di\n\nimport com.rosan.accounts.data.service.model.ShizukuUserService\nimport com.rosan.accounts.data.service.repo.UserService\nimport org.koin.android.ext.koin.androidContext\nimport org.koin.dsl.module\n\nval serviceModule = module {\n    single<UserService> {\n        ShizukuUserService(androidContext())\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/di/viewmodel_module.kt",
    "content": "package com.rosan.accounts.di\n\nimport com.rosan.accounts.ui.page.account_manager.AccountManagerViewModel\nimport com.rosan.accounts.ui.page.user_manager.UserManagerViewModel\nimport org.koin.androidx.viewmodel.dsl.viewModel\nimport org.koin.dsl.module\n\nval viewModelModule = module {\n    viewModel {\n        UserManagerViewModel()\n    }\n\n    viewModel {\n        AccountManagerViewModel(get())\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/activity/MainActivity.kt",
    "content": "package com.rosan.accounts.ui.activity\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.ui.Modifier\nimport com.rosan.accounts.ui.page.main.MainPage\nimport com.rosan.accounts.ui.theme.AccountsTheme\n\nclass MainActivity : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccountsTheme {\n                Surface(\n                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background\n                ) {\n                    MainPage()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/account_manager/AccountManagerPage.kt",
    "content": "package com.rosan.accounts.ui.page.account_manager\n\nimport androidx.compose.animation.AnimatedContent\nimport androidx.compose.animation.ExperimentalAnimationApi\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.spring\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.WindowInsets\nimport androidx.compose.foundation.layout.displayCutout\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.size\nimport androidx.compose.foundation.layout.windowInsetsPadding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.twotone.ContentCopy\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.IconButton\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedCard\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.SideEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.graphicsLayer\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport androidx.navigation.NavController\nimport com.google.accompanist.drawablepainter.rememberDrawablePainter\nimport com.rosan.accounts.R\nimport com.rosan.accounts.data.common.utils.copy\nimport com.rosan.accounts.data.common.utils.toast\nimport org.json.JSONArray\nimport org.koin.androidx.compose.getViewModel\nimport org.koin.core.parameter.parametersOf\n\n@OptIn(\n    ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class,\n    ExperimentalFoundationApi::class\n)\n@Composable\nfun AccountManagerPage(\n    userId: Int,\n    navController: NavController,\n    viewModel: AccountManagerViewModel = getViewModel {\n        parametersOf(userId)\n    }\n) {\n    SideEffect {\n        viewModel.dispatch(AccountManagerViewAction.Load)\n    }\n\n    val context = LocalContext.current\n\n    Scaffold(\n        modifier = Modifier\n            .fillMaxSize()\n            .windowInsetsPadding(WindowInsets.displayCutout),\n        topBar = {\n            TopAppBar(\n                title = {\n                    Text(stringResource(R.string.account_manager))\n                },\n                actions = {\n                    IconButton(onClick = {\n                        val array = JSONArray()\n                        viewModel.state.authenticators.forEach {\n                            array.put(it.auth.packageName)\n                        }\n                        context.copy(array.toString())\n                        context.toast(R.string.copied_format_hail)\n                    }) {\n                        Icon(\n                            imageVector = Icons.TwoTone.ContentCopy,\n                            contentDescription = null\n                        )\n                    }\n                }\n            )\n        },\n    ) {\n        AnimatedContent(\n            viewModel.state.authenticators.isEmpty(),\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(it)\n        ) {\n            if (it) {\n                Box(\n                    modifier = Modifier.fillMaxSize()\n                ) {\n                    Text(\n                        stringResource(R.string.account_empty),\n                        modifier = Modifier.align(Alignment.Center)\n                    )\n                }\n            } else {\n                LazyColumn(\n                    modifier = Modifier.fillMaxSize(),\n                    contentPadding = PaddingValues(16.dp),\n                    verticalArrangement = Arrangement.spacedBy(16.dp)\n                ) {\n                    items(viewModel.state.authenticators, key = {\n                        it.auth.type\n                    }) {\n                        var alpha by remember {\n                            mutableStateOf(0f)\n                        }\n                        ItemWidget(\n                            modifier = Modifier\n                                .fillMaxWidth()\n//                                .clip(RoundedCornerShape(8.dp))\n                                .animateItemPlacement()\n                                .graphicsLayer(\n                                    alpha = animateFloatAsState(\n                                        targetValue = alpha,\n                                        animationSpec = spring(stiffness = 100f)\n                                    ).value\n                                ),\n                            authenticator = it\n                        )\n                        SideEffect {\n                            alpha = 1f\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun ItemWidget(\n    modifier: Modifier = Modifier,\n    authenticator: AccountManagerViewState.Authenticator\n) {\n    OutlinedCard(modifier = modifier) {\n        Row(\n            modifier = Modifier\n                .padding(horizontal = 24.dp, vertical = 16.dp),\n            horizontalArrangement = Arrangement.spacedBy(24.dp)\n        ) {\n            Image(\n                modifier = Modifier\n                    .size(32.dp)\n                    .align(Alignment.CenterVertically),\n                painter = rememberDrawablePainter(authenticator.auth.icon),\n                contentDescription = null\n            )\n            Column {\n                Text(authenticator.auth.label, style = MaterialTheme.typography.titleMedium)\n                Text(authenticator.auth.type, style = MaterialTheme.typography.bodyMedium)\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/account_manager/AccountManagerViewAction.kt",
    "content": "package com.rosan.accounts.ui.page.account_manager\n\nsealed class AccountManagerViewAction {\n    object Load : AccountManagerViewAction()\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/account_manager/AccountManagerViewModel.kt",
    "content": "package com.rosan.accounts.ui.page.account_manager\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.setValue\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.rosan.accounts.data.common.utils.replace\nimport com.rosan.accounts.data.service.repo.UserService\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport org.koin.core.component.KoinComponent\nimport org.koin.core.component.inject\n\nclass AccountManagerViewModel(\n    private val userId: Int\n) : ViewModel(), KoinComponent {\n    private val jobs = mutableMapOf<String, Job>()\n\n    private val userService by inject<UserService>()\n\n    var state by mutableStateOf(AccountManagerViewState())\n        private set\n\n    fun dispatch(action: AccountManagerViewAction) {\n        when (action) {\n            AccountManagerViewAction.Load -> load()\n        }\n    }\n\n    private fun load() {\n        jobs.replace(\"load\") {\n            it?.cancel()\n\n            viewModelScope.launch(Dispatchers.IO) {\n                while (true) {\n                    val auths = userService.getAccountAuthenticators(userId)\n                    val accounts = userService.getAccounts(userId)\n                    state = state.copy(\n                        authenticators = auths.map { auth ->\n                            AccountManagerViewState.Authenticator(\n                                auth = auth,\n                                accounts = accounts.filter { auth.type == it.type }\n                            )\n                        }.filter { it.accounts.isNotEmpty() }\n                    )\n                    delay(3000)\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/account_manager/AccountManagerViewState.kt",
    "content": "package com.rosan.accounts.ui.page.account_manager\n\nimport com.rosan.accounts.data.service.entity.AccountAuthenticatorEntity\nimport com.rosan.accounts.data.service.entity.AccountEntity\n\ndata class AccountManagerViewState(\n    val authenticators: List<Authenticator> = emptyList()\n) {\n    data class Authenticator(\n        val auth: AccountAuthenticatorEntity,\n        val accounts: List<AccountEntity>\n    )\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/main/MainPage.kt",
    "content": "package com.rosan.accounts.ui.page.main\n\nimport androidx.compose.animation.AnimatedContentScope\nimport androidx.compose.animation.ExperimentalAnimationApi\nimport androidx.compose.runtime.Composable\nimport androidx.navigation.NavType\nimport androidx.navigation.navArgument\nimport com.google.accompanist.navigation.animation.AnimatedNavHost\nimport com.google.accompanist.navigation.animation.composable\nimport com.google.accompanist.navigation.animation.rememberAnimatedNavController\nimport com.rosan.accounts.ui.page.account_manager.AccountManagerPage\nimport com.rosan.accounts.ui.page.user_manager.UserManagerPage\n\n@OptIn(ExperimentalAnimationApi::class)\n@Composable\nfun MainPage() {\n    val navController = rememberAnimatedNavController()\n\n    AnimatedNavHost(\n        navController = navController,\n        startDestination = MainScreen.UserManager.route\n    ) {\n        composable(route = MainScreen.UserManager.route) {\n            UserManagerPage(\n                navController = navController\n            )\n        }\n        composable(\n            route = MainScreen.AccountManager.route,\n            arguments = listOf(\n                navArgument(\"id\") {\n                    type = NavType.IntType\n                }\n            ),\n            enterTransition = {\n                slideIntoContainer(\n                    AnimatedContentScope.SlideDirection.Up,\n                )\n            },\n            popExitTransition = {\n                slideOutOfContainer(\n                    AnimatedContentScope.SlideDirection.Down,\n                )\n            }\n        ) {\n            val userId = it.arguments?.getInt(\"id\")\n            if (userId == null) {\n                navController.navigateUp()\n                return@composable\n            }\n            AccountManagerPage(\n                userId = userId,\n                navController = navController\n            )\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/main/MainScreen.kt",
    "content": "package com.rosan.accounts.ui.page.main\n\nsealed class MainScreen(val route: String) {\n    object UserManager : MainScreen(\"user\")\n\n    object AccountManager : MainScreen(\"user/{id}/account\") {\n        fun builder(id: Int): String =\n            \"user/$id/account\"\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/user_manager/UserManagerPage.kt",
    "content": "package com.rosan.accounts.ui.page.user_manager\n\nimport android.os.Process\nimport androidx.compose.animation.AnimatedContent\nimport androidx.compose.animation.ExperimentalAnimationApi\nimport androidx.compose.animation.core.animateFloatAsState\nimport androidx.compose.animation.core.spring\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.WindowInsets\nimport androidx.compose.foundation.layout.displayCutout\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.windowInsetsPadding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.twotone.Warning\nimport androidx.compose.material3.AlertDialog\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.OutlinedCard\nimport androidx.compose.material3.Scaffold\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TextButton\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.SideEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.graphicsLayer\nimport androidx.compose.ui.res.stringResource\nimport androidx.compose.ui.unit.dp\nimport androidx.navigation.NavController\nimport com.rosan.accounts.R\nimport com.rosan.accounts.data.common.utils.help\nimport com.rosan.accounts.data.common.utils.id\nimport com.rosan.accounts.data.service.entity.UserEntity\nimport com.rosan.accounts.ui.page.main.MainScreen\nimport org.koin.androidx.compose.getViewModel\n\n@OptIn(\n    ExperimentalMaterial3Api::class,\n    ExperimentalFoundationApi::class, ExperimentalAnimationApi::class\n)\n@Composable\nfun UserManagerPage(\n    navController: NavController,\n    viewModel: UserManagerViewModel = getViewModel()\n) {\n    SideEffect {\n        viewModel.dispatch(UserManagerViewAction.Load)\n    }\n\n    Scaffold(\n        modifier = Modifier\n            .fillMaxSize()\n            .windowInsetsPadding(WindowInsets.displayCutout),\n        topBar = {\n            TopAppBar(\n                title = {\n                    Text(stringResource(R.string.user_manager))\n                }\n            )\n        },\n    ) {\n        AnimatedContent(\n            viewModel.state.cause != null,\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(it)\n        ) {\n            if (it) Box(\n                modifier = Modifier.fillMaxSize()\n            ) {\n                Text(\n                    viewModel.state.cause.let {\n                        it?.help() ?: it?.localizedMessage ?: it?.toString() ?: \"\"\n                    },\n                    modifier = Modifier.align(Alignment.Center)\n                )\n            } else LazyColumn(\n                modifier = Modifier\n                    .fillMaxSize(),\n                contentPadding = PaddingValues(16.dp),\n                verticalArrangement = Arrangement.spacedBy(16.dp)\n            ) {\n                items(viewModel.state.users, key = {\n                    it.id\n                }) {\n                    var alpha by remember {\n                        mutableStateOf(0f)\n                    }\n                    ItemWidget(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .animateItemPlacement()\n                            .graphicsLayer(\n                                alpha = animateFloatAsState(\n                                    targetValue = alpha,\n                                    animationSpec = spring(stiffness = 100f)\n                                ).value\n                            ),\n                        viewModel = viewModel,\n                        navController = navController,\n                        user = it\n                    )\n                    SideEffect {\n                        alpha = 1f\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun ItemWidget(\n    modifier: Modifier = Modifier,\n    viewModel: UserManagerViewModel,\n    navController: NavController,\n    user: UserEntity\n) {\n    OutlinedCard(\n        modifier = modifier\n    ) {\n        Column(\n            modifier = Modifier.padding(8.dp),\n            verticalArrangement = Arrangement.spacedBy(8.dp)\n        ) {\n            Row(\n                modifier = Modifier\n                    .padding(horizontal = 16.dp)\n                    .padding(top = 8.dp),\n                verticalAlignment = Alignment.CenterVertically,\n                horizontalArrangement = Arrangement.spacedBy(8.dp)\n            ) {\n                Text(\n                    user.id.toString(),\n                    color = MaterialTheme.colorScheme.primary,\n                    style = MaterialTheme.typography.titleMedium\n                )\n                Text(\n                    user.name ?: stringResource(R.string.user_name_default),\n                    style = MaterialTheme.typography.titleMedium\n                )\n            }\n            Row(\n                modifier = Modifier\n                    .padding(horizontal = 8.dp),\n                horizontalArrangement = Arrangement.spacedBy(8.dp),\n                verticalAlignment = Alignment.CenterVertically\n            ) {\n                Spacer(modifier = Modifier.weight(1f))\n                TextButton(onClick = {\n                    navController.navigate(MainScreen.AccountManager.builder(user.id))\n                }) {\n                    Text(stringResource(R.string.account_manager))\n                }\n                val curUserId = Process.myUserHandle().id\n                if (curUserId != user.id) {\n                    var showing by remember {\n                        mutableStateOf(false)\n                    }\n                    TextButton(onClick = { showing = true }) {\n                        Text(stringResource(R.string.remove))\n                    }\n                    DeleteUserDialog(\n                        viewModel = viewModel,\n                        showing = showing,\n                        onDismissRequest = {\n                            showing = false\n                        },\n                        user = user\n                    )\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun DeleteUserDialog(\n    viewModel: UserManagerViewModel,\n    showing: Boolean,\n    onDismissRequest: () -> Unit,\n    user: UserEntity\n) {\n    if (!showing) return\n    AlertDialog(onDismissRequest = onDismissRequest, icon = {\n        Icon(imageVector = Icons.TwoTone.Warning, contentDescription = null)\n    }, title = {\n        Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {\n            Text(\n                user.id.toString(),\n                color = MaterialTheme.colorScheme.primary\n            )\n            Text(user.name ?: stringResource(R.string.user_name_default))\n        }\n    }, text = {\n        Text(stringResource(R.string.delete_user_warning))\n    }, confirmButton = {\n        TextButton(onClick = {\n            viewModel.dispatch(UserManagerViewAction.Remove(user))\n            onDismissRequest()\n        }) {\n            Text(stringResource(R.string.delete_user_confirm))\n        }\n    }, dismissButton = {\n        TextButton(onClick = onDismissRequest) {\n            Text(stringResource(R.string.delete_user_cancel))\n        }\n    })\n}\n"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/user_manager/UserManagerViewAction.kt",
    "content": "package com.rosan.accounts.ui.page.user_manager\n\nimport com.rosan.accounts.data.service.entity.UserEntity\n\nsealed class UserManagerViewAction {\n    object Load : UserManagerViewAction()\n\n    data class Remove(val user: UserEntity) : UserManagerViewAction()\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/user_manager/UserManagerViewModel.kt",
    "content": "package com.rosan.accounts.ui.page.user_manager\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.setValue\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport com.rosan.accounts.data.common.utils.replace\nimport com.rosan.accounts.data.service.entity.UserEntity\nimport com.rosan.accounts.data.service.repo.UserService\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.launch\nimport org.koin.core.component.KoinComponent\nimport org.koin.core.component.inject\n\nclass UserManagerViewModel : ViewModel(), KoinComponent {\n    private val jobs = mutableMapOf<String, Job>()\n\n    private val userService by inject<UserService>()\n\n    var state by mutableStateOf(UserManagerViewState())\n        private set\n\n    fun dispatch(action: UserManagerViewAction) {\n        when (action) {\n            UserManagerViewAction.Load -> load()\n            is UserManagerViewAction.Remove -> remove(action.user)\n        }\n    }\n\n    private fun load() {\n        jobs.replace(\"load\") {\n            it?.cancel()\n\n            viewModelScope.launch(Dispatchers.IO) {\n                while (true) {\n                    kotlin.runCatching {\n                        userService.getUsers().sortedBy { it.id }\n                    }.onFailure {\n                        it.printStackTrace()\n                        state = state.copy(cause = it)\n                    }.onSuccess {\n                        state = state.copy(users = it, cause = null)\n                    }\n                    delay(1500)\n                }\n            }\n        }\n    }\n\n    private fun remove(user: UserEntity) {\n        viewModelScope.launch {\n            userService.removeUser(user)\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/page/user_manager/UserManagerViewState.kt",
    "content": "package com.rosan.accounts.ui.page.user_manager\n\nimport com.rosan.accounts.data.service.entity.UserEntity\n\ndata class UserManagerViewState(\n    val users: List<UserEntity> = emptyList(),\n    val cause: Throwable? = null\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/theme/Color.kt",
    "content": "package com.rosan.accounts.ui.theme\n\nimport androidx.compose.ui.graphics.Color\n\nval Purple80 = Color(0xFFD0BCFF)\nval PurpleGrey80 = Color(0xFFCCC2DC)\nval Pink80 = Color(0xFFEFB8C8)\n\nval Purple40 = Color(0xFF6650a4)\nval PurpleGrey40 = Color(0xFF625b71)\nval Pink40 = Color(0xFF7D5260)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/theme/Theme.kt",
    "content": "package com.rosan.accounts.ui.theme\n\nimport android.app.Activity\nimport android.os.Build\nimport android.view.WindowManager\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.darkColorScheme\nimport androidx.compose.material3.dynamicDarkColorScheme\nimport androidx.compose.material3.dynamicLightColorScheme\nimport androidx.compose.material3.lightColorScheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.SideEffect\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.toArgb\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.compose.ui.platform.LocalView\nimport androidx.core.view.WindowCompat\n\nprivate val DarkColorScheme = darkColorScheme(\n    primary = Purple80,\n    secondary = PurpleGrey80,\n    tertiary = Pink80\n)\n\nprivate val LightColorScheme = lightColorScheme(\n    primary = Purple40,\n    secondary = PurpleGrey40,\n    tertiary = Pink40\n\n    /* Other default colors to override\n    background = Color(0xFFFFFBFE),\n    surface = Color(0xFFFFFBFE),\n    onPrimary = Color.White,\n    onSecondary = Color.White,\n    onTertiary = Color.White,\n    onBackground = Color(0xFF1C1B1F),\n    onSurface = Color(0xFF1C1B1F),\n    */\n)\n\n@Composable\nfun AccountsTheme(\n    darkTheme: Boolean = isSystemInDarkTheme(),\n    // Dynamic color is available on Android 12+\n    dynamicColor: Boolean = true,\n    content: @Composable () -> Unit\n) {\n    val colorScheme = when {\n        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {\n            val context = LocalContext.current\n            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)\n        }\n\n        darkTheme -> DarkColorScheme\n        else -> LightColorScheme\n    }\n    val view = LocalView.current\n    SideEffect {\n        val window = (view.context as Activity).window\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)\n            window.attributes.layoutInDisplayCutoutMode =\n                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES\n\n        WindowCompat.setDecorFitsSystemWindows(window, false)\n\n        window.statusBarColor = Color.Transparent.toArgb()\n        WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =\n            !darkTheme\n\n        window.navigationBarColor = Color.Transparent.toArgb()\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)\n            window.navigationBarDividerColor = Color.Transparent.toArgb()\n        WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars =\n            !darkTheme\n    }\n\n    MaterialTheme(\n        colorScheme = colorScheme,\n        typography = Typography,\n        content = content\n    )\n}"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/theme/Type.kt",
    "content": "package com.rosan.accounts.ui.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui.text.TextStyle\nimport androidx.compose.ui.text.font.FontFamily\nimport androidx.compose.ui.text.font.FontWeight\nimport androidx.compose.ui.unit.sp\n\n// Set of Material typography styles to start with\nval Typography = Typography(\n    bodyLarge = TextStyle(\n        fontFamily = FontFamily.Default,\n        fontWeight = FontWeight.Normal,\n        fontSize = 16.sp,\n        lineHeight = 24.sp,\n        letterSpacing = 0.5.sp\n    )\n    /* Other default text styles to override\n    titleLarge = TextStyle(\n        fontFamily = FontFamily.Default,\n        fontWeight = FontWeight.Normal,\n        fontSize = 22.sp,\n        lineHeight = 28.sp,\n        letterSpacing = 0.sp\n    ),\n    labelSmall = TextStyle(\n        fontFamily = FontFamily.Default,\n        fontWeight = FontWeight.Medium,\n        fontSize = 11.sp,\n        lineHeight = 16.sp,\n        letterSpacing = 0.5.sp\n    )\n    */\n)"
  },
  {
    "path": "app/src/main/java/com/rosan/accounts/ui/widget/PositionDialog.kt",
    "content": "package com.rosan.accounts.ui.widget\n\nimport androidx.compose.animation.core.animateDpAsState\nimport androidx.compose.foundation.gestures.detectTapGestures\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.sizeIn\nimport androidx.compose.material3.AlertDialogDefaults\nimport androidx.compose.material3.LocalContentColor\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ProvideTextStyle\nimport androidx.compose.material3.Surface\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.CompositionLocalProvider\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Shape\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.layout.onSizeChanged\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.window.Dialog\nimport androidx.compose.ui.window.DialogProperties\n\n@Composable\nfun PositionDialog(\n    properties: DialogProperties = DialogProperties(),\n    onDismissRequest: () -> Unit,\n    modifier: Modifier = Modifier,\n    shape: Shape = AlertDialogDefaults.shape,\n    containerColor: Color = AlertDialogDefaults.containerColor,\n    iconContentColor: Color = AlertDialogDefaults.iconContentColor,\n    titleContentColor: Color = AlertDialogDefaults.titleContentColor,\n    textContentColor: Color = AlertDialogDefaults.textContentColor,\n    tonalElevation: Dp = AlertDialogDefaults.TonalElevation,\n    leftIcon: @Composable (() -> Unit)? = null,\n    centerIcon: @Composable (() -> Unit)? = null,\n    rightIcon: @Composable (() -> Unit)? = null,\n    leftTitle: @Composable (() -> Unit)? = null,\n    centerTitle: @Composable (() -> Unit)? = null,\n    rightTitle: @Composable (() -> Unit)? = null,\n    leftSubtitle: @Composable (() -> Unit)? = null,\n    centerSubtitle: @Composable (() -> Unit)? = null,\n    rightSubtitle: @Composable (() -> Unit)? = null,\n    leftText: @Composable (() -> Unit)? = null,\n    centerText: @Composable (() -> Unit)? = null,\n    rightText: @Composable (() -> Unit)? = null,\n    leftContent: @Composable (() -> Unit)? = null,\n    centerContent: @Composable (() -> Unit)? = null,\n    rightContent: @Composable (() -> Unit)? = null,\n    leftButton: @Composable (() -> Unit)? = null,\n    centerButton: @Composable (() -> Unit)? = null,\n    rightButton: @Composable (() -> Unit)? = null\n) {\n    Dialog(onDismissRequest = onDismissRequest, properties = properties) {\n        Box(modifier = Modifier\n            .fillMaxSize()\n            .pointerInput(null) {\n                detectTapGestures(onTap = {\n                    onDismissRequest()\n                })\n            }) {\n            Box(modifier = Modifier\n                .align(Alignment.Center)\n                .pointerInput(null) {\n                    detectTapGestures(onTap = {})\n                }) {\n                Surface(\n                    modifier = modifier,\n                    shape = shape,\n                    color = containerColor,\n                    tonalElevation = tonalElevation\n                ) {\n                    Box(\n                        modifier = Modifier\n                            .sizeIn(minWidth = MinWidth, maxHeight = MaxWidth)\n                            .padding(DialogPadding)\n                    ) {\n                        // set the button always in bottom\n                        var buttonHeightPx by remember {\n                            mutableStateOf(0)\n                        }\n                        val buttonHeight = (buttonHeightPx / LocalDensity.current.density).dp\n                        Box(modifier = Modifier\n                            .align(Alignment.BottomCenter)\n                            .onSizeChanged {\n                                buttonHeightPx = it.height\n                            }) {\n                            PositionChildWidget(\n                                leftButton, centerButton, rightButton\n                            ) { button ->\n                                CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.primary) {\n                                    val textStyle = MaterialTheme.typography.labelLarge\n                                    ProvideTextStyle(value = textStyle) {\n                                        Box(\n                                            modifier = Modifier.padding(ButtonPadding)\n                                        ) {\n                                            button?.invoke()\n                                        }\n                                    }\n                                }\n                            }\n                        }\n\n                        Column(\n                            modifier = Modifier.padding(bottom = animateDpAsState(targetValue = buttonHeight).value)\n                        ) {\n                            PositionChildWidget(\n                                leftIcon, centerIcon, rightIcon\n                            ) { icon ->\n                                CompositionLocalProvider(LocalContentColor provides iconContentColor) {\n                                    Box(\n                                        modifier = Modifier\n                                            .padding(IconPadding)\n                                            .align(Alignment.CenterHorizontally)\n                                    ) {\n                                        icon?.invoke()\n                                    }\n                                }\n                            }\n                            PositionChildWidget(\n                                leftTitle, centerTitle, rightTitle\n                            ) { title ->\n                                CompositionLocalProvider(LocalContentColor provides titleContentColor) {\n                                    ProvideTextStyle(MaterialTheme.typography.headlineSmall) {\n                                        Box(\n                                            modifier = Modifier\n                                                .padding(TitlePadding)\n                                                .align(Alignment.CenterHorizontally)\n                                        ) {\n                                            title?.invoke()\n                                        }\n                                    }\n                                }\n                            }\n                            PositionChildWidget(\n                                leftSubtitle, centerSubtitle, rightSubtitle\n                            ) { subtitle ->\n                                CompositionLocalProvider(LocalContentColor provides titleContentColor) {\n                                    val textStyle = MaterialTheme.typography.bodyMedium\n                                    ProvideTextStyle(textStyle) {\n                                        Box(\n                                            modifier = Modifier\n                                                .padding(SubtitlePadding)\n                                                .align(Alignment.CenterHorizontally)\n                                        ) {\n                                            subtitle?.invoke()\n                                        }\n                                    }\n                                }\n                            }\n                            val contentMode =\n                                leftContent != null || centerContent != null || rightContent != null\n                            PositionChildWidget(\n                                if (contentMode) leftContent else leftText,\n                                if (contentMode) centerContent else centerText,\n                                if (contentMode) rightContent else rightText\n                            ) { text ->\n                                CompositionLocalProvider(LocalContentColor provides textContentColor) {\n                                    val textStyle = MaterialTheme.typography.bodyMedium\n                                    ProvideTextStyle(textStyle) {\n                                        Box(\n                                            modifier = Modifier\n                                                .weight(weight = 1f, fill = false)\n                                                .padding(if (contentMode) ContentPadding else TextPadding)\n                                        ) {\n                                            text?.invoke()\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n@Composable\nprivate fun PositionChildWidget(\n    left: @Composable (() -> Unit)? = null,\n    center: @Composable (() -> Unit)? = null,\n    right: @Composable (() -> Unit)? = null,\n    parent: @Composable ((child: @Composable (() -> Unit)?) -> Unit)\n) {\n    if (left == null && center == null && right == null) return\n    Box(modifier = Modifier.fillMaxWidth()) {\n        Box(modifier = Modifier.align(Alignment.TopStart)) {\n            parent.invoke(left)\n        }\n        Box(modifier = Modifier.align(Alignment.TopCenter)) {\n            parent.invoke(center)\n        }\n        Box(modifier = Modifier.align(Alignment.TopEnd)) {\n            parent.invoke(right)\n        }\n    }\n}\n\nprivate val ButtonsMainAxisSpacing = 8.dp\nprivate val ButtonsCrossAxisSpacing = 12.dp\n\nprivate val DialogSinglePadding = 24.dp\n\nprivate val DialogPadding = PaddingValues(top = DialogSinglePadding, bottom = DialogSinglePadding)\nprivate val IconPadding =\n    PaddingValues.Absolute(left = DialogSinglePadding, right = DialogSinglePadding, bottom = 12.dp)\nprivate val TitlePadding =\n    PaddingValues.Absolute(left = DialogSinglePadding, right = DialogSinglePadding, bottom = 12.dp)\nprivate val SubtitlePadding =\n    PaddingValues.Absolute(left = DialogSinglePadding, right = DialogSinglePadding, bottom = 12.dp)\nprivate val TextPadding =\n    PaddingValues.Absolute(left = DialogSinglePadding, right = DialogSinglePadding, bottom = 12.dp)\nprivate val ContentPadding = PaddingValues.Absolute(bottom = 12.dp)\nprivate val ButtonPadding = PaddingValues(horizontal = DialogSinglePadding)\n\nprivate val MinWidth = 280.dp\nprivate val MaxWidth = 560.dp"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_200\">#FFBB86FC</color>\n    <color name=\"purple_500\">#FF6200EE</color>\n    <color name=\"purple_700\">#FF3700B3</color>\n    <color name=\"teal_200\">#FF03DAC5</color>\n    <color name=\"teal_700\">#FF018786</color>\n    <color name=\"black\">#FF000000</color>\n    <color name=\"white\">#FFFFFFFF</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\" translatable=\"false\">Accounts</string>\n\n    <string name=\"user_manager\">User Manager</string>\n    <string name=\"user_name_default\">Untitled</string>\n\n    <string name=\"delete_user_warning\">Are you sure you want to delete this user space?\\nAll applications and data in this user space will be lost!!!</string>\n    <string name=\"delete_user_confirm\">Yes, I\\'m sure!</string>\n    <string name=\"delete_user_cancel\">No</string>\n\n    <string name=\"remove\">Remove</string>\n\n    <string name=\"account_manager\">Account Manager</string>\n    <string name=\"account_empty\">No account exists</string>\n    <string name=\"copied_format_hail\">copied, import it in Hail!</string>\n    <string name=\"shizuku_not_working\">Please activate Shizuku and agree to grant permission.</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Theme.Accounts\" parent=\"android:Theme.Material.Light.NoActionBar\">\n        <item name=\"android:windowTranslucentStatus\">true</item>\n        <item name=\"android:windowTranslucentNavigation\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"user_manager\">用户管理</string>\n    <string name=\"user_name_default\">未命名</string>\n\n    <string name=\"delete_user_warning\">确定要删除此用户吗？\\n所有处于此用户空间的应用和数据都会消失！！！</string>\n    <string name=\"delete_user_confirm\">是的，我确定！</string>\n\n    <string name=\"delete_user_cancel\">取消</string>\n    <string name=\"remove\">移除</string>\n    <string name=\"account_manager\">账号管理</string>\n    <string name=\"account_empty\">没有账户存在</string>\n    <string name=\"copied_format_hail\">复制成功，请在【雹】中导入！</string>\n    <string name=\"shizuku_not_working\">请激活Shizuku并同意权限请求！</string>\n</resources>"
  },
  {
    "path": "app/src/test/java/com/rosan/accounts/ExampleUnitTest.kt",
    "content": "package com.rosan.accounts\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    id 'com.android.application' version '8.0.2' apply false\n    id 'com.android.library' version '8.0.2' apply false\n    id 'org.jetbrains.kotlin.android' version '1.8.20' apply false\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sat Jun 17 18:49:09 CST 2023\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n# Enables namespacing of each library's R class so that its R class includes only the\n# resources declared in the library itself and none from the library's dependencies,\n# thereby reducing the size of the R class for that library\nandroid.nonTransitiveRClass=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "hidden-api/.gitignore",
    "content": "/build"
  },
  {
    "path": "hidden-api/build.gradle",
    "content": "plugins {\n    id(\"com.android.library\")\n}\n\nandroid {\n    namespace \"com.rosan.hidden_api\"\n    compileSdk 33\n\n    defaultConfig {\n        minSdk 19\n    }\n}\n\ndependencies {\n    def annotation = \"1.6.0\"\n    compileOnly \"androidx.annotation:annotation:$annotation\"\n}"
  },
  {
    "path": "hidden-api/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />"
  },
  {
    "path": "hidden-api/src/main/java/android/accounts/IAccountManager.java",
    "content": "package android.accounts;\n\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport androidx.annotation.DeprecatedSinceApi;\nimport androidx.annotation.RequiresApi;\n\npublic interface IAccountManager extends IInterface {\n    AuthenticatorDescription[] getAuthenticatorTypes(int userId);\n\n    @RequiresApi(Build.VERSION_CODES.M)\n    Account[] getAccountsAsUser(String accountType, int userId, String opPackageName);\n\n    @DeprecatedSinceApi(api = Build.VERSION_CODES.M)\n    Account[] getAccountsAsUser(String accountType, int userId);\n\n    abstract class Stub extends Binder implements IAccountManager {\n        public static IAccountManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hidden-api/src/main/java/android/content/pm/IPackageManager.java",
    "content": "package android.content.pm;\n\nimport android.os.Binder;\nimport android.os.Build;\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport androidx.annotation.DeprecatedSinceApi;\nimport androidx.annotation.RequiresApi;\n\npublic interface IPackageManager extends IInterface {\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    PackageInfo getPackageInfo(String packageName, long flags, int userId);\n\n    @DeprecatedSinceApi(api = Build.VERSION_CODES.TIRAMISU)\n    PackageInfo getPackageInfo(String packageName, int flags, int userId);\n\n    abstract class Stub extends Binder implements IPackageManager {\n\n        public static IPackageManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hidden-api/src/main/java/android/content/pm/UserInfo.java",
    "content": "package android.content.pm;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\npublic class UserInfo implements Parcelable {\n    public int id;\n\n    public @Nullable String name;\n\n    protected UserInfo(Parcel in) {\n    }\n\n    public static final Creator<UserInfo> CREATOR = new Creator<UserInfo>() {\n        @Override\n        public UserInfo createFromParcel(Parcel in) {\n            return new UserInfo(in);\n        }\n\n        @Override\n        public UserInfo[] newArray(int size) {\n            return new UserInfo[size];\n        }\n    };\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(@NonNull Parcel dest, int flags) {\n    }\n}\n"
  },
  {
    "path": "hidden-api/src/main/java/android/os/IUserManager.java",
    "content": "package android.os;\n\nimport android.content.pm.UserInfo;\n\nimport androidx.annotation.DeprecatedSinceApi;\nimport androidx.annotation.RequiresApi;\n\nimport java.util.List;\n\npublic interface IUserManager extends IInterface {\n    @RequiresApi(Build.VERSION_CODES.R)\n    List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);\n\n    @DeprecatedSinceApi(api = Build.VERSION_CODES.R)\n    List<UserInfo> getUsers(boolean excludeDying);\n\n    boolean removeUser(int userHandle);\n\n    abstract class Stub extends Binder implements IUserManager {\n        public static IUserManager asInterface(IBinder obj) {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "hidden-api/src/main/java/android/os/ServiceManager.java",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage android.os;\n\nimport java.util.Map;\n\n/**\n * @hide\n */\npublic final class ServiceManager {\n    public static IBinder getService(String name) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Returns a reference to a service with the given name, or throws\n     * {@link NullPointerException} if none is found.\n     *\n     * @hide\n     */\n    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Place a new @a service called @a name into the service\n     * manager.\n     *\n     * @param name    the name of the new service\n     * @param service the service object\n     */\n    public static void addService(String name, IBinder service) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Place a new @a service called @a name into the service\n     * manager.\n     *\n     * @param name          the name of the new service\n     * @param service       the service object\n     * @param allowIsolated set to true to allow isolated sandboxed processes\n     *                      to access this service\n     */\n    public static void addService(String name, IBinder service, boolean allowIsolated) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Retrieve an existing service called @a name from the\n     * service manager.  Non-blocking.\n     */\n    public static IBinder checkService(String name) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Return a list of all currently running services.\n     *\n     * @return an array of all currently running services, or <code>null</code> in\n     * case of an exception\n     */\n    public static String[] listServices() {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * This is only intended to be called when the process is first being brought\n     * up and bound by the activity manager. There is only one thread in the process\n     * at that time, so no locking is done.\n     *\n     * @param cache the cache of service references\n     * @hide\n     */\n    public static void initServiceCache(Map<String, IBinder> cache) {\n        throw new RuntimeException(\"STUB\");\n    }\n\n    /**\n     * Exception thrown when no service published for given name. This might be\n     * thrown early during boot before certain services have published\n     * themselves.\n     *\n     * @hide\n     */\n    public static class ServiceNotFoundException extends Exception {\n        public ServiceNotFoundException(String name) {\n            super(\"No service published for: \" + name);\n        }\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n    }\n}\nrootProject.name = \"Accounts\"\ninclude ':app'\ninclude ':hidden-api'"
  }
]