diff --git a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt index 5956b3c02a9..96fa124dec4 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt @@ -1,30 +1,25 @@ package fr.free.nrw.commons -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.rule.ActivityTestRule import androidx.test.uiautomator.UiDevice -import androidx.viewpager.widget.ViewPager import fr.free.nrw.commons.utils.ConfigUtils -import org.hamcrest.core.IsNot.not +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.CoreMatchers.equalTo @LargeTest @RunWith(AndroidJUnit4::class) class WelcomeActivityTest { + @get:Rule - var activityRule: ActivityTestRule<*> = ActivityTestRule(WelcomeActivity::class.java) + val composeTestRule = createAndroidComposeRule() private val device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -37,97 +32,97 @@ class WelcomeActivityTest { @Test fun ifBetaShowsSkipButton() { if (ConfigUtils.isBetaFlavour) { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.finishTutorialButton)) - .check(matches(isDisplayed())) + composeTestRule.onNodeWithText("OK").performClick() + composeTestRule.onNodeWithText("Skip Tutorial").assertIsDisplayed() } } @Test fun ifProdHidesSkipButton() { if (!ConfigUtils.isBetaFlavour) { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.finishTutorialButton)) - .check(matches(not(isDisplayed()))) + composeTestRule.onNodeWithText("Skip Tutorial").assertDoesNotExist() } } @Test fun testBetaSkipButton() { if (ConfigUtils.isBetaFlavour) { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.finishTutorialButton)) - .perform(ViewActions.click()) - assertThat(activityRule.activity.isDestroyed, equalTo(true)) + composeTestRule.onNodeWithText("OK").performClick() + composeTestRule.onNodeWithText("Skip Tutorial").performClick() + composeTestRule.waitForIdle() + assertThat(composeTestRule.activity.isDestroyed, equalTo(true)) } } @Test fun testSwipingOnce() { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeLeft()) + if (ConfigUtils.isBetaFlavour) { + composeTestRule.onNodeWithText("OK").performClick() + } + + composeTestRule.onRoot().performTouchInput { swipeLeft() } assertThat(true, equalTo(true)) - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeRight()) + + composeTestRule.onRoot().performTouchInput { swipeRight() } assertThat(true, equalTo(true)) } @Test fun testSwipingWholeTutorial() { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeLeft()) - .perform(ViewActions.swipeLeft()) - .perform(ViewActions.swipeLeft()) - .perform(ViewActions.swipeLeft()) + if (ConfigUtils.isBetaFlavour) { + composeTestRule.onNodeWithText("OK").performClick() + } + + repeat(4) { + composeTestRule.onRoot().performTouchInput { swipeLeft() } + } assertThat(true, equalTo(true)) - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeRight()) - .perform(ViewActions.swipeRight()) - .perform(ViewActions.swipeRight()) - .perform(ViewActions.swipeRight()) + + repeat(4) { + composeTestRule.onRoot().performTouchInput { swipeRight() } + } assertThat(true, equalTo(true)) } @Test fun swipeBeyondBounds() { - val viewPager = activityRule.activity.findViewById(R.id.welcomePager) - - viewPager.adapter?.let { - if (viewPager.currentItem == 3) { - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeLeft()) - assertThat(true, equalTo(true)) - onView(withId(R.id.welcomePager)) - .perform(ViewActions.swipeRight()) - assertThat(true, equalTo(true)) - } + if (ConfigUtils.isBetaFlavour) { + composeTestRule.onNodeWithText("OK").performClick() + } + + repeat(4) { + composeTestRule.onRoot().performTouchInput { swipeLeft() } } + + composeTestRule.onRoot().performTouchInput { swipeLeft() } + assertThat(true, equalTo(true)) + + composeTestRule.onRoot().performTouchInput { swipeRight() } + assertThat(true, equalTo(true)) } @Test fun swipeTillLastAndFinish() { - val viewPager = activityRule.activity.findViewById(R.id.welcomePager) - - viewPager.adapter?.let { - if (viewPager.currentItem == 3) { - onView(withId(R.id.button_ok)) - .perform(ViewActions.click()) - onView(withId(R.id.finishTutorialButton)) - .perform(ViewActions.click()) - assertThat(activityRule.activity.isDestroyed, equalTo(true)) - } + if (ConfigUtils.isBetaFlavour) { + composeTestRule.onNodeWithText("OK").performClick() + } + + repeat(4) { + composeTestRule.onRoot().performTouchInput { swipeLeft() } } + + composeTestRule.onNodeWithText("YES!").performClick() + + composeTestRule.waitForIdle() + assertThat(composeTestRule.activity.isDestroyed, equalTo(true)) } @Test fun orientationChange() { - UITestHelper.changeOrientation(activityRule) + device.setOrientationLeft() + composeTestRule.waitForIdle() + + device.setOrientationNatural() + composeTestRule.waitForIdle() } -} +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt index 0882ba11764..2a21f6ff02a 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt @@ -1,53 +1,31 @@ package fr.free.nrw.commons -import android.app.AlertDialog import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.View -import fr.free.nrw.commons.databinding.ActivityWelcomeBinding -import fr.free.nrw.commons.databinding.PopupForCopyrightBinding import fr.free.nrw.commons.quiz.QuizActivity import fr.free.nrw.commons.theme.BaseActivity -import fr.free.nrw.commons.utils.applyEdgeToEdgeAllInsets +import androidx.activity.compose.setContent +import androidx.compose.material3.MaterialTheme import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour class WelcomeActivity : BaseActivity() { - private var binding: ActivityWelcomeBinding? = null private var isQuiz = false - /** - * Initialises exiting fields and dependencies - * - * @param savedInstanceState WelcomeActivity bundled data - */ public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityWelcomeBinding.inflate(layoutInflater) - applyEdgeToEdgeAllInsets(binding!!.welcomePager.rootView) - setContentView(binding!!.root) - isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false - // Enable skip button if beta flavor - if (isBetaFlavour) { - binding!!.finishTutorialButton.visibility = View.VISIBLE - - val copyrightBinding = PopupForCopyrightBinding.inflate(layoutInflater) - - val dialog = AlertDialog.Builder(this) - .setView(copyrightBinding.root) - .setCancelable(false) - .create() - dialog.show() - - copyrightBinding.buttonOk.setOnClickListener { v: View? -> dialog.dismiss() } + setContent { + MaterialTheme { + WelcomeScreen( + isBetaFlavour = isBetaFlavour, + pageCount = 5, + onSkipClicked = { finishTutorial() }, + onBackPressedAtStart = { handleBackAtStart() } + ) + } } - - val adapter = WelcomePagerAdapter() - binding!!.welcomePager.adapter = adapter - binding!!.welcomePagerIndicator.setViewPager(binding!!.welcomePager) - binding!!.finishTutorialButton.setOnClickListener { v: View? -> finishTutorial() } } public override fun onDestroy() { @@ -57,15 +35,11 @@ class WelcomeActivity : BaseActivity() { super.onDestroy() } - override fun onBackPressed() { - if (binding!!.welcomePager.currentItem != 0) { - binding!!.welcomePager.setCurrentItem(binding!!.welcomePager.currentItem - 1, true) + private fun handleBackAtStart() { + if (defaultKvStore.getBoolean("firstrun", true)) { + finishAffinity() } else { - if (defaultKvStore.getBoolean("firstrun", true)) { - finishAffinity() - } else { - super.onBackPressed() - } + finish() } } diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeDoUploadPage.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeDoUploadPage.kt new file mode 100644 index 00000000000..4bfbb2c6808 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeDoUploadPage.kt @@ -0,0 +1,92 @@ +package fr.free.nrw.commons + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import fr.free.nrw.commons.R + +@Composable +fun WelcomeDoUploadPage() { + val configuration = LocalConfiguration.current + val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = dimensionResource(id = R.dimen.huge_gap)) + .verticalScroll(rememberScrollState()) + .padding(top = 90.dp, bottom = 45.dp) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Image( + painter = painterResource(id = R.drawable.welcome_do_upload), + contentDescription = stringResource(id = R.string.welcome_do_upload_content_description), + modifier = Modifier + .size(if (isLandscape) 200.dp else 300.dp), + contentScale = ContentScale.Fit + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.tutorial_2_text), + color = Color.White, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium, + ) + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth() + ) { + BulletText(textRes = R.string.tutorial_2_subtext_1) + BulletText(textRes = R.string.tutorial_2_subtext_2) + BulletText(textRes = R.string.tutorial_2_subtext_3) + } + } +} + +@Composable +fun BulletText(textRes: Int) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Top + ) { + Box( + modifier = Modifier + .padding(top = 8.dp, end = 8.dp) + .size(6.dp) + .clip(CircleShape) + .background(Color.White) + ) + Text( + text = stringResource(id = textRes), + color = Color.White, + style = MaterialTheme.typography.bodyMedium + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeDontUploadPage.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeDontUploadPage.kt new file mode 100644 index 00000000000..f2e9241dbbd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeDontUploadPage.kt @@ -0,0 +1,67 @@ +package fr.free.nrw.commons + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import fr.free.nrw.commons.R + +@Composable +fun WelcomeDontUploadPage() { + val configuration = LocalConfiguration.current + val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = dimensionResource(id = R.dimen.huge_gap)) + .verticalScroll(rememberScrollState()) + .padding(top = 32.dp, bottom = 80.dp) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Image( + painter = painterResource(id = R.drawable.welcome_dont_upload), + contentDescription = stringResource(id = R.string.welcome_dont_upload_content_description), + modifier = Modifier + .size(if (isLandscape) 200.dp else 300.dp), + contentScale = ContentScale.Fit + ) + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.tutorial_3_text), + color = Color.White, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium + ) + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth() + ) { + BulletText(textRes = R.string.tutorial_3_subtext_1) + BulletText(textRes = R.string.tutorial_3_subtext_2) + BulletText(textRes = R.string.tutorial_3_subtext_3) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeFinalPage.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeFinalPage.kt new file mode 100644 index 00000000000..f91fd9c8289 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeFinalPage.kt @@ -0,0 +1,111 @@ +package fr.free.nrw.commons + +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import fr.free.nrw.commons.utils.handleWebUrl + +@Composable +fun WelcomeFinalPage(onFinishClicked: () -> Unit) { + val context = LocalContext.current + + Box( + modifier = Modifier.fillMaxSize() + ) { + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = dimensionResource(id = R.dimen.huge_gap)), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Row( + modifier = Modifier.height(180.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(id = R.drawable.welcome_wikipedia), + contentDescription = stringResource(id = R.string.welcome_image_welcome_wikipedia), + modifier = Modifier.size(width = 150.dp, height = 180.dp), + contentScale = ContentScale.Fit + ) + + Image( + painter = painterResource(id = R.drawable.welcome_copyright), + contentDescription = stringResource(id = R.string.welcome_image_welcome_copyright), + modifier = Modifier + .height(120.dp) + .width(dimensionResource(id = R.dimen.giant_height)) + .padding(start = dimensionResource(id = R.dimen.standard_gap)), + contentScale = ContentScale.Fit + ) + } + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Text( + text = stringResource(id = R.string.welcome_final_text), + color = Color.White, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.widthIn(max = dimensionResource(id = R.dimen.very_large_height)) + ) + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Button( + onClick = onFinishClicked, + colors = ButtonDefaults.buttonColors( + contentColor = colorResource(id = R.color.primaryColor) + ), + modifier = Modifier + .width(120.dp) + .height(dimensionResource(id = R.dimen.overflow_button_dimen)) + ) { + Text( + text = stringResource(id = R.string.welcome_final_button_text), + fontWeight = FontWeight.Bold + ) + } + } + + Text( + text = stringResource(id = R.string.welcome_help_button_text), + color = Color.White, + textDecoration = TextDecoration.Underline, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding( + end = dimensionResource(id = R.dimen.standard_gap), + bottom = dimensionResource(id = R.dimen.standard_gap) + ) + .clickable { + handleWebUrl(context, Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents")) + } + .padding(dimensionResource(id = R.dimen.standard_gap)) // Inner padding to make it easier to tap + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeImageExamplePage.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeImageExamplePage.kt new file mode 100644 index 00000000000..10f9caa38a2 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeImageExamplePage.kt @@ -0,0 +1,69 @@ +package fr.free.nrw.commons + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import fr.free.nrw.commons.R + +@Composable +fun WelcomeImageExamplePage() { + val configuration = LocalConfiguration.current + val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = dimensionResource(id = R.dimen.huge_gap)) + .verticalScroll(rememberScrollState()) + .padding(top = 45.dp, bottom = 50.dp) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Image( + painter = painterResource(id = R.drawable.welcome_image_example), + contentDescription = stringResource(id = R.string.welcome_image_sydney_opera_house), + modifier = Modifier + .size(if (isLandscape) 200.dp else 300.dp), + contentScale = ContentScale.Fit + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.tutorial_4_text), + color = Color.White, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium, + ) + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth() + ) { + BulletText(textRes = R.string.tutorial_4_subtext_1) + BulletText(textRes = R.string.tutorial_4_subtext_2) + BulletText(textRes = R.string.tutorial_4_subtext_3) + } + } + } diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt b/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt deleted file mode 100644 index 0cb88c48bca..00000000000 --- a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt +++ /dev/null @@ -1,70 +0,0 @@ -package fr.free.nrw.commons - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.core.net.toUri -import androidx.viewpager.widget.PagerAdapter -import fr.free.nrw.commons.utils.UnderlineUtils.setUnderlinedText -import fr.free.nrw.commons.utils.handleWebUrl - -class WelcomePagerAdapter : PagerAdapter() { - /** - * Gets total number of layouts - * @return Number of layouts - */ - override fun getCount(): Int = PAGE_LAYOUTS.size - - /** - * Compares given view with provided object - * @param view Adapter view - * @param obj Adapter object - * @return Equality between view and object - */ - override fun isViewFromObject(view: View, obj: Any): Boolean = (view === obj) - - /** - * Provides a way to remove an item from container - * @param container Adapter view group container - * @param position Index of item - * @param obj Adapter object - */ - override fun destroyItem(container: ViewGroup, position: Int, obj: Any) = - container.removeView(obj as View) - - override fun instantiateItem(container: ViewGroup, position: Int): Any { - val inflater = LayoutInflater.from(container.context) - val layout = inflater.inflate(PAGE_LAYOUTS[position], container, false) as ViewGroup - - // If final page - if (position == PAGE_LAYOUTS.size - 1) { - // Add link to more information - val moreInfo = layout.findViewById(R.id.welcomeInfo) - setUnderlinedText(moreInfo, R.string.welcome_help_button_text) - moreInfo.setOnClickListener { - handleWebUrl( - container.context, - "https://commons.wikimedia.org/wiki/Help:Contents".toUri() - ) - } - - // Handle click of finishTutorialButton ("YES!" button) inside layout - layout.findViewById(R.id.finishTutorialButton) - .setOnClickListener { view: View? -> (container.context as WelcomeActivity).finishTutorial() } - } - - container.addView(layout) - return layout - } - - companion object { - private val PAGE_LAYOUTS = intArrayOf( - R.layout.welcome_wikipedia, - R.layout.welcome_do_upload, - R.layout.welcome_dont_upload, - R.layout.welcome_image_example, - R.layout.welcome_final - ) - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeScreen.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeScreen.kt new file mode 100644 index 00000000000..cb415bd26f1 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeScreen.kt @@ -0,0 +1,171 @@ +package fr.free.nrw.commons + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import kotlinx.coroutines.launch +import androidx.compose.foundation.isSystemInDarkTheme + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun WelcomeScreen( + isBetaFlavour: Boolean, + pageCount: Int = 5, + onSkipClicked: () -> Unit, + onBackPressedAtStart: () -> Unit + +) { + val pagerState = rememberPagerState(pageCount = { 5 }) + val coroutineScope = rememberCoroutineScope() + var showDialog by rememberSaveable { mutableStateOf(isBetaFlavour) } + val backgroundColor = if (isSystemInDarkTheme()) { + Color(0xFF303030) + } else { + colorResource(id = R.color.primaryColor) + } + BackHandler { + if (pagerState.currentPage != 0) { + coroutineScope.launch { + pagerState.animateScrollToPage(pagerState.currentPage - 1) + } + } else { + onBackPressedAtStart() + } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + ) { + HorizontalPager( + state = pagerState, + modifier = Modifier.fillMaxSize() + ) { + page -> + when (page){ + 0 -> WelcomeWikipediaPage() + 1 -> WelcomeDoUploadPage() + 2 -> WelcomeDontUploadPage() + 3 -> WelcomeImageExamplePage() + 4 -> WelcomeFinalPage(onFinishClicked = onSkipClicked) + } + + } + + if (isBetaFlavour) { + Text( + text = stringResource(id = R.string.welcome_skip_button), + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 16.sp, + modifier = Modifier + .align(Alignment.TopEnd) + .safeDrawingPadding() + .padding(16.dp) + .clickable { onSkipClicked() } + ) + } + + PageIndicator( + pageCount = pageCount, + currentPage = pagerState.currentPage, + modifier = Modifier + .align(Alignment.BottomCenter) + .safeDrawingPadding() + .padding(bottom = 16.dp) + ) + } + + if (showDialog) { + CopyrightDialog( + onDismiss = { showDialog = false } + ) + } +} + +@Composable +fun PageIndicator(pageCount: Int, currentPage: Int, modifier: Modifier = Modifier) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier + ) { + repeat(pageCount) { index -> + val color = if (index == currentPage) Color.White else Color.White.copy(alpha = 0.5f) + Box( + modifier = Modifier + .size(8.dp) + .clip(CircleShape) + .background(color) + ) + } + } +} +@Composable +fun CopyrightDialog(onDismiss: () -> Unit) { + Dialog( + onDismissRequest = {}, + properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false) + ) { + Surface( + modifier = Modifier + .width(350.dp) + .height(350.dp), + shape = RoundedCornerShape(16.dp), + color = MaterialTheme.colorScheme.surface + ) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(37.dp)) + + Text( + text = stringResource(id = R.string.warning), + color = Color(0xFFFF0202), + fontSize = 34.sp, + fontWeight = FontWeight.Bold + ) + + Spacer(modifier = Modifier.height(21.dp)) + + Text( + text = stringResource(id = R.string.copyright_popup), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + Spacer(modifier = Modifier.height(37.dp)) + + Button( + onClick = { onDismiss() } + ) { + Text(text = stringResource(id = R.string.ok)) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeWikipediaPage.kt b/app/src/main/java/fr/free/nrw/commons/WelcomeWikipediaPage.kt new file mode 100644 index 00000000000..737bdc054b8 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeWikipediaPage.kt @@ -0,0 +1,67 @@ +package fr.free.nrw.commons + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import fr.free.nrw.commons.R + +@Composable +fun WelcomeWikipediaPage() { + val configuration = LocalConfiguration.current + val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = dimensionResource(id = R.dimen.huge_gap)) + .verticalScroll(rememberScrollState()) + .padding(top = 32.dp, bottom = 80.dp) + .safeDrawingPadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + + Image( + painter = painterResource(id = R.drawable.welcome_wikipedia), + contentDescription = stringResource(id = R.string.welcome_image_welcome_wikipedia), + modifier = Modifier + .size(if (isLandscape) 200.dp else 300.dp), + contentScale = ContentScale.Fit + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.tutorial_1_text), + color = Color.White, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium, + ) + + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.standard_gap))) + + Text( + text = stringResource(id = R.string.tutorial_1_subtext), + color = Color.White, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodyMedium + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/theme/Color.kt b/app/src/main/java/fr/free/nrw/commons/theme/Color.kt new file mode 100644 index 00000000000..b10c4838497 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/theme/Color.kt @@ -0,0 +1,11 @@ +package fr.free.nrw.commons.theme + +import androidx.compose.ui.graphics.Color + +val CommonsPrimary = Color(0xFF006699) +val CommonsPrimaryDark = Color(0xFF004466) +val CommonsAccent = Color(0xFF339966) +val CommonsWhite = Color(0xFFFFFFFF) +val CommonsBlack = Color(0xFF000000) +val CommonsBackgroundLight = Color(0xFFF8F9FA) +val CommonsBackgroundDark = Color(0xFF121212) \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/theme/Theme.kt b/app/src/main/java/fr/free/nrw/commons/theme/Theme.kt new file mode 100644 index 00000000000..cbb6fc21d6c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/theme/Theme.kt @@ -0,0 +1,64 @@ +package fr.free.nrw.commons.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = CommonsPrimary, + secondary = CommonsAccent, + background = CommonsBackgroundDark, + surface = CommonsBackgroundDark, + onPrimary = CommonsWhite, + onBackground = CommonsWhite +) + +private val LightColorScheme = lightColorScheme( + primary = CommonsPrimary, + secondary = CommonsAccent, + background = CommonsBackgroundLight, + surface = CommonsBackgroundLight, + onPrimary = CommonsWhite, + onBackground = CommonsBlack +) + +@Composable +fun CommonsTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = false, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/res/layout/popup_for_copyright.xml b/app/src/main/res/layout/popup_for_copyright.xml deleted file mode 100644 index 0b4075b8da2..00000000000 --- a/app/src/main/res/layout/popup_for_copyright.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - -