x1ongzhu преди 1 година
родител
ревизия
eeb9c781ab

+ 3 - 0
app/src/main/java/com/example/modifier/data/BackupItemDao.kt

@@ -24,6 +24,9 @@ interface BackupItemDao {
     @Query("SELECT * FROM backupitem ORDER BY id")
     @Query("SELECT * FROM backupitem ORDER BY id")
     suspend fun getAll(): List<BackupItem>
     suspend fun getAll(): List<BackupItem>
 
 
+    @Query("SELECT * FROM backupitem ORDER BY id DESC")
+    suspend fun getAllR(): List<BackupItem>
+
     @Query("SELECT * FROM backupitem WHERE country = :country AND number = :number limit 1")
     @Query("SELECT * FROM backupitem WHERE country = :country AND number = :number limit 1")
     suspend fun findBackupForNumber(country: String, number: String): BackupItem?
     suspend fun findBackupForNumber(country: String, number: String): BackupItem?
 
 

+ 23 - 3
app/src/main/java/com/example/modifier/ui/backup/BackupFragment.kt

@@ -1,5 +1,6 @@
 package com.example.modifier.ui.backup
 package com.example.modifier.ui.backup
 
 
+import android.content.Context
 import android.os.Bundle
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
@@ -38,6 +39,15 @@ class BackupFragment : Fragment() {
     private val backupRepository by lazy {
     private val backupRepository by lazy {
         BackupRepository(requireContext(), backupItemDao, spoofedSimInfoRepository)
         BackupRepository(requireContext(), backupItemDao, spoofedSimInfoRepository)
     }
     }
+    private var sort: Boolean
+        get() {
+            return requireContext().getSharedPreferences("backup", Context.MODE_PRIVATE)
+                .getBoolean("sort", false)
+        }
+        set(value) {
+            requireContext().getSharedPreferences("backup", Context.MODE_PRIVATE).edit()
+                .putBoolean("sort", value).apply()
+        }
 
 
     override fun onCreateView(
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         inflater: LayoutInflater, container: ViewGroup?,
@@ -74,9 +84,16 @@ class BackupFragment : Fragment() {
                 }
                 }
             }
             }
         }
         }
+        binding.fabSort.setOnClickListener {
+            sort = !sort
+            lifecycleScope.launch {
+                withContext(Dispatchers.IO) {
+                    refresh()
+                }
+            }
+        }
 
 
-        binding.lifecycleOwner = this
-        viewLifecycleOwner.lifecycleScope.launch {
+        lifecycleScope.launch {
             // repeatOnLifecycle launches the block in a new coroutine every time the
             // repeatOnLifecycle launches the block in a new coroutine every time the
             // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
             // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -90,7 +107,10 @@ class BackupFragment : Fragment() {
     }
     }
 
 
     private suspend fun refresh() {
     private suspend fun refresh() {
-        val all = backupItemDao.getAll()
+        val all = when (sort) {
+            true -> backupItemDao.getAll()
+            false -> backupItemDao.getAllR()
+        }
         list.clear()
         list.clear()
         list.addAll(all)
         list.addAll(all)
         withContext(Dispatchers.Main) {
         withContext(Dispatchers.Main) {

+ 173 - 167
app/src/main/java/com/example/modifier/ui/settings/SettingsFragment.kt

@@ -20,8 +20,11 @@ import androidx.activity.result.contract.ActivityResultContracts
 import androidx.core.app.ActivityCompat
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
 import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import com.example.modifier.R
 import com.example.modifier.R
+import com.example.modifier.TAG
 import com.example.modifier.Utils
 import com.example.modifier.Utils
 import com.example.modifier.constants.servers
 import com.example.modifier.constants.servers
 import com.example.modifier.data.AppPreferences
 import com.example.modifier.data.AppPreferences
@@ -99,164 +102,164 @@ class SettingsFragment : Fragment() {
                     // decision.
                     // decision.
                 }
                 }
             }
             }
+        Log.i(TAG, "SettingsFragment.onCreate")
     }
     }
 
 
     override fun onCreateView(
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?,
         inflater: LayoutInflater, container: ViewGroup?,
         savedInstanceState: Bundle?
         savedInstanceState: Bundle?
     ): View {
     ): View {
-        if (this::binding.isInitialized) {
-            return binding.root
-        }
-        binding = FragmentSettingsBinding.inflate(inflater, container, false)
-        binding.tlIccid.setEndIconOnClickListener {
-            if (binding.etMcc.text.isNullOrEmpty() || binding.etMnc.text.isNullOrEmpty() || binding.etAreaCode.text.isNullOrEmpty()) {
-                Toast.makeText(context, "MNC and Code required", Toast.LENGTH_SHORT).show()
-                return@setEndIconOnClickListener
-            }
-            binding.etIccid.setText(
-                genICCID(
-                    binding.etMnc.text.toString(),
-                    binding.etAreaCode.text.toString()
+        Log.i(TAG, "SettingsFragment.onCreateView")
+        if (!this::binding.isInitialized) {
+            binding = FragmentSettingsBinding.inflate(inflater, container, false)
+            binding.tlIccid.setEndIconOnClickListener {
+                if (binding.etMcc.text.isNullOrEmpty() || binding.etMnc.text.isNullOrEmpty() || binding.etAreaCode.text.isNullOrEmpty()) {
+                    Toast.makeText(context, "MNC and Code required", Toast.LENGTH_SHORT).show()
+                    return@setEndIconOnClickListener
+                }
+                binding.etIccid.setText(
+                    genICCID(
+                        binding.etMnc.text.toString(),
+                        binding.etAreaCode.text.toString()
+                    )
                 )
                 )
-            )
-        }
-        binding.tlImsi.setEndIconOnClickListener {
-            val mcc = Optional.ofNullable(
-                binding.etMcc.text
-            ).map { o: Editable? -> Objects.toString(o) }.orElse("")
-            val mnc = Optional.ofNullable(binding.etMnc.text)
-                .map { o: Editable? -> Objects.toString(o) }
-                .orElse("")
-            if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) {
-                Toast.makeText(context, "MCC and MNC are required", Toast.LENGTH_SHORT).show()
-                return@setEndIconOnClickListener
             }
             }
-            binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
-        }
-        binding.tlImei.setEndIconOnClickListener {
-            binding.etImei.setText(genIMEI())
-        }
-        binding.btnSave.setOnClickListener {
-            onSave()
-        }
-        binding.etServer.threshold = 1000
-        binding.etServer.setSimpleItems(servers)
-        binding.btnServer.setOnClickListener { v: View? ->
-            val server = binding.etServer.text.toString()
-            if (StringUtils.isEmpty(server)) {
-                Toast.makeText(context, "Server is required", Toast.LENGTH_SHORT).show()
-                return@setOnClickListener
+            binding.tlImsi.setEndIconOnClickListener {
+                val mcc = Optional.ofNullable(
+                    binding.etMcc.text
+                ).map { o: Editable? -> Objects.toString(o) }.orElse("")
+                val mnc = Optional.ofNullable(binding.etMnc.text)
+                    .map { o: Editable? -> Objects.toString(o) }
+                    .orElse("")
+                if (StringUtils.isEmpty(mcc) || StringUtils.isEmpty(mnc)) {
+                    Toast.makeText(context, "MCC and MNC are required", Toast.LENGTH_SHORT).show()
+                    return@setEndIconOnClickListener
+                }
+                binding.etImsi.setText(mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length))
             }
             }
-            if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
-                Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
-                return@setOnClickListener
+            binding.tlImei.setEndIconOnClickListener {
+                binding.etImei.setText(genIMEI())
             }
             }
-            lifecycleScope.launch {
-                appPreferencesRepository.updateServer(server)
-                appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
-                instance?.connect()
-                Utils.makeLoadingButton(context, binding.btnServer)
-                binding.btnServer.isEnabled = false
-                delay(500)
-                binding.btnServer.setIconResource(R.drawable.ic_done)
-                binding.btnServer.text = "OK"
-                delay(500)
-                binding.btnServer.isEnabled = true
-                binding.btnServer.icon = null
-                binding.btnServer.text = "Save"
+            binding.btnSave.setOnClickListener {
+                onSave()
+            }
+            binding.etServer.threshold = 1000
+            binding.etServer.setSimpleItems(servers)
+            binding.btnServer.setOnClickListener { v: View? ->
+                val server = binding.etServer.text.toString()
+                if (StringUtils.isEmpty(server)) {
+                    Toast.makeText(context, "Server is required", Toast.LENGTH_SHORT).show()
+                    return@setOnClickListener
+                }
+                if (!UrlValidator(arrayOf("http", "https")).isValid(server)) {
+                    Toast.makeText(context, "Invalid server URL", Toast.LENGTH_SHORT).show()
+                    return@setOnClickListener
+                }
+                lifecycleScope.launch {
+                    appPreferencesRepository.updateServer(server)
+                    appPreferencesRepository.updateName(binding.etDeviceLabel.text.toString())
+                    instance?.connect()
+                    Utils.makeLoadingButton(context, binding.btnServer)
+                    binding.btnServer.isEnabled = false
+                    delay(500)
+                    binding.btnServer.setIconResource(R.drawable.ic_done)
+                    binding.btnServer.text = "OK"
+                    delay(500)
+                    binding.btnServer.isEnabled = true
+                    binding.btnServer.icon = null
+                    binding.btnServer.text = "Save"
+                }
             }
             }
-        }
 
 
-        binding.btnRequest.setOnClickListener { v: View? ->
-            lifecycleScope.launch {
-                Utils.makeLoadingButton(context, binding.btnRequest)
-                binding.btnRequest.isEnabled = false
+            binding.btnRequest.setOnClickListener { v: View? ->
+                lifecycleScope.launch {
+                    Utils.makeLoadingButton(context, binding.btnRequest)
+                    binding.btnRequest.isEnabled = false
 
 
-                withContext(Dispatchers.IO) req@{
-                    val response: HttpResponse
-                    try {
-                        response = ktorClient(appPreferences.value.server).put(
-                            RcsNumberApi()
-                        ) {
-                            contentType(ContentType.Application.Json)
-                            setBody(RcsNumberRequest(deviceId = uniqueId))
+                    withContext(Dispatchers.IO) req@{
+                        val response: HttpResponse
+                        try {
+                            response = ktorClient(appPreferences.value.server).put(
+                                RcsNumberApi()
+                            ) {
+                                contentType(ContentType.Application.Json)
+                                setBody(RcsNumberRequest(deviceId = uniqueId))
+                            }
+                        } catch (e: Exception) {
+                            withContext(Dispatchers.Main) {
+                                MaterialAlertDialogBuilder(requireContext())
+                                    .setTitle("Error")
+                                    .setMessage(e.message)
+                                    .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
+                                        dialog.dismiss()
+                                    }
+                                    .show()
+                                binding.btnRequest.isEnabled = true
+                                binding.btnRequest.text = "Request"
+                                binding.btnRequest.icon = null
+                            }
+                            return@req
                         }
                         }
-                    } catch (e: Exception) {
-                        withContext(Dispatchers.Main) {
-                            MaterialAlertDialogBuilder(requireContext())
-                                .setTitle("Error")
-                                .setMessage(e.message)
-                                .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
-                                    dialog.dismiss()
-                                }
-                                .show()
-                            binding.btnRequest.isEnabled = true
-                            binding.btnRequest.text = "Request"
-                            binding.btnRequest.icon = null
-                        }
-                        return@req
-                    }
-                    Log.i(com.example.modifier.TAG, "response: ${response.bodyAsText()}")
-                    val res = response.body<RcsNumberResponse>()
-                    val number = res.number
-                    val mcc = res.mcc
-                    val mnc = res.mnc
-                    val country = res.country
-                    val areaCode = res.areaCode
-                    val iccid = genICCID(mnc, areaCode)
-                    val imsi =
-                        mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
-                    val imei = genIMEI()
-                    spoofedSimInfoRepository.updateSpoofedSimInfo(
-                        SpoofedSimInfo(
-                            number,
-                            mcc,
-                            mnc,
-                            iccid,
-                            imsi,
-                            imei,
-                            country,
-                            areaCode,
-                            false,
-                            res.carrierId,
-                            res.carrierName
+                        Log.i(com.example.modifier.TAG, "response: ${response.bodyAsText()}")
+                        val res = response.body<RcsNumberResponse>()
+                        val number = res.number
+                        val mcc = res.mcc
+                        val mnc = res.mnc
+                        val country = res.country
+                        val areaCode = res.areaCode
+                        val iccid = genICCID(mnc, areaCode)
+                        val imsi =
+                            mcc + mnc + RandomStringUtils.randomNumeric(15 - mcc.length - mnc.length)
+                        val imei = genIMEI()
+                        spoofedSimInfoRepository.updateSpoofedSimInfo(
+                            SpoofedSimInfo(
+                                number,
+                                mcc,
+                                mnc,
+                                iccid,
+                                imsi,
+                                imei,
+                                country,
+                                areaCode,
+                                false,
+                                res.carrierId,
+                                res.carrierName
+                            )
                         )
                         )
-                    )
+                    }
+                    binding.btnRequest.icon = null
+                    binding.btnRequest.isEnabled = true
+                    binding.btnRequest.text = "Request"
                 }
                 }
-                binding.btnRequest.icon = null
-                binding.btnRequest.isEnabled = true
-                binding.btnRequest.text = "Request"
             }
             }
