add possibility to move the main navigation to the bottom (#1808)
* add possibility to move the main navigation to the bottom * add top toolbar with drawer toggle, title and search button
This commit is contained in:
parent
70d426975f
commit
74bd493878
10 changed files with 188 additions and 61 deletions
|
@ -31,6 +31,7 @@ import android.view.View
|
|||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
|
@ -59,7 +60,10 @@ import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
|||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||
import com.keylesspalace.tusky.pager.MainPagerAdapter
|
||||
import com.keylesspalace.tusky.util.*
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import com.mikepenz.materialdrawer.iconics.iconicsIcon
|
||||
import com.mikepenz.materialdrawer.model.*
|
||||
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||
|
@ -89,8 +93,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
private lateinit var drawerToggle: ActionBarDrawerToggle
|
||||
|
||||
private var notificationTabPosition = 0
|
||||
|
||||
private var adapter: MainPagerAdapter? = null
|
||||
private var onTabSelectedListener: OnTabSelectedListener? = null
|
||||
|
||||
private val emojiInitCallback = object : InitCallback() {
|
||||
override fun onInitialized() {
|
||||
|
@ -159,6 +162,19 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
|
||||
startActivity(composeIntent)
|
||||
}
|
||||
|
||||
mainToolbar.menu.add(R.string.action_search).apply {
|
||||
setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = ThemeUtils.getColor(this@MainActivity, android.R.attr.textColorPrimary)
|
||||
}
|
||||
setOnMenuItemClickListener {
|
||||
startActivity(SearchActivity.getIntent(this@MainActivity))
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
setupDrawer(savedInstanceState)
|
||||
|
||||
/* Fetch user info while we're doing other things. This has to be done after setting up the
|
||||
|
@ -167,30 +183,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
setupTabs(showNotificationTab)
|
||||
|
||||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||
viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
|
||||
|
||||
val uswSwipeForTabs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("enableSwipeForTabs", true)
|
||||
viewPager.isUserInputEnabled = uswSwipeForTabs
|
||||
|
||||
tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
if (tab.position == notificationTabPosition) {
|
||||
NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
val fragment = adapter?.getFragment(tab.position)
|
||||
if (fragment is ReselectableFragment) {
|
||||
(fragment as ReselectableFragment).onReselect()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Setup push notifications
|
||||
if (NotificationHelper.areNotificationsEnabled(this, accountManager)) {
|
||||
NotificationHelper.enablePullNotifications(this)
|
||||
|
@ -376,13 +368,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
startActivityWithSlideInAnimation(ListsActivity.newIntent(context))
|
||||
}
|
||||
},
|
||||
primaryDrawerItem {
|
||||
nameRes = R.string.action_search
|
||||
iconicsIcon = GoogleMaterial.Icon.gmd_search
|
||||
onClick = {
|
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(context))
|
||||
}
|
||||
},
|
||||
primaryDrawerItem {
|
||||
nameRes = R.string.action_access_saved_toot
|
||||
iconRes = R.drawable.ic_notebook
|
||||
|
@ -461,20 +446,37 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
|
||||
private fun setupTabs(selectNotificationTab: Boolean) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
val activeTabLayout = if(preferences.getString("mainNavPosition", "top") == "bottom") {
|
||||
val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize)
|
||||
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
|
||||
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
|
||||
tabLayout.hide()
|
||||
bottomTabLayout
|
||||
} else {
|
||||
bottomNav.hide()
|
||||
(viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
|
||||
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager
|
||||
tabLayout
|
||||
}
|
||||
|
||||
val tabs = accountManager.activeAccount!!.tabPreferences
|
||||
adapter = MainPagerAdapter(tabs, this)
|
||||
|
||||
val adapter = MainPagerAdapter(tabs, this)
|
||||
viewPager.adapter = adapter
|
||||
TabLayoutMediator(tabLayout, viewPager, TabConfigurationStrategy { _: TabLayout.Tab?, _: Int -> }).attach()
|
||||
tabLayout.removeAllTabs()
|
||||
TabLayoutMediator(activeTabLayout, viewPager, TabConfigurationStrategy { _: TabLayout.Tab?, _: Int -> }).attach()
|
||||
activeTabLayout.removeAllTabs()
|
||||
for (i in tabs.indices) {
|
||||
val tab = tabLayout.newTab()
|
||||
val tab = activeTabLayout.newTab()
|
||||
.setIcon(tabs[i].icon)
|
||||
if (tabs[i].id == LIST) {
|
||||
tab.contentDescription = tabs[i].arguments[1]
|
||||
} else {
|
||||
tab.setContentDescription(tabs[i].text)
|
||||
}
|
||||
tabLayout.addTab(tab)
|
||||
activeTabLayout.addTab(tab)
|
||||
|
||||
if (tabs[i].id == NOTIFICATIONS) {
|
||||
notificationTabPosition = i
|
||||
if (selectNotificationTab) {
|
||||
|
@ -482,6 +484,40 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||
viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
|
||||
|
||||
val uswSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true)
|
||||
viewPager.isUserInputEnabled = uswSwipeForTabs
|
||||
|
||||
onTabSelectedListener?.let {
|
||||
activeTabLayout.removeOnTabSelectedListener(it)
|
||||
}
|
||||
|
||||
onTabSelectedListener = object : OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
if (tab.position == notificationTabPosition) {
|
||||
NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager)
|
||||
}
|
||||
|
||||
mainToolbar.title = tabs[tab.position].title(this@MainActivity)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
val fragment = adapter.getFragment(tab.position)
|
||||
if (fragment is ReselectableFragment) {
|
||||
(fragment as ReselectableFragment).onReselect()
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
activeTabLayout.addOnTabSelectedListener(it)
|
||||
}
|
||||
|
||||
mainToolbar.title = tabs[0].title(this@MainActivity)
|
||||
|
||||
}
|
||||
|
||||
private fun handleProfileClick(profile: IProfile, current: Boolean): Boolean {
|
||||
|
|
|
@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
|||
|
||||
}
|
||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
|
||||
"useBlurhash", "showCardsInTimelines", "confirmReblogs", "enableSwipeForTabs" -> {
|
||||
"useBlurhash", "showCardsInTimelines", "confirmReblogs", "enableSwipeForTabs", "mainNavPosition" -> {
|
||||
restartActivitiesOnExit = true
|
||||
}
|
||||
"language" -> {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
|
@ -36,17 +37,58 @@ data class TabData(val id: String,
|
|||
@StringRes val text: Int,
|
||||
@DrawableRes val icon: Int,
|
||||
val fragment: (List<String>) -> Fragment,
|
||||
val arguments: List<String> = emptyList())
|
||||
val arguments: List<String> = emptyList(),
|
||||
val title: (Context) -> String = { context -> context.getString(text)}
|
||||
)
|
||||
|
||||
fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabData {
|
||||
return when (id) {
|
||||
HOME -> TabData(HOME, R.string.title_home, R.drawable.ic_home_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.HOME) })
|
||||
NOTIFICATIONS -> TabData(NOTIFICATIONS, R.string.title_notifications, R.drawable.ic_notifications_24dp, { NotificationsFragment.newInstance() })
|
||||
LOCAL -> TabData(LOCAL, R.string.title_public_local, R.drawable.ic_local_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL) })
|
||||
FEDERATED -> TabData(FEDERATED, R.string.title_public_federated, R.drawable.ic_public_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED) })
|
||||
DIRECT -> TabData(DIRECT, R.string.title_direct_messages, R.drawable.ic_reblog_direct_24dp, { ConversationsFragment.newInstance() })
|
||||
HASHTAG -> TabData(HASHTAG, R.string.hashtags, R.drawable.ic_hashtag, { args -> TimelineFragment.newHashtagInstance(args) }, arguments)
|
||||
LIST -> TabData(LIST, R.string.list, R.drawable.ic_list, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty()) }, arguments)
|
||||
HOME -> TabData(
|
||||
HOME,
|
||||
R.string.title_home,
|
||||
R.drawable.ic_home_24dp,
|
||||
{ TimelineFragment.newInstance(TimelineFragment.Kind.HOME) }
|
||||
)
|
||||
NOTIFICATIONS -> TabData(
|
||||
NOTIFICATIONS,
|
||||
R.string.title_notifications,
|
||||
R.drawable.ic_notifications_24dp,
|
||||
{ NotificationsFragment.newInstance() }
|
||||
)
|
||||
LOCAL -> TabData(
|
||||
LOCAL,
|
||||
R.string.title_public_local,
|
||||
R.drawable.ic_local_24dp,
|
||||
{ TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL) }
|
||||
)
|
||||
FEDERATED -> TabData(
|
||||
FEDERATED,
|
||||
R.string.title_public_federated,
|
||||
R.drawable.ic_public_24dp,
|
||||
{ TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED) }
|
||||
)
|
||||
DIRECT -> TabData(
|
||||
DIRECT,
|
||||
R.string.title_direct_messages,
|
||||
R.drawable.ic_reblog_direct_24dp,
|
||||
{ ConversationsFragment.newInstance() }
|
||||
)
|
||||
HASHTAG -> TabData(
|
||||
HASHTAG,
|
||||
R.string.hashtags,
|
||||
R.drawable.ic_hashtag,
|
||||
{ args -> TimelineFragment.newHashtagInstance(args) },
|
||||
arguments,
|
||||
{ context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) }}
|
||||
)
|
||||
LIST -> TabData(
|
||||
LIST,
|
||||
R.string.list,
|
||||
R.drawable.ic_list,
|
||||
{ args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty()) },
|
||||
arguments,
|
||||
{ arguments.getOrNull(1).orEmpty() }
|
||||
)
|
||||
else -> throw IllegalArgumentException("unknown tab type")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,15 @@ class PreferencesFragment : PreferenceFragmentCompat() {
|
|||
icon = makeIcon(GoogleMaterial.Icon.gmd_format_size)
|
||||
}
|
||||
|
||||
listPreference {
|
||||
setDefaultValue("top")
|
||||
setEntries(R.array.pref_main_nav_position_options)
|
||||
setEntryValues(R.array.pref_main_nav_position_values)
|
||||
key = PrefKeys.MAIN_NAV_POSITION
|
||||
setSummaryProvider { entry }
|
||||
setTitle(R.string.pref_main_nav_position)
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(false)
|
||||
key = PrefKeys.FAB_HIDE
|
||||
|
|
|
@ -21,6 +21,7 @@ object PrefKeys {
|
|||
const val FAB_HIDE = "fabHide"
|
||||
const val LANGUAGE = "language"
|
||||
const val STATUS_TEXT_SIZE = "statusTextSize"
|
||||
const val MAIN_NAV_POSITION = "mainNavPosition"
|
||||
const val ABSOLUTE_TIME_VIEW = "absoluteTimeView"
|
||||
const val SHOW_BOT_OVERLAY = "showBotOverlay"
|
||||
const val ANIMATE_GIF_AVATARS = "animateGifAvatars"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
@ -51,6 +52,13 @@ public class ThemeUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static int getDimension(@NonNull Context context, @AttrRes int attribute) {
|
||||
TypedArray array = context.obtainStyledAttributes(new int[] { attribute });
|
||||
int dimen = array.getDimensionPixelSize(0, -1);
|
||||
array.recycle();
|
||||
return dimen;
|
||||
}
|
||||
|
||||
/** this can be replaced with drawableTint in xml once minSdkVersion >= 23 */
|
||||
@Nullable
|
||||
public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) {
|
||||
|
|
|
@ -22,19 +22,17 @@
|
|||
android:id="@+id/mainToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetStartWithNavigation="0dp">
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_scrollFlags="scroll|enterAlways" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/TuskyTabAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed"
|
||||
app:tabUnboundedRipple="false" />
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
style="@style/TuskyTabAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -42,15 +40,32 @@
|
|||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/tabLayout"
|
||||
android:layout_marginBottom="?attr/actionBarSize"
|
||||
android:background="?attr/windowBackgroundColor"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottomNav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:contentInsetStart="0dp"
|
||||
app:fabAlignmentMode="end">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/bottomTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabIndicator="@null"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/composeButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_margin="@dimen/fabMargin"
|
||||
android:contentDescription="@string/action_compose"
|
||||
app:layout_anchor="@id/viewPager"
|
||||
app:layout_anchorGravity="bottom|end"
|
||||
|
|
|
@ -48,4 +48,6 @@
|
|||
|
||||
<dimen name="adaptive_bitmap_inner_size">72dp</dimen>
|
||||
<dimen name="adaptive_bitmap_outer_size">108dp</dimen>
|
||||
|
||||
<dimen name="fabMargin">16dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -110,6 +110,15 @@
|
|||
<item>ja</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref_main_nav_position_options">
|
||||
<item>@string/pref_main_nav_position_option_top</item>
|
||||
<item>@string/pref_main_nav_position_option_bottom</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref_main_nav_position_values">
|
||||
<item>top</item>
|
||||
<item>bottom</item>
|
||||
</string-array>
|
||||
|
||||
<string name="description_status" translatable="false">
|
||||
<!-- Display name, cw?, content?, poll? relative date, reposted by?, reposted?, favorited?, bookmarked?, username, media?; visibility, fav number?, reblog number?-->
|
||||
|
|
|
@ -252,6 +252,11 @@
|
|||
<string name="pref_publishing">Publishing (synced with server)</string>
|
||||
<string name="pref_failed_to_sync">Failed to sync settings</string>
|
||||
|
||||
<string name="pref_main_nav_position">Main navigation position</string>
|
||||
<string name="pref_main_nav_position_option_top">Top</string>
|
||||
<string name="pref_main_nav_position_option_bottom">Bottom</string>
|
||||
|
||||
|
||||
<string name="post_privacy_public">Public</string>
|
||||
<string name="post_privacy_unlisted">Unlisted</string>
|
||||
<string name="post_privacy_followers_only">Followers-only</string>
|
||||
|
|
Loading…
Reference in a new issue