Merge branch 'tutorial' into 'master'
Tutorials See merge request pixeldroid/PixelDroid!601
This commit is contained in:
commit
49b4f7e445
|
@ -171,7 +171,7 @@ dependencies {
|
|||
implementation 'androidx.hilt:hilt-common:1.2.0'
|
||||
implementation 'androidx.hilt:hilt-work:1.2.0'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.1'
|
||||
|
||||
/**
|
||||
* AndroidX dependencies:
|
||||
|
@ -226,6 +226,9 @@ dependencies {
|
|||
|
||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||
|
||||
//Interactive tutorial
|
||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.14.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
|
||||
//Dagger (dependency injection)
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
|||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
|
@ -20,6 +21,8 @@ import org.pixeldroid.app.BuildConfig
|
|||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityLoginBinding
|
||||
import org.pixeldroid.app.main.MainActivity
|
||||
import org.pixeldroid.app.settings.SettingsActivity
|
||||
import org.pixeldroid.app.settings.TutorialSettingsDialog.Companion.START_TUTORIAL
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.openUrl
|
||||
|
@ -89,11 +92,25 @@ class LoginActivity : BaseActivity() {
|
|||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
model.finishedLogin.collectLatest {
|
||||
if (it) {
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
intent.flags =
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
when (it) {
|
||||
LoginActivityViewModel.FinishedLogin.Finished -> {
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
intent.flags =
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
}
|
||||
LoginActivityViewModel.FinishedLogin.FinishedFirstTime -> MaterialAlertDialogBuilder(binding.root.context)
|
||||
.setMessage(R.string.first_time_question)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val intent = Intent(this@LoginActivity, SettingsActivity::class.java)
|
||||
intent.flags =
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
intent.putExtra(START_TUTORIAL, true)
|
||||
startActivity(intent)
|
||||
}
|
||||
.setNegativeButton(R.string.skip_tutorial) { _, _ -> model.finishLogin()}
|
||||
.show()
|
||||
LoginActivityViewModel.FinishedLogin.NotFinished -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,10 @@ class LoginActivityViewModel @Inject constructor(
|
|||
private val _loadingState: MutableStateFlow<LoginState> = MutableStateFlow(LoginState(LoginState.LoadingState.Resting))
|
||||
val loadingState = _loadingState.asStateFlow()
|
||||
|
||||
private val _finishedLogin = MutableStateFlow(false)
|
||||
enum class FinishedLogin {
|
||||
NotFinished, Finished, FinishedFirstTime
|
||||
}
|
||||
private val _finishedLogin = MutableStateFlow(FinishedLogin.NotFinished)
|
||||
val finishedLogin = _finishedLogin.asStateFlow()
|
||||
|
||||
private val _promptOauth: MutableStateFlow<PromptOAuth?> = MutableStateFlow(null)
|
||||
|
@ -207,6 +210,7 @@ class LoginActivityViewModel @Inject constructor(
|
|||
|
||||
private suspend fun storeUser(accessToken: String, refreshToken: String?, clientId: String, clientSecret: String, instance: String) {
|
||||
try {
|
||||
val firstTime = db.userDao().getActiveUser() == null
|
||||
val user = pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
||||
db.userDao().deActivateActiveUsers()
|
||||
addUser(
|
||||
|
@ -220,12 +224,14 @@ class LoginActivityViewModel @Inject constructor(
|
|||
clientSecret = clientSecret
|
||||
)
|
||||
apiHolder.setToCurrentUser()
|
||||
|
||||
fetchNotifications()
|
||||
|
||||
_finishedLogin.value = if(firstTime) FinishedLogin.FinishedFirstTime else FinishedLogin.Finished
|
||||
} catch (exception: Exception) {
|
||||
return failedRegistration(R.string.verify_credentials)
|
||||
}
|
||||
|
||||
fetchNotifications()
|
||||
_finishedLogin.value = true
|
||||
}
|
||||
|
||||
// Fetch the latest notifications of this account, to avoid launching old notifications
|
||||
|
@ -280,4 +286,8 @@ class LoginActivityViewModel @Inject constructor(
|
|||
_loadingState.value = LoginState(LoginState.LoadingState.Resting)
|
||||
}
|
||||
|
||||
fun finishLogin() {
|
||||
_finishedLogin.value = FinishedLogin.Finished
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,10 @@ import android.util.Log
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
|
@ -34,6 +37,8 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
|
|||
import androidx.viewpager2.widget.ViewPager2
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.bumptech.glide.Glide
|
||||
import com.getkeepsafe.taptargetview.TapTarget
|
||||
import com.getkeepsafe.taptargetview.TapTargetView
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
|
@ -50,6 +55,7 @@ import com.mikepenz.materialdrawer.model.interfaces.nameText
|
|||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.ligi.tracedroid.sending.sendTraceDroidStackTracesIfExist
|
||||
import org.pixeldroid.app.R
|
||||
|
@ -66,6 +72,8 @@ import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
|||
import org.pixeldroid.app.profile.ProfileActivity
|
||||
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||
import org.pixeldroid.app.settings.SettingsActivity
|
||||
import org.pixeldroid.app.settings.SettingsActivity.SettingsFragment
|
||||
import org.pixeldroid.app.settings.TutorialSettingsDialog.Companion.START_TUTORIAL
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.Tab
|
||||
import org.pixeldroid.app.utils.api.objects.Notification
|
||||
|
@ -87,6 +95,7 @@ import java.time.Instant
|
|||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
private lateinit var tabStored: List<Tab>
|
||||
private lateinit var header: AccountHeaderView
|
||||
private var user: UserDatabaseEntity? = null
|
||||
|
||||
|
@ -127,10 +136,14 @@ class MainActivity : BaseActivity() {
|
|||
setupTabs()
|
||||
|
||||
val showNotification: Boolean = intent.getBooleanExtra(SHOW_NOTIFICATION_TAG, false)
|
||||
val showTutorial: Int = intent.getIntExtra(START_TUTORIAL, -1)
|
||||
|
||||
if(showNotification){
|
||||
binding.viewPager.currentItem = 3
|
||||
} else if(showTutorial >= 0) {
|
||||
showTutorial(showTutorial)
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(applicationContext,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
|
@ -141,6 +154,220 @@ class MainActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showTutorial(showTutorial: Int) {
|
||||
when(showTutorial){
|
||||
0 -> tutorialOnTabs(Tab.HOME_FEED)
|
||||
1 -> tutorialOnTabs(Tab.CREATE_FEED)
|
||||
2 -> dmTutorial()
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
private fun tutorialOnTabs(tab: Tab) {
|
||||
val target = (binding.tabs as? NavigationBarView)?.let{ findTab(it, tab) } ?: return //TODO tablet landscape not supported
|
||||
|
||||
when(tab){
|
||||
Tab.HOME_FEED -> homeTutorial(target)
|
||||
Tab.SEARCH_DISCOVER_FEED -> homeTutorialSearch(target)
|
||||
Tab.PUBLIC_FEED -> homeTutorialPublic(target)
|
||||
Tab.NOTIFICATIONS_FEED -> homeTutorialNotifications(target)
|
||||
Tab.CREATE_FEED -> createTutorial(target)
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
private fun dmTutorial(){
|
||||
val target = (binding.tabs as? NavigationBarView)?.let{ findTab(it, Tab.DIRECT_MESSAGES) } ?: binding.mainDrawerButton ?: return //TODO tablet landscape not supported
|
||||
|
||||
if(target is ImageButton) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target, getString(R.string.dm_tutorial_drawer))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
dmTutorial2(null)
|
||||
}
|
||||
})
|
||||
} else dmTutorial2(target)
|
||||
}
|
||||
private fun findViewWithText(root: ViewGroup, text: String?): View? {
|
||||
for (i in 0 until root.childCount) {
|
||||
val child = root.getChildAt(i)
|
||||
if (child is TextView) {
|
||||
if (child.text.toString().contains(text!!)) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
if (child is ViewGroup) {
|
||||
val result = findViewWithText(child, text)
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private fun dmTutorial2(target: View?) {
|
||||
lifecycleScope.launch {
|
||||
var target = target ?: findViewWithText(binding.drawer as ViewGroup, getString(R.string.direct_messages))
|
||||
while (target == null) {
|
||||
target = findViewWithText(binding.drawer as ViewGroup, getString(R.string.direct_messages))
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target, getString(R.string.direct_messages),
|
||||
getString(R.string.dm_tutorial_text))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
//tutorialOnTabs(Tab.NOTIFICATIONS_FEED)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun homeTutorialPublic(target: View) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target, getString(R.string.public_feed),
|
||||
getString(R.string.public_feed_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
tutorialOnTabs(Tab.NOTIFICATIONS_FEED)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun homeTutorialNotifications(target: View) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target,
|
||||
getString(R.string.notifications_tutorial_title),
|
||||
getString(R.string.notifications_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun homeTutorialSearch(target: View) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target,
|
||||
getString(R.string.discover_tutorial_title),
|
||||
getString(R.string.discover_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
tutorialOnTabs(Tab.PUBLIC_FEED)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun homeTutorial(target: View) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target,
|
||||
getString(R.string.home_feed_tutorial_title),
|
||||
getString(R.string.home_feed_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
tutorialOnTabs(Tab.SEARCH_DISCOVER_FEED)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun createTutorial(target: View) {
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(target, getString(R.string.create_tutorial_title),
|
||||
getString(R.string.create_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
target.performClick()
|
||||
lifecycleScope.launch {
|
||||
var targetCamera = findViewById<View>(R.id.camera_capture_button)
|
||||
while (targetCamera == null) {
|
||||
targetCamera = findViewById(R.id.camera_capture_button)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
this@MainActivity,
|
||||
TapTarget.forView(targetCamera,
|
||||
getString(R.string.create_tutorial_title_2),
|
||||
getString(R.string.create_tutorial_explanation_2))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
targetCamera.performClick()
|
||||
}
|
||||
|
||||
override fun onTargetCancel(view: TapTargetView?) {
|
||||
super.onTargetCancel(view)
|
||||
intent.removeExtra(START_TUTORIAL)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun findTab(navBar: NavigationBarView, tab: Tab): View? {
|
||||
val index = tabStored.indexOf(tab)
|
||||
for (i in 0 until navBar.childCount) {
|
||||
val child = navBar.getChildAt(i)
|
||||
if (child is ViewGroup) {
|
||||
return child.getChildAt(index)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val notificationPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted: Boolean ->
|
||||
|
@ -515,6 +742,8 @@ class MainActivity : BaseActivity() {
|
|||
)
|
||||
}
|
||||
|
||||
tabStored = tabs
|
||||
|
||||
val bottomNavigationMenu: Menu? = (binding.tabs as? NavigationBarView)?.menu?.apply {
|
||||
clear()
|
||||
}
|
||||
|
|
|
@ -5,8 +5,15 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import com.getkeepsafe.taptargetview.TapTarget
|
||||
import com.getkeepsafe.taptargetview.TapTargetView
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
|
@ -58,6 +65,126 @@ class PostCreationActivity : BaseActivity() {
|
|||
supportFragmentManager.findFragmentById(R.id.postCreationContainer) as NavHostFragment
|
||||
navController = navHostFragment.navController
|
||||
navController.setGraph(R.navigation.post_creation_graph)
|
||||
|
||||
lifecycleScope.launch {
|
||||
var targetCamera = findViewById<View>(R.id.toggleStoryPost)
|
||||
while (targetCamera == null) {
|
||||
targetCamera = findViewById(R.id.toggleStoryPost)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forView(targetCamera,
|
||||
getString(R.string.story_tutorial_title),
|
||||
getString(R.string.story_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
findViewById<View>(R.id.buttonStory)?.performClick()
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forView(findViewById(R.id.editPhotoButton),
|
||||
getString(R.string.edit_tutorial_title),
|
||||
getString(R.string.edit_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
findViewById<View>(R.id.editPhotoButton)?.performClick()
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forView(findViewById(R.id.tv_caption),
|
||||
getString(R.string.media_description_tutorial_title),
|
||||
getString(R.string.media_description_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
findViewById<View>(R.id.tv_caption)?.performClick()
|
||||
lifecycleScope.launch {
|
||||
delay(1000)
|
||||
var tv_caption = findViewById<View>(R.id.tv_caption)
|
||||
while (tv_caption == null || tv_caption.visibility != View.VISIBLE) {
|
||||
tv_caption = findViewById(R.id.tv_caption)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forView(findViewById(R.id.post_creation_next_button),
|
||||
getString(
|
||||
R.string.picture_tutorial_title
|
||||
),
|
||||
getString(R.string.picture_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
findViewById<View>(R.id.post_creation_next_button)?.performClick()
|
||||
showAccountChooser()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAccountChooser() {
|
||||
lifecycleScope.launch {
|
||||
var toolbar = findViewById<View>(R.id.top_bar) as? MaterialToolbar
|
||||
while (toolbar == null) {
|
||||
toolbar = findViewById(R.id.top_bar) as? MaterialToolbar
|
||||
delay(100)
|
||||
}
|
||||
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forToolbarMenuItem(
|
||||
toolbar,
|
||||
R.id.action_switch_accounts,
|
||||
getString(R.string.switch_accounts_tutorial_title),
|
||||
getString(R.string.switch_accounts_tutorial_explanation)
|
||||
)
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
showPostButton()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPostButton() {
|
||||
TapTargetView.showFor(
|
||||
this@PostCreationActivity, // `this` is an Activity
|
||||
TapTarget.forView(findViewById(R.id.post_submission_send_button),
|
||||
getString(R.string.post_button_tutorial_title),
|
||||
getString(R.string.post_button_tutorial_explanation))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60),
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
findViewById<View>(R.id.post_creation_next_button)?.performClick()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp() = navController.navigateUp() || super.onSupportNavigateUp()
|
||||
|
|
|
@ -37,6 +37,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.databinding.FragmentCameraBinding
|
||||
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||
import org.pixeldroid.app.settings.TutorialSettingsDialog.Companion.START_TUTORIAL
|
||||
import org.pixeldroid.app.utils.BaseFragment
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
@ -68,6 +69,7 @@ class CameraFragment : BaseFragment() {
|
|||
|
||||
private var inActivity by Delegates.notNull<Boolean>()
|
||||
private var addToStory by Delegates.notNull<Boolean>()
|
||||
private var tutorial by Delegates.notNull<Int>()
|
||||
|
||||
private var filePermissionDialogLaunched: Boolean = false
|
||||
|
||||
|
@ -90,6 +92,8 @@ class CameraFragment : BaseFragment() {
|
|||
inActivity = arguments?.getBoolean(CAMERA_ACTIVITY) ?: false
|
||||
addToStory = arguments?.getBoolean(CAMERA_ACTIVITY_STORY) ?: false
|
||||
|
||||
tutorial = arguments?.getInt(START_TUTORIAL) ?: -1
|
||||
|
||||
binding = FragmentCameraBinding.inflate(layoutInflater)
|
||||
|
||||
return binding.root
|
||||
|
@ -457,6 +461,9 @@ class CameraFragment : BaseFragment() {
|
|||
if(addToStory){
|
||||
intent.putExtra(CAMERA_ACTIVITY_STORY, addToStory)
|
||||
}
|
||||
if(!inActivity && tutorial != -1){
|
||||
intent.putExtra(START_TUTORIAL, true)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.pixeldroid.app.settings
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
|
@ -16,13 +15,17 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.getkeepsafe.taptargetview.TapTarget
|
||||
import com.getkeepsafe.taptargetview.TapTargetView
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.LayoutTabsArrangeBinding
|
||||
import org.pixeldroid.app.utils.Tab
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.db.entities.TabsDatabaseEntity
|
||||
|
@ -31,20 +34,23 @@ import javax.inject.Inject
|
|||
@AndroidEntryPoint
|
||||
class ArrangeTabsFragment: DialogFragment() {
|
||||
|
||||
private lateinit var binding: LayoutTabsArrangeBinding
|
||||
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
private val model: ArrangeTabsViewModel by viewModels()
|
||||
|
||||
var showTutorial = false
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
|
||||
val inflater: LayoutInflater = requireActivity().layoutInflater
|
||||
val dialogView: View = inflater.inflate(R.layout.layout_tabs_arrange, null)
|
||||
binding = LayoutTabsArrangeBinding.inflate(layoutInflater)
|
||||
|
||||
val itemCount = model.initTabsChecked()
|
||||
model.initTabsButtons(itemCount, requireContext())
|
||||
|
||||
val listFeed: RecyclerView = dialogView.findViewById(R.id.tabs)
|
||||
val listFeed: RecyclerView = binding.tabs
|
||||
val listAdapter = ListViewAdapter(model)
|
||||
listFeed.adapter = listAdapter
|
||||
listFeed.layoutManager = LinearLayoutManager(requireActivity())
|
||||
|
@ -68,7 +74,7 @@ class ArrangeTabsFragment: DialogFragment() {
|
|||
val dialog = MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setIcon(R.drawable.outline_bottom_navigation)
|
||||
setTitle(R.string.arrange_tabs_summary)
|
||||
setView(dialogView)
|
||||
setView(binding.root)
|
||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
// Save values into preferences
|
||||
|
@ -81,10 +87,77 @@ class ArrangeTabsFragment: DialogFragment() {
|
|||
}
|
||||
}
|
||||
}.create()
|
||||
|
||||
if (showTutorial) showTutorial(dialog)
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun showTutorial(dialog: Dialog){
|
||||
lifecycleScope.launch {
|
||||
var handle = binding.tabs.findViewHolderForLayoutPosition(0)?.itemView?.findViewById<ImageView>(R.id.dragHandle)
|
||||
while (handle == null) {
|
||||
handle = binding.tabs.findViewHolderForLayoutPosition(0)?.itemView?.findViewById(R.id.dragHandle)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
dialog,
|
||||
TapTarget.forView(handle, getString(R.string.drag_customtabs_tutorial))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
val checkBox = binding.tabs.findViewHolderForLayoutPosition(0)?.itemView?.findViewById<View>(R.id.checkBox)
|
||||
TapTargetView.showFor(
|
||||
dialog,
|
||||
TapTarget.forView(checkBox,
|
||||
getString(R.string.de_activate_tabs_tutorial))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
val index = (Tab.defaultTabs + Tab.otherTabs).size - 1
|
||||
binding.tabs.scrollToPosition(index)
|
||||
lifecycleScope.launch {
|
||||
var hashtag =
|
||||
binding.tabs.findViewHolderForLayoutPosition(index)?.itemView?.findViewById<View>(
|
||||
R.id.textView
|
||||
)
|
||||
while (hashtag == null) {
|
||||
hashtag =
|
||||
binding.tabs.findViewHolderForLayoutPosition(index)?.itemView?.findViewById(
|
||||
R.id.textView
|
||||
)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
dialog,
|
||||
TapTarget.forView(
|
||||
hashtag,
|
||||
getString(R.string.custom_feed_tutorial_title),
|
||||
getString(R.string.custom_feed_tutorial_explanation)
|
||||
)
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
inner class ListViewAdapter(val model: ArrangeTabsViewModel):
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
|
|
|
@ -4,38 +4,62 @@ import android.content.Intent
|
|||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.addCallback
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.getkeepsafe.taptargetview.TapTarget
|
||||
import com.getkeepsafe.taptargetview.TapTargetView
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.SettingsBinding
|
||||
import org.pixeldroid.app.main.MainActivity
|
||||
import org.pixeldroid.app.settings.TutorialSettingsDialog.Companion.START_TUTORIAL
|
||||
import org.pixeldroid.app.utils.setThemeFromPreferences
|
||||
import org.pixeldroid.common.ThemedActivity
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private var restartMainOnExit = false
|
||||
private lateinit var binding: SettingsBinding
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val binding = SettingsBinding.inflate(layoutInflater)
|
||||
binding = SettingsBinding.inflate(layoutInflater)
|
||||
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.topBar)
|
||||
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, SettingsFragment())
|
||||
.replace(R.id.settings, SettingsFragment(), "topsettingsfragment")
|
||||
.commit()
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
val showTutorial = intent.getBooleanExtra(START_TUTORIAL, false)
|
||||
|
||||
if(showTutorial){
|
||||
lifecycleScope.launch {
|
||||
var target =
|
||||
(supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs(10)
|
||||
while (target == null) {
|
||||
target = (supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs(10)
|
||||
delay(100)
|
||||
}
|
||||
target.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this /* lifecycle owner */) {
|
||||
// Handle the back button event
|
||||
// If a setting (for example language or theme) was changed, the main activity should be
|
||||
|
@ -98,6 +122,36 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
|
|||
super.startActivity(intent)
|
||||
}
|
||||
|
||||
fun customTabsTutorial(){
|
||||
lifecycleScope.launch {
|
||||
var target =
|
||||
(supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs(5)
|
||||
while (target == null) {
|
||||
target = (supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs(5)
|
||||
delay(100)
|
||||
}
|
||||
TapTargetView.showFor(
|
||||
this@SettingsActivity,
|
||||
TapTarget.forView(target, getString(R.string.arrange_tabs_tutorial_title))
|
||||
.transparentTarget(true)
|
||||
.targetRadius(60), // Specify the target radius (in dp)
|
||||
object : TapTargetView.Listener() {
|
||||
// The listener can listen for regular clicks, long clicks or cancels
|
||||
override fun onTargetClick(view: TapTargetView?) {
|
||||
super.onTargetClick(view) // This call is optional
|
||||
// Perform action for the current target
|
||||
val dialogFragment = ArrangeTabsFragment().apply { showTutorial = true }
|
||||
dialogFragment.setTargetFragment(
|
||||
(supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment),
|
||||
0
|
||||
)
|
||||
dialogFragment.show(supportFragmentManager, "settings_fragment")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||
var dialogFragment: DialogFragment? = null
|
||||
|
@ -107,6 +161,8 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
|
|||
dialogFragment = LanguageSettingFragment()
|
||||
} else if (preference.key == "arrange_tabs") {
|
||||
dialogFragment = ArrangeTabsFragment()
|
||||
} else if (preference.key == "tutorial") {
|
||||
dialogFragment = TutorialSettingsDialog()
|
||||
}
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0)
|
||||
|
@ -115,6 +171,16 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
|
|||
super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
fun scrollToArrangeTabs(position: Int): View? {
|
||||
//Hardcoded positions because it's too annoying to find!
|
||||
|
||||
if (listView != null && position != -1) {
|
||||
listView.post {
|
||||
listView.smoothScrollToPosition(position)
|
||||
}
|
||||
}
|
||||
return listView.findViewHolderForAdapterPosition(position)?.itemView
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package org.pixeldroid.app.settings;
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.getkeepsafe.taptargetview.TapTarget
|
||||
import com.getkeepsafe.taptargetview.TapTargetView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.main.MainActivity
|
||||
import org.pixeldroid.app.utils.Tab
|
||||
|
||||
class TutorialSettingsDialog: DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val items = arrayOf(
|
||||
Pair(R.string.feeds_tutorial, R.drawable.ic_home_white_24dp),
|
||||
Pair(R.string.create_tutorial, R.drawable.photo_camera),
|
||||
Pair(R.string.dm_tutorial, R.drawable.message),
|
||||
Pair(R.string.custom_tabs_tutorial, R.drawable.outline_bottom_navigation)
|
||||
)
|
||||
|
||||
val adapter = object : ArrayAdapter<Pair<Int, Int>>(requireContext(), android.R.layout.simple_list_item_1, items) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
|
||||
val view: TextView = if (convertView == null) {
|
||||
LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false) as TextView
|
||||
} else {
|
||||
convertView as TextView
|
||||
}
|
||||
|
||||
val item = getItem(position)
|
||||
|
||||
if (item != null) {
|
||||
view.setText(item.first)
|
||||
view.setTypeface(null, Typeface.NORMAL) // Set the typeface to normal
|
||||
view.setCompoundDrawablesWithIntrinsicBounds(item.second, 0, 0, 0)
|
||||
view.compoundDrawablePadding = 16 // Add padding between text and drawable
|
||||
}
|
||||
|
||||
view.setPadding(0, 32, 0, 32)
|
||||
return view
|
||||
}
|
||||
}
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.tutorial_choice))
|
||||
.setAdapter(adapter) { _, which ->
|
||||
if(which == 3){
|
||||
customTabsTutorial()
|
||||
return@setAdapter
|
||||
}
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
intent.putExtra(START_TUTORIAL, which)
|
||||
startActivity(intent)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun customTabsTutorial() {
|
||||
(requireActivity() as SettingsActivity).customTabsTutorial()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val START_TUTORIAL = "tutorial_start_intent"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M19,2L5,2c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM13,18h-2v-2h2v2zM15.07,10.25l-0.9,0.92C13.45,11.9 13,12.5 13,14h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
|
||||
|
||||
</vector>
|
|
@ -1,5 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
</vector>
|
|
@ -1,5 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
|
||||
|
||||
</vector>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
|
||||
<path android:fillColor="?attr/colorOnBackground" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
|
||||
</vector>
|
|
@ -361,4 +361,43 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
|||
<string name="dm_target">Target username</string>
|
||||
<string name="new_dm">Message you want to write. Say hello!</string>
|
||||
<string name="new_dm_error">Error sending your message! Check the username</string>
|
||||
<string name="tutorial">Tutorial</string>
|
||||
<string name="tutorial_explanation">Explanations on how to use PixelDroid and Pixelfed</string>
|
||||
<string name="feeds_tutorial">Feeds, how do they work? Where do they come from?</string>
|
||||
<string name="create_tutorial">A little walk through creating posts</string>
|
||||
<string name="tutorial_choice">What could you use some help with?</string>
|
||||
<string name="dm_tutorial">Direct Messages: keep in touch!</string>
|
||||
<string name="custom_tabs_tutorial">Customize what tabs show up on the main PixelDroid screen!</string>
|
||||
<string name="first_time_question">It seems like it might be your first time using PixelDroid. Do you want to open a tutorial? You can always find the tutorials in the settings.</string>
|
||||
<string name="skip_tutorial">No, continue</string>
|
||||
<string name="dm_tutorial_text">Send messages to other Pixelfed users: on your instance or on others</string>
|
||||
<string name="public_feed_tutorial_explanation">This feed contains all the posts on your instance! Maybe you can find some interesting posts here :)</string>
|
||||
<string name="notifications_tutorial_title">Notifications keep you in the loop</string>
|
||||
<string name="notifications_tutorial_explanation">PixelDroid will also send you push notifications to make sure you don\'t miss anything!</string>
|
||||
<string name="home_feed_tutorial_title">This is your home feed</string>
|
||||
<string name="home_feed_tutorial_explanation">The posts of the people you follow will show up here. No algorithms, just chronological goodness. Only you decide what you want to see!</string>
|
||||
<string name="create_tutorial_title">This is where everything begins</string>
|
||||
<string name="create_tutorial_explanation">First, let\'s navigate to the create tab. Click here</string>
|
||||
<string name="create_tutorial_title_2">Take a picture to share</string>
|
||||
<string name="create_tutorial_explanation_2">It doesn\'t have to be very good for now</string>
|
||||
<string name="story_tutorial_title">Story or Post?</string>
|
||||
<string name="story_tutorial_explanation">Stories are short-lived: engage your followers and keep them coming back for more. Try them out!</string>
|
||||
<string name="edit_tutorial_title">Edit your picture to make it shine ✨</string>
|
||||
<string name="edit_tutorial_explanation">You can add filters, draw or add text, edit video, and more! 📷</string>
|
||||
<string name="media_description_tutorial_title">Don\'t forget to add a media description!</string>
|
||||
<string name="media_description_tutorial_explanation">This helps make Pixelfed accessible to everyone, and also lets you clarify what we\'re supposed to see in your pretty image ;)</string>
|
||||
<string name="picture_tutorial_title">Take a picture to share</string>
|
||||
<string name="picture_tutorial_explanation">It doesn\'t have to be very good for now</string>
|
||||
<string name="switch_accounts_tutorial_title">Switch accounts!</string>
|
||||
<string name="switch_accounts_tutorial_explanation">PixelDroid supports using multiple Pixelfed accounts. Make sure you don\'t post those cat pics on the dog-only instance! 😱</string>
|
||||
<string name="post_button_tutorial_title">Final stretch! Post that picture</string>
|
||||
<string name="post_button_tutorial_explanation">Have fun sharing your pictures with the world! Click anywhere else to cancel and keep looking around :)</string>
|
||||
<string name="drag_customtabs_tutorial">Drag this to change the order of the tabs</string>
|
||||
<string name="de_activate_tabs_tutorial">De-activate tabs you don\'t need</string>
|
||||
<string name="custom_feed_tutorial_title">Create a custom feed with a hashtag you like</string>
|
||||
<string name="custom_feed_tutorial_explanation">You really like cats? Try #caturday! Lakes? #lake! Or #hiking? And it will be right there in a tab</string>
|
||||
<string name="arrange_tabs_tutorial_title">First open the \"Arrange tabs\" settings</string>
|
||||
<string name="discover_tutorial_title">This tab can get you started finding interesting accounts to follow</string>
|
||||
<string name="discover_tutorial_explanation">Maybe take a look at the trending posts 📈, or discover some random posts every day to broaden your horizons and find the real gems! 💎</string>
|
||||
<string name="dm_tutorial_drawer">First open the drawer menu</string>
|
||||
</resources>
|
||||
|
|
|
@ -54,6 +54,11 @@
|
|||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/about">
|
||||
<ListPreference
|
||||
android:key="tutorial"
|
||||
android:title="@string/tutorial"
|
||||
android:summary="@string/tutorial_explanation"
|
||||
android:icon="@drawable/help" />
|
||||
<Preference android:title="@string/about"
|
||||
android:key="about"
|
||||
android:summary="@string/about_pixeldroid"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 702d14fe701343958337efa1b4eb31f0250849f6
|
||||
Subproject commit def947b5b1392b3282174bebd0217037f66d0362
|
Loading…
Reference in New Issue