Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 1 addition & 35 deletions app/src/androidTest/kotlin/com/itsaky/androidide/EndToEndTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.itsaky.androidide.screens.OnboardingScreen
import com.itsaky.androidide.screens.ProjectSettingsScreen.clickCreateProjectProjectSettings
import com.itsaky.androidide.screens.ProjectSettingsScreen.setProjectName
import com.itsaky.androidide.screens.PermissionScreen
import com.itsaky.androidide.screens.PermissionsInfoScreen
import com.itsaky.androidide.utils.PermissionsHelper
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -75,7 +74,7 @@ class EndToEndTest : TestCase() {

advancePastWelcomeScreen()

// ── Permissions Info Screen ──
// ── Permissions Screen (with privacy disclosure dialog overlay) ──

step("Verify privacy disclosure dialog") {
val d = device.uiDevice
Expand All @@ -90,46 +89,13 @@ class EndToEndTest : TestCase() {
device.uiDevice.waitForIdle()
}

step("Verify permissions info content") {
flakySafely(timeoutMs = 2_000) {
PermissionsInfoScreen {
introText { isVisible() }
permissionsList { isVisible() }
}
}
}

step("Verify NEXT button on permissions info") {
OnboardingScreen.nextButton { isVisible(); isClickable() }
}

step("Verify privacy dialog does not reappear") {
assertFalse(
"Dialog should not reappear",
device.uiDevice.findObject(UiSelector().text(dialogTitle)).exists(),
)
}

// ── Permissions Screen ──

step("Advance to permissions list") {
val d = device.uiDevice
val nextObj = d.findObject(UiSelector().descriptionContains("NEXT"))
if (!nextObj.waitForExists(3_000)) {
throw AssertionError("NEXT button not found on permissions info slide")
}
clickFirstAccessibilityNodeByText(
searchText = "NEXT",
errorLabel = "NEXT",
matchBy = { node ->
val desc = node.contentDescription?.toString() ?: ""
val text = node.text?.toString() ?: ""
desc.contains("NEXT", ignoreCase = true) || text.contains("NEXT", ignoreCase = true)
},
)
d.waitForIdle()
}

val required = PermissionsHelper.getRequiredPermissions(targetContext)

step("Verify all permission items") {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.itsaky.androidide.helper.advancePastWelcomeScreen
import com.itsaky.androidide.helper.clickFirstAccessibilityNodeByText
import com.itsaky.androidide.helper.grantAllRequiredPermissionsThroughOnboardingUi
import com.itsaky.androidide.helper.logOnboardingNavigation
import com.itsaky.androidide.helper.passPermissionsInfoSlideWithPrivacyDialog
import com.itsaky.androidide.helper.waitForMainHomeOrEditorUi
import com.itsaky.androidide.screens.InstallToolsScreen
import com.itsaky.androidide.screens.PermissionScreen
Expand Down Expand Up @@ -67,24 +66,6 @@ class NavigateToMainScreenScenario : Scenario() {
}
}

// If NEXT button is visible (permissions info slide), advance to permissions list
step("Advance past permissions info (if showing)") {
val nextBtn = d.findObject(UiSelector().descriptionContains("NEXT"))
if (nextBtn.waitForExists(1_000)) {
logOnboardingNavigation("Permissions info slide — clicking NEXT")
clickFirstAccessibilityNodeByText(
searchText = "NEXT",
errorLabel = "NEXT",
matchBy = { node ->
val desc = node.contentDescription?.toString() ?: ""
val text = node.text?.toString() ?: ""
desc.contains("NEXT", ignoreCase = true) || text.contains("NEXT", ignoreCase = true)
},
)
d.waitForIdle()
}
}

// If the permission list is showing, grant all permissions
step("Grant permissions (if on permission list)") {
val permScreen = d.findObject(UiSelector().resourceIdMatches(".*:id/onboarding_items"))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import com.itsaky.androidide.app.configuration.IJdkDistributionProvider
import com.itsaky.androidide.fragments.onboarding.GreetingFragment
import com.itsaky.androidide.fragments.onboarding.OnboardingInfoFragment
import com.itsaky.androidide.fragments.onboarding.PermissionsFragment
import com.itsaky.androidide.fragments.onboarding.PermissionsInfoFragment
import com.itsaky.androidide.models.JdkDistribution
import com.itsaky.androidide.preferences.internal.prefManager
import com.itsaky.androidide.tasks.doAsyncWithProgress
Expand Down Expand Up @@ -113,7 +112,6 @@ class OnboardingActivity : AppIntro2() {
pulseAnimation = AnimationUtils.loadAnimation(this, R.anim.pulse_animation)

addSlide(GreetingFragment())
addSlide(PermissionsInfoFragment())

if (!PackageUtils.isCurrentUserThePrimaryUser(this)) {
val errorMessage =
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.net.toUri
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import com.github.appintro.SlidePolicy
import com.github.appintro.SlideSelectionListener
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.itsaky.androidide.R
import com.itsaky.androidide.activities.OnboardingActivity
import com.itsaky.androidide.adapters.onboarding.OnboardingPermissionsAdapter
import com.itsaky.androidide.buildinfo.BuildInfo
import com.itsaky.androidide.databinding.LayoutOnboardingPermissionsBinding
import com.itsaky.androidide.events.InstallationEvent
import com.itsaky.androidide.preferences.internal.prefManager
import com.itsaky.androidide.tasks.doAsyncWithProgress
import com.itsaky.androidide.utils.PermissionsHelper
import com.itsaky.androidide.utils.flashError
Expand All @@ -49,6 +53,7 @@ import com.itsaky.androidide.utils.isAtLeastR
import com.itsaky.androidide.utils.viewLifecycleScope
import com.itsaky.androidide.viewmodel.InstallationState
import com.itsaky.androidide.viewmodel.InstallationViewModel
import io.sentry.Sentry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
Expand All @@ -60,7 +65,8 @@ import org.slf4j.LoggerFactory
*/
class PermissionsFragment :
OnboardingFragment(),
SlidePolicy {
SlidePolicy,
SlideSelectionListener {
var adapter: OnboardingPermissionsAdapter? = null
private val viewModel: InstallationViewModel by viewModels()
private var permissionsBinding: LayoutOnboardingPermissionsBinding? = null
Expand Down Expand Up @@ -89,6 +95,7 @@ class PermissionsFragment :

companion object {
private val logger = LoggerFactory.getLogger(PermissionsFragment::class.java)
private const val KEY_PRIVACY_DISCLOSURE_SHOWN = "privacy.disclosure.shown"

@JvmStatic
fun newInstance(context: Context): PermissionsFragment =
Expand Down Expand Up @@ -355,6 +362,49 @@ class PermissionsFragment :
}
}

override fun onSlideSelected() {
if (!isPrivacyDisclosureShown()) {
showPrivacyDialog()
}
}

override fun onSlideDeselected() {
}

private fun showPrivacyDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(com.itsaky.androidide.resources.R.string.privacy_disclosure_title)
.setMessage(com.itsaky.androidide.resources.R.string.privacy_disclosure_message)
.setPositiveButton(com.itsaky.androidide.resources.R.string.privacy_disclosure_accept) { dialog, _ ->
markPrivacyDisclosureAsShown()
dialog.dismiss()
}
.setNeutralButton(com.itsaky.androidide.resources.R.string.privacy_disclosure_learn_more) { _, _ ->
openPrivacyPolicy()
markPrivacyDisclosureAsShown()
}
.setCancelable(false)
.show()
}

private fun isPrivacyDisclosureShown(): Boolean {
return prefManager.getBoolean(KEY_PRIVACY_DISCLOSURE_SHOWN, false)
}

private fun markPrivacyDisclosureAsShown() {
prefManager.putBoolean(KEY_PRIVACY_DISCLOSURE_SHOWN, true)
}

private fun openPrivacyPolicy() {
try {
val privacyPolicyUrl = getString(R.string.privacy_policy_url)
val intent = Intent(Intent.ACTION_VIEW, privacyPolicyUrl.toUri())
startActivity(intent)
} catch (e: Exception) {
Sentry.captureException(e)
}
}

private fun enableFinishButton() {
finishButton?.isEnabled = true
if (!isTestMode()) {
Expand Down
Loading
Loading