rewriting user fragment

This commit is contained in:
Mariotaku Lee 2017-10-23 18:37:24 +08:00
parent 05ffb78bf8
commit 7a5f44a06c
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
22 changed files with 632 additions and 259 deletions

View File

@ -68,7 +68,7 @@ subprojects {
KPreferences : '0.9.7',
Kovenant : '3.3.0',
ParcelablePlease : '1.0.2',
Chameleon : '0.9.22',
Chameleon : '0.9.23',
UniqR : '0.9.4',
SQLiteQB : '0.9.15',
Glide : '3.7.0',

View File

@ -1,91 +0,0 @@
package org.mariotaku.twidere.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import org.mariotaku.twidere.R;
/**
* Created by mariotaku on 14/11/16.
*/
public class BadgeView extends View {
private final TextPaint mTextPaint;
private String mText;
private float mTextX, mTextY;
private Rect mTextBounds;
public BadgeView(Context context) {
this(context, null);
}
public BadgeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BadgeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BadgeView);
setColor(a.getColor(R.styleable.BadgeView_android_textColor, Color.WHITE));
setText(a.getString(R.styleable.BadgeView_android_text));
a.recycle();
mTextPaint.setTextAlign(Align.CENTER);
mTextBounds = new Rect();
}
public void setColor(int color) {
mTextPaint.setColor(color);
invalidate();
}
public void setText(String text) {
mText = text;
updateTextPosition();
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
final int hPadding = (int) (Math.round(w * (Math.pow(2, 0.5f) - 1)) / 2);
final int vPadding = (int) (Math.round(h * (Math.pow(2, 0.5f) - 1)) / 2);
setPadding(hPadding, vPadding, hPadding, vPadding);
updateTextPosition();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mTextBounds.isEmpty()) {
canvas.drawText(mText, mTextX, mTextY, mTextPaint);
}
}
private void updateTextPosition() {
final int width = getWidth(), height = getHeight();
if (width == 0 || height == 0) return;
final float contentWidth = width - getPaddingLeft() - getPaddingRight();
final float contentHeight = height - getPaddingTop() - getPaddingBottom();
if (mText != null) {
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
final float scale = Math.min(contentWidth / mTextBounds.width(), contentHeight / mTextBounds.height());
mTextPaint.setTextSize(Math.min(height / 2, mTextPaint.getTextSize() * scale));
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
mTextX = contentWidth / 2 + getPaddingLeft();
mTextY = contentHeight / 2 + getPaddingTop() + mTextBounds.height() / 2;
} else {
mTextBounds.setEmpty();
}
}
}

View File

@ -47,7 +47,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
private final TabLayoutManager mLayoutManager;
private final ExtendedDividerItemDecoration mItemDecoration;
private ViewPager mViewPager;
private PagerAdapter mPagerProvider;
private TabProvider mTabProvider;
private OnPageChangeListener mPageChangeListener;
private int mOption;
@ -84,6 +84,10 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
mItemDecoration.setDecorationStart(0);
mItemDecoration.setDecorationEndOffset(1);
a.recycle();
if (isInEditMode()) {
mTabProvider = new DemoTabProvider();
}
}
public int getCount() {
@ -168,18 +172,18 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
private void dispatchTabClick(int position) {
final int currentItem = getCurrentItem();
setCurrentItem(position);
if (mPagerProvider instanceof TabListener) {
if (mTabProvider instanceof TabListener) {
if (currentItem != position) {
((TabListener) mPagerProvider).onPageSelected(position);
((TabListener) mTabProvider).onPageSelected(position);
} else {
((TabListener) mPagerProvider).onPageReselected(position);
((TabListener) mTabProvider).onPageReselected(position);
}
}
}
private boolean dispatchTabLongClick(int position) {
if (mPagerProvider instanceof TabListener) {
return ((TabListener) mPagerProvider).onTabLongClick(position);
if (mTabProvider instanceof TabListener) {
return ((TabListener) mTabProvider).onTabLongClick(position);
}
return false;
}
@ -210,7 +214,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
throw new IllegalArgumentException();
}
mViewPager = view;
mPagerProvider = adapter;
mTabProvider = (TabProvider) adapter;
view.addOnPageChangeListener(this);
mIndicatorAdapter.setTabProvider((TabProvider) adapter);
}
@ -580,4 +584,27 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
mTabProvider = tabProvider;
}
}
private static class DemoTabProvider implements TabProvider {
@Override
public int getCount() {
return 4;
}
@Override
public Drawable getPageIcon(int position) {
return null;
}
@Override
public CharSequence getPageTitle(int position) {
return "Title " + position;
}
@Override
public float getPageWidth(int position) {
return 1;
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package android.support.design.widget
import android.support.v4.view.WindowInsetsCompat
val CoordinatorLayout.lastWindowInsetsCompat: WindowInsetsCompat?
get() = lastWindowInsets

View File

@ -0,0 +1,55 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package android.support.design.widget
import android.content.Context
import android.util.AttributeSet
import android.view.View
internal open class PublicHeaderBehavior<V : View>(context: Context, attrs: AttributeSet? = null) : HeaderBehavior<V>(context, attrs) {
internal override fun getScrollRangeForDragFling(view: V): Int {
return super.getScrollRangeForDragFling(view)
}
internal override fun getMaxDragOffset(view: V): Int {
return super.getMaxDragOffset(view)
}
internal override fun setHeaderTopBottomOffset(parent: CoordinatorLayout, header: V, newOffset: Int, minOffset: Int, maxOffset: Int): Int {
return super.setHeaderTopBottomOffset(parent, header, newOffset, minOffset, maxOffset)
}
internal override fun setHeaderTopBottomOffset(parent: CoordinatorLayout, header: V, newOffset: Int): Int {
return super.setHeaderTopBottomOffset(parent, header, newOffset)
}
internal override fun getTopBottomOffsetForScrollingSibling(): Int {
return super.getTopBottomOffsetForScrollingSibling()
}
internal override fun onFlingFinished(parent: CoordinatorLayout?, layout: V) {
super.onFlingFinished(parent, layout)
}
internal override fun canDragView(view: V): Boolean {
return super.canDragView(view)
}
}

View File

@ -26,8 +26,7 @@ import android.support.v4.view.WindowInsetsCompat
import android.view.WindowInsets
inline val WindowInsetsCompat.systemWindowInsets: Rect
get() = Rect(systemWindowInsetLeft, systemWindowInsetTop, systemWindowInsetRight,
systemWindowInsetBottom)
get() = Rect().also { getSystemWindowInsets(it) }
fun WindowInsetsCompat.getSystemWindowInsets(rect: Rect) {
rect.set(systemWindowInsetLeft, systemWindowInsetTop, systemWindowInsetRight,

View File

@ -861,7 +861,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
val displayDoneIcon = isAccountSelectorVisible
if (single != null) {
accountsCount.setText(null)
accountsCount.text = null
if (displayDoneIcon) {
Glide.clear(accountProfileImage)
@ -878,7 +878,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
accountProfileImage.setBorderColor(single.color)
} else {
accountsCount.setText(accounts.size.toString())
accountsCount.text = accounts.size.toString()
Glide.clear(accountProfileImage)
if (displayDoneIcon) {

View File

@ -0,0 +1,41 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension.view
import android.support.design.widget.CoordinatorLayout
import android.support.design.widget.lastWindowInsetsCompat
import android.view.View
import android.view.ViewGroup
fun CoordinatorLayout.measureChildIgnoringInsets(child: View, parentWidthMeasureSpec: Int,
widthUsed: Int, parentHeightMeasureSpec: Int, heightUsed: Int): Boolean {
val lastInsets = lastWindowInsetsCompat ?: return false
val lp = child.layoutParams as ViewGroup.MarginLayoutParams
val childWidthMeasureSpec = ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec
+ lastInsets.systemWindowInsetLeft + lastInsets.systemWindowInsetRight,
paddingLeft + paddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width)
val childHeightMeasureSpec = ViewGroup.getChildMeasureSpec(parentHeightMeasureSpec
+ lastInsets.systemWindowInsetTop + lastInsets.systemWindowInsetBottom,
paddingTop + paddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height)
child.measure(childWidthMeasureSpec, childHeightMeasureSpec)
return true
}

View File

@ -56,7 +56,6 @@ import android.support.v4.content.Loader
import android.support.v4.content.pm.ShortcutManagerCompat
import android.support.v4.content.res.ResourcesCompat
import android.support.v4.graphics.ColorUtils
import android.support.v4.view.OnApplyWindowInsetsListener
import android.support.v4.view.ViewCompat
import android.support.v4.view.ViewPager.OnPageChangeListener
import android.support.v4.view.WindowCompat
@ -74,11 +73,11 @@ import android.view.animation.AnimationUtils
import android.widget.TextView
import android.widget.Toast
import com.squareup.otto.Subscribe
import kotlinx.android.synthetic.main.fragment_user.*
import kotlinx.android.synthetic.main.fragment_user.view.*
import kotlinx.android.synthetic.main.fragment_user2.*
import kotlinx.android.synthetic.main.fragment_user2.view.*
import kotlinx.android.synthetic.main.header_user.*
import kotlinx.android.synthetic.main.header_user.view.*
import kotlinx.android.synthetic.main.layout_content_pages_common.*
import kotlinx.android.synthetic.main.layout_content_fragment_common.*
import nl.komponents.kovenant.task
import nl.komponents.kovenant.then
import nl.komponents.kovenant.ui.alwaysUi
@ -142,22 +141,20 @@ import org.mariotaku.twidere.util.menu.TwidereMenuInfo
import org.mariotaku.twidere.util.shortcut.ShortcutCreator
import org.mariotaku.twidere.util.support.ActivitySupport
import org.mariotaku.twidere.util.support.ActivitySupport.TaskDescriptionCompat
import org.mariotaku.twidere.util.support.ViewSupport
import org.mariotaku.twidere.util.support.WindowSupport
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback
import org.mariotaku.twidere.view.TabPagerIndicator
import org.mariotaku.twidere.view.iface.IExtendedView.OnSizeChangedListener
import java.lang.ref.WeakReference
import java.util.*
class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
OnSizeChangedListener, OnTouchListener, DrawerCallback, SupportFragmentCallback,
OnSizeChangedListener, OnTouchListener, SupportFragmentCallback,
SystemWindowInsetsCallback, RefreshScrollTopInterface, OnPageChangeListener,
KeyboardShortcutCallback, UserColorChangedListener, UserNicknameChangedListener,
IToolBarSupportFragment, AbsContentRecyclerViewFragment.RefreshCompleteListener {
override val toolbar: Toolbar
get() = profileContentContainer.toolbar
get() = coordinatorLayout.toolbar
private lateinit var profileBirthdayBanner: View
private lateinit var actionBarBackground: ActionBarDrawable
@ -221,6 +218,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
val userKey = args.getParcelable<UserKey?>(EXTRA_USER_KEY)
val screenName = args.getString(EXTRA_SCREEN_NAME)
if (user == null && (!omitIntentExtra || !args.containsKey(EXTRA_USER))) {
errorContainer.visibility = View.GONE
progressContainer.visibility = View.VISIBLE
errorText.text = null
errorText.visibility = View.GONE
}
val user = this@UserFragment.user
val loadFromCache = user == null || !user.is_cache && user.key.maybeEquals(userKey)
@ -237,6 +238,8 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
val activity = activity ?: return
if (data.data != null) {
val user = data.data
errorContainer.visibility = View.GONE
progressContainer.visibility = View.GONE
val account: AccountDetails = data.extras.getParcelable(EXTRA_ACCOUNT)
displayUser(user, account)
if (user.is_cache) {
@ -249,11 +252,17 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
}
updateOptionsMenuVisibility()
} else if (user?.is_cache == true) {
errorContainer.visibility = View.GONE
progressContainer.visibility = View.GONE
displayUser(user, account)
updateOptionsMenuVisibility()
} else {
if (data.hasException()) {
errorText.text = data.exception?.getErrorMessage(activity)
errorText.visibility = View.VISIBLE
}
errorContainer.visibility = View.VISIBLE
progressContainer.visibility = View.GONE
displayUser(null, null)
updateOptionsMenuVisibility()
}
@ -288,18 +297,15 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
relationship.blocked_by -> {
pagesErrorContainer.visibility = View.GONE
pagesErrorText.text = null
pagesContent.visibility = View.VISIBLE
}
!relationship.following && user.hide_protected_contents -> {
pagesErrorContainer.visibility = View.VISIBLE
pagesErrorText.setText(R.string.user_protected_summary)
pagesErrorIcon.setImageResource(R.drawable.ic_info_locked)
pagesContent.visibility = View.GONE
}
else -> {
pagesErrorContainer.visibility = View.GONE
pagesErrorText.text = null
pagesContent.visibility = View.VISIBLE
}
}
when {
@ -324,32 +330,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
followContainer.follow.visibility = View.VISIBLE
}
override fun canScroll(dy: Float): Boolean {
val fragment = currentVisibleFragment
return fragment is DrawerCallback && fragment.canScroll(dy)
}
override fun cancelTouch() {
val fragment = currentVisibleFragment
if (fragment is DrawerCallback) {
fragment.cancelTouch()
}
}
override fun fling(velocity: Float) {
val fragment = currentVisibleFragment
if (fragment is DrawerCallback) {
fragment.fling(velocity)
}
}
override fun isScrollContent(x: Float, y: Float): Boolean {
val v = viewPager
val location = IntArray(2)
v.getLocationInWindow(location)
return x >= location[0] && x <= location[0] + v.width
&& y >= location[1] && y <= location[1] + v.height
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
@ -363,29 +343,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
}
override fun scrollBy(dy: Float) {
val fragment = currentVisibleFragment
if (fragment is DrawerCallback) {
fragment.scrollBy(dy)
}
}
override fun shouldLayoutHeaderBottom(): Boolean {
val drawer = userProfileDrawer ?: return false
return drawer.headerTop - drawer.paddingTop <= 0
}
override fun topChanged(top: Int) {
val drawer = userProfileDrawer ?: return
val offset = drawer.paddingTop - top
updateScrollOffset(offset)
val fragment = currentVisibleFragment
if (fragment is DrawerCallback) {
fragment.topChanged(top)
}
}
@UiThread
fun displayUser(user: ParcelableUser?, account: AccountDetails?) {
val activity = activity ?: return
@ -399,15 +356,18 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
return
}
val adapter = pagerAdapter
for (i in 0 until adapter.count) {
val sf = adapter.instantiateItem(viewPager, i) as? AbsTimelineFragment
sf?.reloadAll()
(0 until adapter.count).forEach { i ->
val sf = adapter.instantiateItem(viewPager, i) as? AbsTimelineFragment ?: return@forEach
if (sf.view == null) return@forEach
sf.reloadAll()
}
profileImage.visibility = View.VISIBLE
val resources = resources
val lm = loaderManager
lm.destroyLoader(LOADER_ID_USER)
lm.destroyLoader(LOADER_ID_FRIENDSHIP)
errorContainer.visibility = View.GONE
progressContainer.visibility = View.GONE
this.user = user
profileImage.setBorderColor(if (user.color != 0) user.color else Color.WHITE)
followContainer.drawEnd(user.account_color)
@ -525,7 +485,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
url.movementMethod = null
updateTitleAlpha()
activity.invalidateOptionsMenu()
updateSubtitle()
}
@ -633,11 +592,12 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_user, container, false)
return inflater.inflate(R.layout.fragment_user2, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
linkHandlerTitle = null
val activity = activity
nameFirst = preferences[nameFirstKey]
cardBackgroundColor = ThemeUtils.getCardBackgroundColor(activity,
@ -656,35 +616,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
NdefMessage(arrayOf(NdefRecord.createUri(LinkCreator.getUserWebLink(user))))
})
userFragmentView.windowInsetsListener = OnApplyWindowInsetsListener listener@ { _, insets ->
insets.getSystemWindowInsets(systemWindowsInsets)
val top = insets.systemWindowInsetTop
profileContentContainer.setPadding(0, top, 0, 0)
profileBannerSpace.statusBarHeight = top
if (profileBannerSpace.toolbarHeight == 0) {
var toolbarHeight = toolbar.measuredHeight
if (toolbarHeight == 0) {
toolbarHeight = ThemeUtils.getActionBarHeight(context)
}
profileBannerSpace.toolbarHeight = toolbarHeight
}
updateRefreshProgressOffset()
return@listener insets
}
profileContentContainer.onSizeChangedListener = object : OnSizeChangedListener {
override fun onSizeChanged(view: View, w: Int, h: Int, oldw: Int, oldh: Int) {
val toolbarHeight = toolbar.measuredHeight
userProfileDrawer.setPadding(0, toolbarHeight, 0, 0)
profileBannerSpace.toolbarHeight = toolbarHeight
}
}
userProfileDrawer.setDrawerCallback(this)
pagerAdapter = SupportTabsAdapter(activity, childFragmentManager)
viewPager.offscreenPageLimit = 3
@ -704,12 +635,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
profileBanner.onSizeChangedListener = this
profileBannerSpace.setOnTouchListener(this)
userProfileSwipeLayout.setOnRefreshListener {
if (!triggerRefresh()) {
userProfileSwipeLayout.isRefreshing = false
}
}
profileNameBackground.setBackgroundColor(cardBackgroundColor)
toolbarTabs.setBackgroundColor(cardBackgroundColor)
@ -850,13 +775,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
intent.putExtras(extras)
menu.removeGroup(MENU_GROUP_USER_EXTENSION)
MenuUtils.addIntentToMenu(activity, menu, intent, MENU_GROUP_USER_EXTENSION)
val drawer = userProfileDrawer
if (drawer != null) {
val offset = drawer.paddingTop - drawer.headerTop
previousActionBarItemIsDark = 0
previousTabItemIsDark = 0
updateScrollOffset(offset)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -1132,7 +1050,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
actionBar.subtitle = null
}
}
updateTitleAlpha()
}
private fun handleFragmentKeyboardShortcutRepeat(handler: KeyboardShortcutsHandler,
@ -1173,7 +1090,9 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
activity.supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
activity.supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_MODE_OVERLAY)
}
WindowSupport.setStatusBarColor(activity.window, Color.TRANSPARENT)
val window = activity.window
window.requestFeature(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
WindowSupport.setStatusBarColor(window, Color.TRANSPARENT)
return true
}
@ -1327,19 +1246,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
}
override fun onRefreshComplete(fragment: AbsContentRecyclerViewFragment<*, *>) {
userProfileSwipeLayout.isRefreshing = false
}
private fun updateRefreshProgressOffset() {
val insets = this.systemWindowsInsets
if (insets.top == 0 || userProfileSwipeLayout == null || userProfileSwipeLayout.isRefreshing) {
return
}
val progressCircleDiameter = userProfileSwipeLayout.progressCircleDiameter
if (progressCircleDiameter == 0) return
val progressViewStart = 0 - progressCircleDiameter
val progressViewEnd = profileBannerSpace.toolbarHeight + resources.getDimensionPixelSize(R.dimen.element_spacing_normal)
userProfileSwipeLayout.setProgressViewOffset(false, progressViewStart, progressViewEnd)
}
private fun getFriendship() {
@ -1398,12 +1304,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
profileBanner.setBackgroundColor(color)
toolbarTabs.setBackgroundColor(primaryColor)
val drawer = userProfileDrawer
if (drawer != null) {
val offset = drawer.paddingTop - drawer.headerTop
updateScrollOffset(offset)
}
}
private fun setupBaseActionBar() {
@ -1483,7 +1383,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
val statusBarColor = sArgbEvaluator.evaluate(factor, 0xA0000000.toInt(),
ChameleonUtils.darkenColor(primaryColorDark)) as Int
val window = activity.window
userFragmentView.statusBarColor = statusBarColor
WindowSupport.setLightStatusBar(window, ThemeUtils.isLightColor(statusBarColor))
val stackedTabColor = primaryColor
@ -1501,10 +1400,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
this.outlineAlphaFactor = tabOutlineAlphaFactor
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
windowOverlay.alpha = factor * tabOutlineAlphaFactor
}
val currentTabColor = sArgbEvaluator.evaluate(tabOutlineAlphaFactor,
stackedTabColor, cardBackgroundColor) as Int
@ -1533,8 +1428,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
ThemeUtils.applyToolbarItemColor(activity, toolbar, currentActionBarColor)
}
previousActionBarItemIsDark = if (actionItemIsDark) 1 else -1
updateTitleAlpha()
}
override var controlBarOffset: Float
@ -1545,21 +1438,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
get() = 0
private fun updateTitleAlpha() {
val location = IntArray(2)
nameContainer.name.getLocationInWindow(location)
val nameShowingRatio = (userProfileDrawer.paddingTop - location[1]) / nameContainer.name.height.toFloat()
val textAlpha = nameShowingRatio.coerceIn(0f, 1f)
val titleView = ViewSupport.findViewByText(toolbar, toolbar.title)
if (titleView != null) {
titleView.alpha = textAlpha
}
val subtitleView = ViewSupport.findViewByText(toolbar, toolbar.subtitle)
if (subtitleView != null) {
subtitleView.alpha = textAlpha
}
}
private fun ParcelableRelationship.check(user: ParcelableUser): Boolean {
if (account_key != user.account_key) {
return false

View File

@ -0,0 +1,98 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint.Align
import android.graphics.Rect
import android.support.v4.graphics.ColorUtils
import android.text.TextPaint
import android.util.AttributeSet
import android.view.View
import org.mariotaku.twidere.R
class BadgeView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
var text: String? = null
set(value) {
field = value
updateTextPosition()
invalidate()
}
var color: Int
get() = ColorUtils.setAlphaComponent(textPaint.color, textPaint.alpha)
set(value) {
textPaint.color = ColorUtils.setAlphaComponent(value, 0xFF)
textPaint.alpha = Color.alpha(value)
invalidate()
}
private val textPaint = TextPaint(TextPaint.ANTI_ALIAS_FLAG)
private var textX: Float = 0.toFloat()
private var textY: Float = 0.toFloat()
private val textBounds: Rect
init {
val a = context.obtainStyledAttributes(attrs, R.styleable.BadgeView)
color = a.getColor(R.styleable.BadgeView_android_textColor, Color.WHITE)
text = a.getString(R.styleable.BadgeView_android_text)
a.recycle()
textPaint.textAlign = Align.CENTER
textBounds = Rect()
}
override fun onSizeChanged(w: Int, h: Int, oldW: Int, oldH: Int) {
super.onSizeChanged(w, h, oldW, oldH)
val hPadding = (Math.round(w * (Math.pow(2.0, 0.5) - 1)) / 2).toInt()
val vPadding = (Math.round(h * (Math.pow(2.0, 0.5) - 1)) / 2).toInt()
setPadding(hPadding, vPadding, hPadding, vPadding)
updateTextPosition()
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (!textBounds.isEmpty) {
canvas.drawText(text, textX, textY, textPaint)
}
}
private fun updateTextPosition() {
val width = width
val height = height
if (width == 0 || height == 0) return
val contentWidth = (width - paddingLeft - paddingRight).toFloat()
val contentHeight = (height - paddingTop - paddingBottom).toFloat()
val text = this.text
if (text != null) {
textPaint.getTextBounds(text, 0, text.length, textBounds)
val scale = Math.min(contentWidth / textBounds.width(), contentHeight / textBounds.height())
textPaint.textSize = Math.min((height / 2).toFloat(), textPaint.textSize * scale)
textPaint.getTextBounds(text, 0, text.length, textBounds)
textX = contentWidth / 2 + paddingLeft
textY = contentHeight / 2 + paddingTop.toFloat() + (textBounds.height() / 2).toFloat()
} else {
textBounds.setEmpty()
}
}
}

View File

@ -0,0 +1,32 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.behavior.userprofile
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.util.AttributeSet
import android.view.View
internal class BannerBehavior(context: Context, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<View>(context, attrs) {
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
return true
}
}

View File

@ -0,0 +1,72 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.behavior.userprofile
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.design.widget.PublicHeaderBehavior
import android.support.v4.view.WindowInsetsCompat
import android.util.AttributeSet
import android.view.View
import org.mariotaku.twidere.R
import org.mariotaku.twidere.extension.view.measureChildIgnoringInsets
internal class DetailsBehavior(context: Context, attrs: AttributeSet? = null) : PublicHeaderBehavior<View>(context, attrs) {
override fun onMeasureChild(parent: CoordinatorLayout, child: View,
parentWidthMeasureSpec: Int, widthUsed: Int, parentHeightMeasureSpec: Int,
heightUsed: Int): Boolean {
return parent.measureChildIgnoringInsets(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed)
}
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
return super.onLayoutChild(parent, child, layoutDirection)
}
override fun onApplyWindowInsets(coordinatorLayout: CoordinatorLayout, child: View, insets: WindowInsetsCompat): WindowInsetsCompat {
return super.onApplyWindowInsets(coordinatorLayout, child, insets)
}
override fun layoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int) {
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
override fun getScrollRangeForDragFling(view: View): Int {
val parent = view.parent as CoordinatorLayout
val toolbar = parent.findViewById<View>(R.id.toolbar)
return super.getScrollRangeForDragFling(view) - toolbar.bottom
}
override fun getMaxDragOffset(view: View): Int {
val parent = view.parent as CoordinatorLayout
val toolbar = parent.findViewById<View>(R.id.toolbar)
return super.getMaxDragOffset(view) + toolbar.bottom
}
override fun canDragView(view: View): Boolean {
return true
}
override fun getTopBottomOffsetForScrollingSibling(): Int {
return super.getTopBottomOffsetForScrollingSibling()
}
}

View File

@ -0,0 +1,37 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.behavior.userprofile
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.v4.view.ViewPager
import android.util.AttributeSet
import org.mariotaku.twidere.extension.view.measureChildIgnoringInsets
internal class PagerBehavior(context: Context?, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<ViewPager>(context, attrs) {
override fun onMeasureChild(parent: CoordinatorLayout, child: ViewPager,
parentWidthMeasureSpec: Int, widthUsed: Int, parentHeightMeasureSpec: Int,
heightUsed: Int): Boolean {
return parent.measureChildIgnoringInsets(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed)
}
}

View File

@ -0,0 +1,46 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.behavior.userprofile
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.design.widget.PublicHeaderBehavior
import android.util.AttributeSet
import android.view.View
import org.mariotaku.twidere.R
import org.mariotaku.twidere.view.TabPagerIndicator
internal class TabBehavior(context: Context, attrs: AttributeSet? = null) : PublicHeaderBehavior<TabPagerIndicator>(context, attrs) {
override fun layoutDependsOn(parent: CoordinatorLayout, child: TabPagerIndicator, dependency: View): Boolean {
return dependency.id == R.id.profileHeader
}
override fun onLayoutChild(parent: CoordinatorLayout, child: TabPagerIndicator, layoutDirection: Int): Boolean {
val profileHeader = parent.getDependencies(child).first()
child.layout(0, profileHeader.bottom, child.measuredWidth, profileHeader.bottom + child.measuredHeight)
return true
}
override fun onDependentViewChanged(parent: CoordinatorLayout, child: TabPagerIndicator, dependency: View): Boolean {
child.requestLayout()
return true
}
}

View File

@ -0,0 +1,29 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.behavior.userprofile
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.v7.widget.Toolbar
import android.util.AttributeSet
internal class ToolbarBehavior(context: Context?, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior<Toolbar>(context, attrs) {
}

View File

@ -144,7 +144,7 @@ class ActivityTitleSummaryViewHolder(
if (users.size > profileImageViews.size) {
val moreNumber = users.size - profileImageViews.size
profileImageMoreNumber.visibility = View.VISIBLE
profileImageMoreNumber.setText(moreNumber.toString())
profileImageMoreNumber.text = moreNumber.toString()
} else {
profileImageMoreNumber.visibility = View.GONE
}

View File

@ -42,7 +42,10 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:popupTheme="?actionBarPopupTheme"/>
app:popupTheme="?actionBarPopupTheme"
tools:backgroundTint="@color/branding_color"
tools:navigationIcon="@drawable/ic_action_back"
tools:title="Twidere"/>
<org.mariotaku.twidere.view.TabPagerIndicator
android:id="@+id/toolbarTabs"

View File

@ -68,6 +68,13 @@
app:hdl_headerLayout="@layout/header_user"/>
</org.mariotaku.twidere.view.ExtendedSwipeRefreshLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/layout_content_fragment_common"/>
</FrameLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"

View File

@ -22,14 +22,25 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:statusBarBackground="@android:color/transparent"
tools:theme="@style/Theme.Twidere.NoActionBar">
<org.mariotaku.twidere.view.ExtendedViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.PagerBehavior"/>
<RelativeLayout
android:id="@+id/profileBannerContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top">
android:layout_gravity="top"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.BannerBehavior">
<org.mariotaku.twidere.view.ProfileBannerImageView
android:id="@+id/profileBanner"
@ -48,4 +59,98 @@
android:layout="@layout/layout_user_profile_birthday_stub"/>
</RelativeLayout>
<FrameLayout
android:id="@+id/profileHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.DetailsBehavior">
<include
layout="@layout/header_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/layout_content_fragment_common"/>
</FrameLayout>
<FrameLayout
android:id="@+id/pagesErrorContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_large">
<org.mariotaku.twidere.view.IconActionView
android:id="@+id/pagesErrorIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:color="?android:textColorSecondary"
android:src="@drawable/ic_info_error_generic"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/pagesErrorText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceMedium"/>
</LinearLayout>
<View
android:id="@+id/errorWindowOverlay"
android:layout_width="match_parent"
android:layout_height="@dimen/element_spacing_normal"
android:layout_gravity="top"
android:background="@drawable/shadow_bottom"/>
</FrameLayout>
<org.mariotaku.twidere.view.TabPagerIndicator
android:id="@+id/toolbarTabs"
android:layout_width="match_parent"
android:layout_height="@dimen/element_size_normal"
android:outlineProvider="background"
android:textColor="?android:textColorSecondary"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.TabBehavior"
app:tabExpandEnabled="true"
app:tabHorizontalPadding="@dimen/element_spacing_large"
app:tabIconColor="?android:colorForeground"
app:tabShowDivider="false"
tools:ignore="UnusedAttribute"
tools:listitem="@layout/layout_tab_item"/>
<!-- Don't change view order, since this view consumes window insets-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_alignParentTop="true"
android:elevation="@dimen/toolbar_elevation"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.ToolbarBehavior"
app:popupTheme="?actionBarPopupTheme"
tools:background="@drawable/shadow_bottom"
tools:elevation="0dp"
tools:layout_marginTop="24dp"
tools:navigationIcon="@drawable/ic_action_back"
tools:title="User"
tools:titleTextColor="?android:colorBackground"/>
<View
android:id="@+id/pagerWindowOverlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:windowContentOverlay"/>
</android.support.design.widget.CoordinatorLayout>

View File

@ -43,7 +43,8 @@
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
android:visibility="gone"
tools:visibility="gone">
<org.mariotaku.twidere.view.IconActionView
android:id="@+id/errorIcon"
@ -58,7 +59,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/element_spacing_large"
android:textAppearance="?android:textAppearanceMedium"/>
android:textAppearance="?android:textAppearanceMedium"
tools:text="Error message"/>
</LinearLayout>
</merge>

View File

@ -27,4 +27,9 @@
<item name="darkThemeResource">@style/Theme.Twidere.Dark.Launcher</item>
<item name="lightThemeResource">@style/Theme.Twidere.Light.Launcher</item>
</style>
<style name="Theme.Twidere.NoActionBar.TranslucentDecor" parent="Theme.Twidere.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>

View File

@ -20,6 +20,7 @@
<item name="darkThemeResource">@style/Theme.Twidere.Dark.NoActionBar</item>
<item name="lightThemeResource">@style/Theme.Twidere.Light.NoActionBar</item>
</style>
>
<style name="Theme.Twidere.Compose" parent="Theme.Twidere.Compose.DayNight">
<item name="darkThemeResource">@style/Theme.Twidere.Dark.Compose</item>
@ -62,4 +63,6 @@
<item name="lightThemeResource">@style/Theme.Twidere.Light.Launcher</item>
</style>
<style name="Theme.Twidere.NoActionBar.TranslucentDecor" parent="Theme.Twidere.NoActionBar"/>
</resources>