Tab customization & direct messages tab (#1012)

* custom tabs

* custom tabs interface

* implement custom tab functionality

* add database migration

* fix bugs, improve ThemeUtils nullability handling

* implement conversationsfragment

* setup ConversationViewHolder

* implement favs

* add button functionality

* revert 10.json

* revert item_status_notification.xml

* implement more menu, replying, fix stuff, clean up

* fix tests

* fix bug with expanding statuses

* min and max number of tabs

* settings support, fix bugs

* database migration

* fix scrolling to top after refresh

* fix                                 bugs

* fix warning in item_conversation
This commit is contained in:
Konrad Pozniak 2019-02-12 19:22:37 +01:00 committed by GitHub
commit e371fa0e24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 3663 additions and 296 deletions

View file

@ -72,10 +72,10 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
super.onViewCreated(view, savedInstanceState)
recyclerView.setHasFixedSize(true)
val layoutManager = LinearLayoutManager(context)
val layoutManager = LinearLayoutManager(view.context)
recyclerView.layoutManager = layoutManager
val divider = DividerItemDecoration(context, layoutManager.orientation)
val drawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable, R.drawable.status_divider_dark)
val divider = DividerItemDecoration(view.context, layoutManager.orientation)
val drawable = ThemeUtils.getDrawable(view.context, R.attr.status_divider_drawable, R.drawable.status_divider_dark)
divider.setDrawable(drawable)
recyclerView.addItemDecoration(divider)

View file