-        }
 
 
-        binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
-            CoroutineScope(Dispatchers.IO).launch {
-                appPreferencesRepository.updatePreventClean(isChecked)
+            binding.switchClean.setOnCheckedChangeListener { buttonView, isChecked ->
+                CoroutineScope(Dispatchers.IO).launch {
+                    appPreferencesRepository.updatePreventClean(isChecked)
+                }
             }
             }
-        }
-        binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
-            CoroutineScope(Dispatchers.IO).launch {
-                appPreferencesRepository.updatePreventRequest(isChecked)
+            binding.switchRequest.setOnCheckedChangeListener { buttonView, isChecked ->
+                CoroutineScope(Dispatchers.IO).launch {
+                    appPreferencesRepository.updatePreventRequest(isChecked)
+                }
             }
             }
-        }
-        binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
-            CoroutineScope(Dispatchers.IO).launch {
-                appPreferencesRepository.updatePreventReset(isChecked)
+            binding.swReset.setOnCheckedChangeListener { buttonView, isChecked ->
+                CoroutineScope(Dispatchers.IO).launch {
+                    appPreferencesRepository.updatePreventReset(isChecked)
+                }
             }
             }
-        }
 
 
-        binding.btnCheck.setOnClickListener {
+            binding.btnCheck.setOnClickListener {
 
 
-            when {
-                ContextCompat.checkSelfPermission(
-                    requireContext(),
-                    Manifest.permission.READ_PHONE_STATE
-                ) == PackageManager.PERMISSION_GRANTED -> {
-                    val telephonyManager =
-                        requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+                when {
+                    ContextCompat.checkSelfPermission(
+                        requireContext(),
+                        Manifest.permission.READ_PHONE_STATE
+                    ) == PackageManager.PERMISSION_GRANTED -> {
+                        val telephonyManager =
+                            requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
 
 
-                    var res = """IMEI: ${telephonyManager.imei}
+                        var res = """IMEI: ${telephonyManager.imei}
                     |IMSI: ${telephonyManager.subscriberId}
                     |IMSI: ${telephonyManager.subscriberId}
                     |ICCID: ${telephonyManager.simSerialNumber}
                     |ICCID: ${telephonyManager.simSerialNumber}
                     |Phone Number: ${telephonyManager.line1Number}
                     |Phone Number: ${telephonyManager.line1Number}
@@ -269,15 +272,15 @@ class SettingsFragment : Fragment() {
                     |DefaultSmsSubscriptionId: ${SmsManager.getDefaultSmsSubscriptionId()}
                     |DefaultSmsSubscriptionId: ${SmsManager.getDefaultSmsSubscriptionId()}
                     |
                     |
                     """.trimMargin()
                     """.trimMargin()
-                    val subscriptionManager: SubscriptionManager =
-                        requireContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
+                        val subscriptionManager: SubscriptionManager =
+                            requireContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
 
 
-                    val simCount = subscriptionManager.activeSubscriptionInfoCountMax
-                    for (i in 0 until simCount) {
-                        val info =
-                            subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
-                                ?: continue
-                        res += """
+                        val simCount = subscriptionManager.activeSubscriptionInfoCountMax
+                        for (i in 0 until simCount) {
+                            val info =
+                                subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i)
+                                    ?: continue
+                            res += """
                         |--- Slot:$i Subscription ID: ${info.subscriptionId} ---
                         |--- Slot:$i Subscription ID: ${info.subscriptionId} ---
                         |ICCID: ${info.iccId}
                         |ICCID: ${info.iccId}
                         |Number: ${subscriptionManager.getPhoneNumber(info.subscriptionId)}
                         |Number: ${subscriptionManager.getPhoneNumber(info.subscriptionId)}
@@ -287,39 +290,41 @@ class SettingsFragment : Fragment() {
                         |carrierId: ${info.carrierId}
                         |carrierId: ${info.carrierId}
                         |roaming: ${info.dataRoaming}
                         |roaming: ${info.dataRoaming}
                         """.trimMargin()
                         """.trimMargin()
-                    }
-                    MaterialAlertDialogBuilder(requireContext())
-                        .setTitle("Info")
-                        .setMessage(res)
-                        .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
-                            dialog.dismiss()
                         }
                         }
-                        .show()
-                }
+                        MaterialAlertDialogBuilder(requireContext())
+                            .setTitle("Info")
+                            .setMessage(res)
+                            .setPositiveButton("OK") { dialog: DialogInterface, which: Int ->
+                                dialog.dismiss()
+                            }
+                            .show()
+                    }
 
 
-                ActivityCompat.shouldShowRequestPermissionRationale(
-                    requireActivity(), Manifest.permission.READ_PHONE_STATE
-                ) -> {
-                    // In an educational UI, explain to the user why your app requires this
-                    // permission for a specific feature to behave as expected, and what
-                    // features are disabled if it's declined. In this UI, include a
-                    // "cancel" or "no thanks" button that lets the user continue
-                    // using your app without granting the permission.
-                }
+                    ActivityCompat.shouldShowRequestPermissionRationale(
+                        requireActivity(), Manifest.permission.READ_PHONE_STATE
+                    ) -> {
+                        // In an educational UI, explain to the user why your app requires this
+                        // permission for a specific feature to behave as expected, and what
+                        // features are disabled if it's declined. In this UI, include a
+                        // "cancel" or "no thanks" button that lets the user continue
+                        // using your app without granting the permission.
+                    }
 
 
-                else -> {
-                    // You can directly ask for the permission.
-                    // The registered ActivityResultCallback gets the result of this request.
-                    requestPermissionLauncher.launch(
-                        Manifest.permission.READ_PHONE_STATE
-                    )
+                    else -> {
+                        // You can directly ask for the permission.
+                        // The registered ActivityResultCallback gets the result of this request.
+                        requestPermissionLauncher.launch(
+                            Manifest.permission.READ_PHONE_STATE
+                        )
+                    }
                 }
                 }
-            }
 
 
+            }
         }
         }
-        lifecycleScope.launch {
+        viewLifecycleOwner.lifecycleScope.launch {
             launch {
             launch {
                 appPreferencesRepository.getAppPreferences().collect {
                 appPreferencesRepository.getAppPreferences().collect {
+                    Log.i(TAG, "appPreferencesRepository.collect")
                     binding.etServer.setText(it.server)
                     binding.etServer.setText(it.server)
                     binding.etDeviceLabel.setText(it.name)
                     binding.etDeviceLabel.setText(it.name)
                     binding.switchClean.isChecked = it.preventClean
                     binding.switchClean.isChecked = it.preventClean
@@ -329,6 +334,7 @@ class SettingsFragment : Fragment() {
             }
             }
             launch {
             launch {
                 spoofedSimInfo.collect {
                 spoofedSimInfo.collect {
+                    Log.i(TAG, "spoofedSimInfo.collect")
                     binding.etNumber.setText(it.number)
                     binding.etNumber.setText(it.number)
                     binding.etMcc.setText(it.mcc)
                     binding.etMcc.setText(it.mcc)
                     binding.etMnc.setText(it.mnc)
                     binding.etMnc.setText(it.mnc)

+ 11 - 0
app/src/main/res/drawable/ic_sort.xml

@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal"
+    android:autoMirrored="true">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M120,720L120,640L360,640L360,720L120,720ZM120,520L120,440L600,440L600,520L120,520ZM120,320L120,240L840,240L840,320L120,320Z"/>
+</vector>

+ 12 - 1
app/src/main/res/layout/fragment_backup.xml

@@ -19,7 +19,7 @@
                 android:layout_height="match_parent"
                 android:layout_height="match_parent"
                 android:clipToPadding="false"
                 android:clipToPadding="false"
                 android:paddingTop="40dp"
                 android:paddingTop="40dp"
-                android:paddingBottom="100dp">
+                android:paddingBottom="150dp">
 
 
             </androidx.recyclerview.widget.RecyclerView>
             </androidx.recyclerview.widget.RecyclerView>
         </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
         </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@@ -34,5 +34,16 @@
             app:icon="@drawable/ic_add"
             app:icon="@drawable/ic_add"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent" />
             app:layout_constraintEnd_toEndOf="parent" />
+
+        <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
+            android:id="@+id/fab_sort"
+            style="?attr/floatingActionButtonSmallStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|end"
+            android:layout_marginBottom="16dp"
+            app:icon="@drawable/ic_sort"
+            app:layout_constraintBottom_toTopOf="@id/fab_backup"
+            app:layout_constraintStart_toStartOf="@id/fab_backup" />
     </androidx.constraintlayout.widget.ConstraintLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
 </layout>
 </layout>