Finished (?) tutorials

This commit is contained in:
Matthieu 2024-08-30 15:30:37 +02:00
parent 40a3a9ba26
commit 9ebeda23a3
9 changed files with 500 additions and 16 deletions

View File

@ -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:

View File

@ -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,7 @@ 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
@ -88,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
@ -128,13 +136,12 @@ 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) {
} else if(showTutorial >= 0) {
showTutorial(showTutorial)
}
if (ActivityCompat.checkSelfPermission(applicationContext,
@ -147,6 +154,216 @@ 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, "First open the drawer menu")
.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, "Direct Messages",
"Send messages to other Pixelfed users: on your instance or on others")
.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, "Public feed",
"This feed contains all the posts on your instance! Maybe you can find some interesting posts here :)")
.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, "Notifications keep you in the loop",
"PixelDroid will also send you push notifications to make sure you don't miss anything!")
.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, "This tab can get you started finding interesting accounts to follow",
"Maybe take a look at the trending posts \uD83D\uDCC8, or discover some random posts every day to broaden your horizons and find the real gems! \uD83D\uDC8E")
.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, "This is your home feed",
"The posts of the people you follow will show up here. No algorithms, just chronological goodness. Only you decide what you want to see!")
.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, "This is where everything begins",
"First, let's navigate to the create tab. Click here")
.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, "Take a picture to share",
"It doesn't have to be very good for now")
.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 ->
@ -521,6 +738,8 @@ class MainActivity : BaseActivity() {
)
}
tabStored = tabs
val bottomNavigationMenu: Menu? = (binding.tabs as? NavigationBarView)?.menu?.apply {
clear()
}

View File

@ -5,10 +5,19 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
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.settings.TutorialSettingsDialog.Companion.START_TUTORIAL
import org.pixeldroid.app.utils.BaseActivity
class PostCreationActivity : BaseActivity() {
@ -58,6 +67,119 @@ 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, "Story or Post?",
"Stories are short-lived: engage your followers and keep them coming back for more. Try them out!")
.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), "Edit your picture to make it shine ✨",
"You can add filters, draw or add text, edit video, and more! \uD83D\uDCF7")
.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), "Don't forget to add a media description!",
"This helps make Pixelfed accessible to everyone, and also lets you clarify what we're supposed to see in your pretty image ;)")
.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), "Take a picture to share",
"It doesn't have to be very good for now")
.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,
"Switch accounts!",
"PixelDroid supports using multiple Pixelfed accounts. Make sure you don't post those cat pics on the dog-only instance! \uD83D\uDE31"
)
.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), "Final stretch! Post that picture",
"Have fun sharing your pictures with the world! Click anywhere else to cancel and keep looking around :)")
.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()

View File

@ -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)
}
}

View File

@ -16,13 +16,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 +35,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 +75,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 +88,76 @@ 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, """Drag this to change the order of the tabs""")
.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, """De-activate tabs you don't need""")
.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,
"""Create a custom feed with a hashtag you like""",
"You really like cats? Try #caturday! Lakes? #lake! Or #hiking? And it will be right there in a tab"
)
.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>() {

View File

@ -4,35 +4,45 @@ 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.PreferenceGroup
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.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)
@ -98,6 +108,36 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
super.startActivity(intent)
}
fun customTabsTutorial(){
lifecycleScope.launch {
var target =
(supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs()
while (target == null) {
target = (supportFragmentManager.findFragmentByTag("topsettingsfragment") as? SettingsFragment)?.scrollToArrangeTabs()
delay(100)
}
TapTargetView.showFor(
this@SettingsActivity,
TapTarget.forView(target, """First open the "Arrange tabs" settings""")
.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
@ -117,6 +157,17 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
super.onDisplayPreferenceDialog(preference)
}
}
fun scrollToArrangeTabs(): View? {
//Hardcoded because it's too annoying to find!
val position = 5
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)

View File

@ -1,7 +1,6 @@
package org.pixeldroid.app.settings;
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
@ -11,9 +10,12 @@ 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() {
@ -21,8 +23,8 @@ class TutorialSettingsDialog: DialogFragment() {
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.feeds_tutorial, R.drawable.notifications),
Pair(R.string.feeds_tutorial, R.drawable.outline_bottom_navigation)
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) {
@ -50,6 +52,10 @@ class TutorialSettingsDialog: DialogFragment() {
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)
@ -61,6 +67,10 @@ class TutorialSettingsDialog: DialogFragment() {
.create()
}
private fun customTabsTutorial() {
(requireActivity() as SettingsActivity).customTabsTutorial()
}
companion object {
const val START_TUTORIAL = "tutorial_start_intent"
}

View File

@ -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>

View File

@ -366,4 +366,6 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</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>
</resources>