@ -81,9 +81,10 @@ class AccountMediaFragment : BaseFragment(), Injectable {
private val callback = object : Callback<List<Status>> {
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) {
fetchingStatus = FetchingStatus.NOT_FETCHING
if (isAdded) {
swipe_refresh_layout.isRefreshing = false
progress_bar.visibility = View.GONE
if(isAdded) {
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
statusView.show()
if (t is IOException) {
statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
@ -101,9 +102,9 @@ class AccountMediaFragment : BaseFragment(), Injectable {
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
fetchingStatus = FetchingStatus.NOT_FETCHING
if (isAdded) {
swipe_refresh_layout.isRefreshing = false
progress_bar.visibility = View.GONE
if(isAdded) {
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
val body = response.body()
body?.let { fetched ->
@ -114,6 +115,7 @@ class AccountMediaFragment : BaseFragment(), Injectable {
result.addAll(AttachmentViewData.list(status))
}
adapter.addTop(result)
if (statuses.isEmpty()) {
statusView.show()
statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
@ -159,19 +161,19 @@ class AccountMediaFragment : BaseFragment(), Injectable {
super.onViewCreated(view, savedInstanceState)
val columnCount = context?.resources?.getInteger(R.integer.profile_media_column_count) ?: 2
val layoutManager = GridLayoutManager(context, columnCount)
val columnCount = view.context.resources.getInteger(R.integer.profile_media_column_count)
val layoutManager = GridLayoutManager(view.context, columnCount)
val bgRes = ThemeUtils.getColorId(context, R.attr.window_background)
val bgRes = ThemeUtils.getColorId(view.context, R.attr.window_background)
adapter.baseItemColor = ContextCompat.getColor(recycler_view.context, bgRes)
adapter.baseItemColor = ContextCompat.getColor(recyclerView.context, bgRes)
recycler_view.layoutManager = layoutManager
recycler_view.adapter = adapter
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
val accountId = arguments?.getString(ACCOUNT_ID_ARG)
swipe_refresh_layout.setOnRefreshListener {
swipeRefreshLayout.setOnRefreshListener {
statusView.hide()
if (fetchingStatus != FetchingStatus.NOT_FETCHING) return@setOnRefreshListener
currentCall = if (statuses.isEmpty()) {
@ -184,12 +186,12 @@ class AccountMediaFragment : BaseFragment(), Injectable {
currentCall?.enqueue(callback)
}
swipe_refresh_layout.setColorSchemeResources(R.color.tusky_blue)
swipe_refresh_layout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(context, android.R.attr.colorBackground))
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(view.context, android.R.attr.colorBackground))
statusView.visibility = View.GONE
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recycler_view: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) {

View file

@ -176,9 +176,9 @@ public class NotificationsFragment extends SFragment implements
@NonNull Context context = inflater.getContext(); // from inflater to silence warning
// Setup the SwipeRefreshLayout.
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
recyclerView = rootView.findViewById(R.id.recycler_view);
progressBar = rootView.findViewById(R.id.progress_bar);
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
recyclerView = rootView.findViewById(R.id.recyclerView);
progressBar = rootView.findViewById(R.id.progressBar);
statusView = rootView.findViewById(R.id.statusView);
swipeRefreshLayout.setOnRefreshListener(this);
@ -417,13 +417,13 @@ public class NotificationsFragment extends SFragment implements
}
@Override
public void onMore(View view, int position) {
public void onMore(@NonNull View view, int position) {
Notification notification = notifications.get(position).asRight();
super.more(notification.getStatus(), view, position);
}
@Override
public void onViewMedia(int position, int attachmentIndex, View view) {
public void onViewMedia(int position, int attachmentIndex, @NonNull View view) {
Notification notification = notifications.get(position).asRightOrNull();
if (notification == null || notification.getStatus() == null) return;
super.viewMedia(attachmentIndex, notification.getStatus(), view);

View file

@ -98,7 +98,7 @@ public abstract class SFragment extends BaseFragment {
}
protected void viewThread(Status status) {
bottomSheetActivity.viewThread(status);
bottomSheetActivity.viewThread(status.getActionableId(), status.getUrl());
}
protected void viewAccount(String accountId) {

View file

@ -182,14 +182,14 @@ class SearchFragment : SFragment(), StatusActionListener, Injectable {
}
}
override fun onMore(view: View?, position: Int) {
override fun onMore(view: View, position: Int) {
val status = searchAdapter.getStatusAtPosition(position)
if (status != null) {
more(status, view, position)
}
}
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View) {
val status = searchAdapter.getStatusAtPosition(position) ?: return
viewMedia(attachmentIndex, status, view)
}

View file

@ -219,9 +219,9 @@ public class TimelineFragment extends SFragment implements
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_timeline, container, false);
recyclerView = rootView.findViewById(R.id.recycler_view);
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
progressBar = rootView.findViewById(R.id.progress_bar);
recyclerView = rootView.findViewById(R.id.recyclerView);
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
progressBar = rootView.findViewById(R.id.progressBar);
statusView = rootView.findViewById(R.id.statusView);
setupSwipeRefreshLayout();
@ -608,7 +608,7 @@ public class TimelineFragment extends SFragment implements
}
@Override
public void onMore(View view, final int position) {
public void onMore(@NonNull View view, final int position) {
super.more(statuses.get(position).asRight(), view, position);
}
@ -689,7 +689,7 @@ public class TimelineFragment extends SFragment implements
}
@Override
public void onViewMedia(int position, int attachmentIndex, View view) {
public void onViewMedia(int position, int attachmentIndex, @NonNull View view) {
Status status = statuses.get(position).asRightOrNull();
if (status == null) return;
super.viewMedia(attachmentIndex, status, view);

View file

@ -137,13 +137,13 @@ public final class ViewThreadFragment extends SFragment implements
View rootView = inflater.inflate(R.layout.fragment_view_thread, container, false);
Context context = getContext();
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(this);
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(
ThemeUtils.getColor(context, android.R.attr.colorBackground));
recyclerView = rootView.findViewById(R.id.recycler_view);
recyclerView = rootView.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
@ -284,12 +284,12 @@ public final class ViewThreadFragment extends SFragment implements
}
@Override
public void onMore(View view, int position) {
public void onMore(@NonNull View view, int position) {
super.more(statuses.get(position), view, position);
}
@Override
public void onViewMedia(int position, int attachmentIndex, View view) {
public void onViewMedia(int position, int attachmentIndex, @NonNull View view) {
Status status = statuses.get(position);
super.viewMedia(attachmentIndex, status, view);
}

View file

@ -26,10 +26,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import android.util.Log
import android.view.View
import com.keylesspalace.tusky.AccountListActivity
import com.keylesspalace.tusky.BuildConfig
import com.keylesspalace.tusky.PreferencesActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.*
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.db.AccountManager
@ -60,6 +57,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(),
lateinit var eventHub: EventHub
private lateinit var notificationPreference: Preference
private lateinit var tabPreference: Preference
private lateinit var mutedUsersPreference: Preference
private lateinit var blockedUsersPreference: Preference
@ -74,6 +72,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(),
addPreferencesFromResource(R.xml.account_preferences)
notificationPreference = findPreference("notificationPreference")
tabPreference = findPreference("tabPreference")
mutedUsersPreference = findPreference("mutedUsersPreference")
blockedUsersPreference = findPreference("blockedUsersPreference")
defaultPostPrivacyPreference = findPreference("defaultPostPrivacy") as ListPreference
@ -81,11 +80,12 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(),
mediaPreviewEnabledPreference = findPreference("mediaPreviewEnabled") as SwitchPreference
alwaysShowSensitiveMediaPreference = findPreference("alwaysShowSensitiveMedia") as SwitchPreference
notificationPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_notifications).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).sizePx(iconSize).color(ThemeUtils.getColor(notificationPreference.context, R.attr.toolbar_icon_tint))
mutedUsersPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp)
blockedUsersPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_block).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
blockedUsersPreference.icon = IconicsDrawable(blockedUsersPreference.context, GoogleMaterial.Icon.gmd_block).sizePx(iconSize).color(ThemeUtils.getColor(blockedUsersPreference.context, R.attr.toolbar_icon_tint))
notificationPreference.onPreferenceClickListener = this
tabPreference.onPreferenceClickListener = this
mutedUsersPreference.onPreferenceClickListener = this
blockedUsersPreference.onPreferenceClickListener = this
@ -161,6 +161,12 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(),
}
return true
}
tabPreference -> {
val intent = Intent(context, TabPreferenceActivity::class.java)
activity?.startActivity(intent)
activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
return true
}
mutedUsersPreference -> {
val intent = Intent(context, AccountListActivity::class.java)
intent.putExtra("type", AccountListActivity.Type.MUTES)

View file

@ -34,13 +34,13 @@ class PreferencesFragment : PreferenceFragmentCompat() {
addPreferencesFromResource(R.xml.preferences)
val themePreference: Preference = findPreference("appTheme")
themePreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_palette).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
themePreference.icon = IconicsDrawable(themePreference.context, GoogleMaterial.Icon.gmd_palette).sizePx(iconSize).color(ThemeUtils.getColor(themePreference.context, R.attr.toolbar_icon_tint))
val emojiPreference: Preference = findPreference("emojiCompat")
emojiPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_sentiment_satisfied).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
emojiPreference.icon = IconicsDrawable(emojiPreference.context, GoogleMaterial.Icon.gmd_sentiment_satisfied).sizePx(iconSize).color(ThemeUtils.getColor(emojiPreference.context, R.attr.toolbar_icon_tint))
val textSizePreference: Preference = findPreference("statusTextSize")
textSizePreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_format_size).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
textSizePreference.icon = IconicsDrawable(textSizePreference.context, GoogleMaterial.Icon.gmd_format_size).sizePx(iconSize).color(ThemeUtils.getColor(textSizePreference.context, R.attr.toolbar_icon_tint))
val timelineFilterPreferences: Preference = findPreference("timelineFilterPreferences")
timelineFilterPreferences.setOnPreferenceClickListener {