implementing conversation info ui
This commit is contained in:
parent
2507d91949
commit
a8c5fca745
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ViewExtensionsKtTest {
|
||||
|
||||
}
|
|
@ -81,6 +81,7 @@ public class ShapedImageView extends ImageView {
|
|||
private float mShadowRadius;
|
||||
private int mStyle;
|
||||
private float mCornerRadius, mCornerRadiusRatio;
|
||||
private boolean mDrawShadow;
|
||||
private RectF mTransitionSource, mTransitionDestination;
|
||||
private int mStrokeWidth, mBorderAlpha;
|
||||
private int[] mBorderColors;
|
||||
|
@ -126,6 +127,7 @@ public class ShapedImageView extends ImageView {
|
|||
setStyle(shapeStyle);
|
||||
setCornerRadius(a.getDimension(R.styleable.ShapedImageView_sivCornerRadius, 0));
|
||||
setCornerRadiusRatio(a.getFraction(R.styleable.ShapedImageView_sivCornerRadiusRatio, 1, 1, -1));
|
||||
setDrawShadow(a.getBoolean(R.styleable.ShapedImageView_sivDrawShadow, true));
|
||||
|
||||
if (useOutline()) {
|
||||
if (a.hasValue(R.styleable.ShapedImageView_sivElevation)) {
|
||||
|
@ -151,7 +153,7 @@ public class ShapedImageView extends ImageView {
|
|||
* @param dest The destination bound on the canvas.
|
||||
*/
|
||||
public void drawBitmapWithCircleOnCanvas(Bitmap bitmap, Canvas canvas,
|
||||
RectF source, @NonNull RectF dest) {
|
||||
RectF source, @NonNull RectF dest) {
|
||||
if (bitmap == null) {
|
||||
if (getStyle() == SHAPE_CIRCLE) {
|
||||
canvas.drawCircle(dest.centerX(), dest.centerY(), Math.min(dest.width(), dest.height()) / 2f,
|
||||
|
@ -265,6 +267,10 @@ public class ShapedImageView extends ImageView {
|
|||
mCornerRadiusRatio = ratio;
|
||||
}
|
||||
|
||||
public void setDrawShadow(final boolean drawShadow) {
|
||||
mDrawShadow = drawShadow;
|
||||
}
|
||||
|
||||
public void setTransitionDestination(RectF dstBounds) {
|
||||
mTransitionDestination = dstBounds;
|
||||
}
|
||||
|
@ -423,7 +429,7 @@ public class ShapedImageView extends ImageView {
|
|||
private void initOutlineProvider() {
|
||||
if (!useOutline()) return;
|
||||
ViewSupport.setClipToOutline(this, true);
|
||||
ViewSupport.setOutlineProvider(this, new CircularOutlineProvider());
|
||||
ViewSupport.setOutlineProvider(this, new CircularOutlineProvider(mDrawShadow));
|
||||
}
|
||||
|
||||
private void setBorderColorsInternal(int alpha, int... colors) {
|
||||
|
@ -504,6 +510,12 @@ public class ShapedImageView extends ImageView {
|
|||
}
|
||||
|
||||
private static class CircularOutlineProvider extends ViewOutlineProviderCompat {
|
||||
boolean drawShadow;
|
||||
|
||||
public CircularOutlineProvider(final boolean drawShadow) {
|
||||
this.drawShadow = drawShadow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOutline(View view, OutlineCompat outline) {
|
||||
final int viewWidth = view.getWidth(), viewHeight = view.getHeight();
|
||||
|
@ -523,6 +535,11 @@ public class ShapedImageView extends ImageView {
|
|||
final float radius = imageView.getCalculatedCornerRadius();
|
||||
outline.setRoundRect(contentLeft, contentTop, contentRight, contentBottom, radius);
|
||||
}
|
||||
if (drawShadow) {
|
||||
outline.setAlpha(1);
|
||||
} else {
|
||||
outline.setAlpha(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.mariotaku.twidere.R
|
|||
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
|
||||
import org.mariotaku.twidere.model.ItemCounts
|
||||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.util.view.display
|
||||
import org.mariotaku.twidere.extension.view.holder.display
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserListViewHolder
|
||||
|
||||
class SimpleParcelableUserListsAdapter(
|
||||
|
|
|
@ -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.extension
|
||||
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
|
||||
val Rect.origin: Point get() = Point(left, top)
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.support.annotation.MainThread
|
||||
import android.view.View
|
||||
|
||||
private val tempLocation = IntArray(2)
|
||||
private val tempRect = Rect()
|
||||
|
||||
@MainThread
|
||||
fun View.getBounds(rect: RectF) {
|
||||
rect.set(x, y, x + width, y + height)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun View.getFrame(rect: Rect) {
|
||||
rect.set(left, top, right, bottom)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun View.getFrameRelatedTo(rect: Rect, view: View? = null) {
|
||||
if (view != null) {
|
||||
view.getFrame(tempRect)
|
||||
offsetToRoot(view, tempRect)
|
||||
}
|
||||
this.getFrame(rect)
|
||||
offsetToRoot(this, rect)
|
||||
if (view != null) {
|
||||
rect.offset(-tempRect.left, -tempRect.top)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.getLocationOnScreen(rect: Rect) {
|
||||
getLocationOnScreen(tempLocation)
|
||||
rect.set(tempLocation[0], tempLocation[1], tempLocation[0] + width, tempLocation[1] + height)
|
||||
}
|
||||
|
||||
fun View.getLocationInWindow(rect: Rect) {
|
||||
getLocationInWindow(tempLocation)
|
||||
rect.set(tempLocation[0], tempLocation[1], tempLocation[0] + width, tempLocation[1] + height)
|
||||
}
|
||||
|
||||
private fun offsetToRoot(view: View, rect: Rect) {
|
||||
var parent = view.parent as? View
|
||||
while (parent != null) {
|
||||
rect.offset(parent.left, parent.top)
|
||||
parent = parent.parent as? View
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.holder
|
||||
|
||||
import android.view.View
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper
|
||||
import org.mariotaku.twidere.util.UserColorNameManager
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserListViewHolder
|
||||
|
||||
fun SimpleUserListViewHolder.display(userList: ParcelableUserList, mediaLoader: MediaLoaderWrapper,
|
||||
userColorNameManager: UserColorNameManager, displayProfileImage: Boolean) {
|
||||
nameView.text = userList.name
|
||||
createdByView.text = createdByView.context.getString(R.string.created_by,
|
||||
userColorNameManager.getDisplayName(userList, false))
|
||||
profileImageView.visibility = if (displayProfileImage) View.VISIBLE else View.GONE
|
||||
if (displayProfileImage) {
|
||||
mediaLoader.displayProfileImage(profileImageView, userList)
|
||||
} else {
|
||||
mediaLoader.cancelDisplayTask(profileImageView)
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ import org.mariotaku.ktextension.coerceInOr
|
|||
import org.mariotaku.ktextension.isNullOrEmpty
|
||||
import org.mariotaku.ktextension.rangeOfSize
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_GAP
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter.Companion.ITEM_VIEW_TYPE_STATUS
|
||||
|
@ -193,7 +192,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
}
|
||||
|
||||
override fun handleKeyboardShortcutRepeat(handler: KeyboardShortcutsHandler, keyCode: Int, repeatCount: Int,
|
||||
event: KeyEvent, metaState: Int): Boolean {
|
||||
event: KeyEvent, metaState: Int): Boolean {
|
||||
return navigationHelper.handleKeyboardShortcutRepeat(handler, keyCode, repeatCount, event, metaState)
|
||||
}
|
||||
|
||||
|
@ -305,7 +304,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
|
||||
override fun onGapClick(holder: GapViewHolder, position: Int) {
|
||||
val activity = adapter.getActivity(position) ?: return
|
||||
DebugLog.v(TwidereConstants.LOGTAG, "Load activity gap $activity")
|
||||
DebugLog.v(msg = "Load activity gap $activity")
|
||||
val accountIds = arrayOf(activity.account_key)
|
||||
val maxIds = arrayOf(activity.min_position)
|
||||
val maxSortIds = longArrayOf(activity.min_sort_position)
|
||||
|
@ -451,7 +450,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
protected abstract fun hasMoreData(data: List<ParcelableActivity>?): Boolean
|
||||
|
||||
protected abstract fun onCreateActivitiesLoader(context: Context, args: Bundle,
|
||||
fromUser: Boolean): Loader<List<ParcelableActivity>>
|
||||
fromUser: Boolean): Loader<List<ParcelableActivity>>
|
||||
|
||||
protected abstract fun onContentLoaded(loader: Loader<List<ParcelableActivity>>, data: List<ParcelableActivity>?)
|
||||
|
||||
|
@ -525,7 +524,7 @@ abstract class AbsActivitiesFragment protected constructor() :
|
|||
|
||||
|
||||
override fun createItemDecoration(context: Context, recyclerView: RecyclerView,
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
layoutManager: LinearLayoutManager): RecyclerView.ItemDecoration? {
|
||||
val itemDecoration = object : DividerItemDecoration(context,
|
||||
(recyclerView.layoutManager as LinearLayoutManager).orientation) {
|
||||
override fun isDividerEnabled(childPos: Int): Boolean {
|
||||
|
|
|
@ -39,7 +39,6 @@ import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
|
|||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants
|
||||
import org.mariotaku.twidere.activity.AccountSelectorActivity
|
||||
import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter
|
||||
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration
|
||||
|
@ -353,7 +352,7 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
override fun onGapClick(holder: GapViewHolder, position: Int) {
|
||||
val adapter = this.adapter
|
||||
val status = adapter.getStatus(position) ?: return
|
||||
DebugLog.v(TwidereConstants.LOGTAG, "Load activity gap " + status)
|
||||
DebugLog.v(msg = "Load activity gap $status")
|
||||
adapter.addGapLoadingId(ObjectId(status.account_key, status.id))
|
||||
val accountIds = arrayOf(status.account_key)
|
||||
val maxIds = arrayOf<String?>(status.id)
|
||||
|
|
|
@ -19,29 +19,79 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment.message
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.FragmentActivity
|
||||
import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.AsyncTaskLoader
|
||||
import android.support.v4.content.Loader
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.android.synthetic.main.activity_home_content.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_messages_conversation_info.*
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.*
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
import org.mariotaku.chameleon.ChameleonUtils
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.useCursor
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
|
||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_CONVERSATION_ID
|
||||
import org.mariotaku.twidere.constant.nameFirstKey
|
||||
import org.mariotaku.twidere.constant.profileImageStyleKey
|
||||
import org.mariotaku.twidere.extension.model.displayAvatarTo
|
||||
import org.mariotaku.twidere.extension.model.getConversationName
|
||||
import org.mariotaku.twidere.fragment.BaseFragment
|
||||
import org.mariotaku.twidere.fragment.iface.IToolBarSupportFragment
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversation
|
||||
import org.mariotaku.twidere.model.ParcelableMessageConversationCursorIndices
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/15.
|
||||
*/
|
||||
|
||||
class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment {
|
||||
class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
|
||||
LoaderManager.LoaderCallbacks<ParcelableMessageConversation?> {
|
||||
|
||||
private val accountKey: UserKey get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY)
|
||||
private val conversationId: String get() = arguments.getString(EXTRA_CONVERSATION_ID)
|
||||
|
||||
override val controlBarHeight: Int get() = toolbar.measuredHeight
|
||||
override var controlBarOffset: Float = 0f
|
||||
|
||||
override val toolbar: Toolbar
|
||||
get() = toolbarLayout.toolbar
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
val activity = this.activity
|
||||
|
||||
if (activity is AppCompatActivity) {
|
||||
activity.supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
val theme = Chameleon.getOverrideTheme(context, activity)
|
||||
|
||||
val profileImageStyle = preferences[profileImageStyleKey]
|
||||
appBarConversationAvatar.style = profileImageStyle
|
||||
conversationAvatar.style = profileImageStyle
|
||||
|
||||
val avatarBackground = ChameleonUtils.getColorDependent(theme.colorToolbar)
|
||||
appBarConversationAvatar.setBackgroundColor(avatarBackground)
|
||||
appBarConversationName.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
|
||||
conversationAvatar.setBackgroundColor(avatarBackground)
|
||||
conversationName.setTextColor(ChameleonUtils.getColorDependent(theme.colorToolbar))
|
||||
|
||||
loaderManager.initLoader(0, null, this)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.fragment_messages_conversation_info, container, false)
|
||||
}
|
||||
|
@ -49,4 +99,45 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment
|
|||
override fun setupWindow(activity: FragmentActivity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<ParcelableMessageConversation?> {
|
||||
return ConversationInfoLoader(context, accountKey, conversationId)
|
||||
}
|
||||
|
||||
override fun onLoaderReset(loader: Loader<ParcelableMessageConversation?>?) {
|
||||
}
|
||||
|
||||
override fun onLoadFinished(loader: Loader<ParcelableMessageConversation?>?, data: ParcelableMessageConversation?) {
|
||||
if (data == null) {
|
||||
activity?.finish()
|
||||
return
|
||||
}
|
||||
val name = data.getConversationName(context, userColorNameManager, preferences[nameFirstKey]).first
|
||||
data.displayAvatarTo(mediaLoader, conversationAvatar)
|
||||
data.displayAvatarTo(mediaLoader, appBarConversationAvatar)
|
||||
appBarConversationName.text = name
|
||||
conversationName.text = name
|
||||
}
|
||||
|
||||
class ConversationInfoLoader(
|
||||
context: Context,
|
||||
val accountKey: UserKey,
|
||||
val conversationId: String) : AsyncTaskLoader<ParcelableMessageConversation?>(context) {
|
||||
override fun loadInBackground(): ParcelableMessageConversation? {
|
||||
val where = Expression.and(Expression.equalsArgs(Conversations.ACCOUNT_KEY),
|
||||
Expression.equalsArgs(Conversations.CONVERSATION_ID)).sql
|
||||
val whereArgs = arrayOf(accountKey.toString(), conversationId)
|
||||
context.contentResolver.query(Conversations.CONTENT_URI, Conversations.COLUMNS, where,
|
||||
whereArgs, null).useCursor { cur ->
|
||||
if (cur.moveToFirst()) {
|
||||
return ParcelableMessageConversationCursorIndices.fromCursor(cur)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartLoading() {
|
||||
forceLoad()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,6 +163,10 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
|
|||
addMedia.setOnClickListener {
|
||||
openMediaPicker()
|
||||
}
|
||||
conversationTitleContainer.setOnClickListener {
|
||||
val intent = IntentUtils.messageConversationInfo(accountKey, conversationId)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
val activity = this.activity
|
||||
if (activity is AppCompatActivity) {
|
||||
|
@ -228,11 +232,6 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.info -> {
|
||||
val intent = IntentUtils.messageConversationInfo(accountKey, conversationId)
|
||||
startActivity(intent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.mariotaku.twidere.fragment.CustomTabsFragment.TabEditorDialogFragment
|
|||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.model.tab.TabConfiguration
|
||||
import org.mariotaku.twidere.util.dagger.DependencyHolder
|
||||
import org.mariotaku.twidere.util.view.display
|
||||
import org.mariotaku.twidere.extension.view.holder.display
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserListViewHolder
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.mariotaku.twidere.util
|
|||
|
||||
import android.util.Log
|
||||
import org.mariotaku.twidere.BuildConfig
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/1/24.
|
||||
|
@ -9,7 +10,7 @@ import org.mariotaku.twidere.BuildConfig
|
|||
object DebugLog {
|
||||
|
||||
@JvmStatic
|
||||
fun v(tag: String, msg: String, tr: Throwable? = null): Int {
|
||||
fun v(tag: String = LOGTAG, msg: String, tr: Throwable? = null): Int {
|
||||
if (!BuildConfig.DEBUG) return 0
|
||||
if (tr != null) {
|
||||
return Log.v(tag, msg, tr)
|
||||
|
@ -19,7 +20,7 @@ object DebugLog {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun d(tag: String, msg: String, tr: Throwable? = null): Int {
|
||||
fun d(tag: String = LOGTAG, msg: String, tr: Throwable? = null): Int {
|
||||
if (!BuildConfig.DEBUG) return 0
|
||||
if (tr != null) {
|
||||
return Log.d(tag, msg, tr)
|
||||
|
@ -29,7 +30,7 @@ object DebugLog {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun w(tag: String, msg: String? = null, tr: Throwable? = null): Int {
|
||||
fun w(tag: String = LOGTAG, msg: String? = null, tr: Throwable? = null): Int {
|
||||
if (!BuildConfig.DEBUG) return 0
|
||||
if (msg != null && tr != null) {
|
||||
return Log.w(tag, msg, tr)
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.support.design.widget.AppBarLayout
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.fragment_messages_conversation_info.view.*
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.extension.getFrame
|
||||
import org.mariotaku.twidere.extension.getFrameRelatedTo
|
||||
import org.mariotaku.twidere.extension.getLocationOnScreen
|
||||
import org.mariotaku.twidere.extension.origin
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
|
||||
class ConversationInfoAvatarBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : CoordinatorLayout.Behavior<View>(context, attrs) {
|
||||
private val marginBottom: Float
|
||||
private val marginStart: Float
|
||||
|
||||
private val appBarRect = Rect()
|
||||
|
||||
private val sourceRect = Rect()
|
||||
private val destRect = Rect()
|
||||
private val tempRect = Rect()
|
||||
|
||||
private var toolbarLayoutHeight = 0f
|
||||
|
||||
var dependentPercent: Float = 0f
|
||||
private set
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(attrs, R.styleable.ConversationInfoAvatarBehavior)
|
||||
marginBottom = a.getDimension(R.styleable.ConversationInfoAvatarBehavior_behavior_avatarMarginBottom, 0f)
|
||||
marginStart = a.getDimension(R.styleable.ConversationInfoAvatarBehavior_behavior_avatarMarginStart, 0f)
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
return dependency is AppBarLayout
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean {
|
||||
val appBar = parent.getDependencies(child).first()
|
||||
// Get AppBar frame (and reset origin to 0, 0)
|
||||
appBar.getFrame(appBarRect)
|
||||
val dependencyOrigin = appBarRect.origin
|
||||
appBarRect.offsetTo(0, 0)
|
||||
|
||||
// Layout avatar
|
||||
val left: Int
|
||||
val right: Int
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
right = appBarRect.right - Math.round(marginStart)
|
||||
left = right - child.measuredWidth
|
||||
} else {
|
||||
left = Math.round(marginStart)
|
||||
right = left + child.measuredWidth
|
||||
}
|
||||
val bottom = Math.round(appBarRect.bottom - marginBottom)
|
||||
val top = bottom - child.measuredHeight
|
||||
child.layout(left, top, right, bottom)
|
||||
|
||||
|
||||
val toolbarLayout = appBar.toolbarLayout
|
||||
toolbarLayoutHeight = toolbarLayout.height.toFloat()
|
||||
|
||||
child.getFrameRelatedTo(sourceRect)
|
||||
appBar.conversationAvatar.getFrameRelatedTo(destRect)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||
val behavior = (dependency.layoutParams as CoordinatorLayout.LayoutParams).behavior as AppBarLayout.Behavior
|
||||
val toolbar = dependency.toolbar
|
||||
toolbar.getLocationOnScreen(tempRect)
|
||||
val percent = behavior.topAndBottomOffset / (tempRect.bottom - toolbarLayoutHeight)
|
||||
child.pivotX = child.width.toFloat()
|
||||
child.pivotY = child.height.toFloat()
|
||||
child.scaleX = 1 - (sourceRect.width() - destRect.width()) * percent / sourceRect.width()
|
||||
child.scaleY = 1 - (sourceRect.height() - destRect.height()) * percent / sourceRect.height()
|
||||
child.translationX = (destRect.right - sourceRect.right) * percent
|
||||
child.translationY = (destRect.bottom - sourceRect.bottom) * percent
|
||||
|
||||
if (percent >= 1) {
|
||||
child.visibility = View.INVISIBLE
|
||||
dependency.conversationAvatar.visibility = View.VISIBLE
|
||||
} else {
|
||||
child.visibility = View.VISIBLE
|
||||
dependency.conversationAvatar.visibility = View.INVISIBLE
|
||||
}
|
||||
dependentPercent = percent
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.util.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.extension.getFrame
|
||||
import org.mariotaku.twidere.extension.getFrameRelatedTo
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/20.
|
||||
*/
|
||||
|
||||
class ConversationInfoTitleBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : CoordinatorLayout.Behavior<TextView>(context, attrs) {
|
||||
private val marginBottom: Float
|
||||
private val marginStart: Float
|
||||
|
||||
private val avatarRect = Rect()
|
||||
|
||||
private val sourceRect = Rect()
|
||||
private val destRect = Rect()
|
||||
|
||||
private var sourceSize: Float = Float.NaN
|
||||
private var destSize: Float = Float.NaN
|
||||
private var viewLaidOut: Boolean = false
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(attrs, R.styleable.ConversationInfoTitleBehavior)
|
||||
marginBottom = a.getDimension(R.styleable.ConversationInfoTitleBehavior_behavior_titleMarginBottom, 0f)
|
||||
marginStart = a.getDimension(R.styleable.ConversationInfoTitleBehavior_behavior_titleMarginStart, 0f)
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(parent: CoordinatorLayout, child: TextView, dependency: View): Boolean {
|
||||
return dependency.id == R.id.appBarConversationAvatar
|
||||
}
|
||||
|
||||
override fun onLayoutChild(parent: CoordinatorLayout, child: TextView, layoutDirection: Int): Boolean {
|
||||
val conversationAvatar = parent.getDependencies(child).first()
|
||||
val appBar = conversationAvatar.parent as View
|
||||
conversationAvatar.getFrame(avatarRect)
|
||||
if (!viewLaidOut) {
|
||||
sourceSize = child.textSize
|
||||
destSize = parent.conversationName.textSize
|
||||
viewLaidOut = true
|
||||
}
|
||||
if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
|
||||
child.right = Math.round(avatarRect.left - marginStart)
|
||||
child.left = child.right - child.measuredWidth
|
||||
} else {
|
||||
child.left = Math.round(avatarRect.right + marginStart)
|
||||
child.right = child.left + child.measuredWidth
|
||||
}
|
||||
child.bottom = Math.round(avatarRect.centerY() - marginBottom)
|
||||
child.top = child.bottom - child.measuredHeight
|
||||
|
||||
child.getFrameRelatedTo(sourceRect)
|
||||
appBar.conversationName.getFrameRelatedTo(destRect)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(parent: CoordinatorLayout, child: TextView, dependency: View): Boolean {
|
||||
val behavior = (dependency.layoutParams as CoordinatorLayout.LayoutParams).behavior as ConversationInfoAvatarBehavior
|
||||
val percent = behavior.dependentPercent
|
||||
child.pivotX = child.width.toFloat()
|
||||
child.pivotY = child.height.toFloat()
|
||||
child.translationX = (destRect.left - sourceRect.left) * percent
|
||||
child.translationY = (destRect.top - sourceRect.top) * percent
|
||||
child.setTextSize(TypedValue.COMPLEX_UNIT_PX, sourceSize + (destSize - sourceSize) * percent)
|
||||
if (percent >= 1) {
|
||||
child.visibility = View.INVISIBLE
|
||||
parent.conversationName.visibility = View.VISIBLE
|
||||
} else {
|
||||
child.visibility = View.VISIBLE
|
||||
parent.conversationName.visibility = View.INVISIBLE
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package org.mariotaku.twidere.util.view
|
||||
|
||||
import android.view.View
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.model.ParcelableUserList
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper
|
||||
import org.mariotaku.twidere.util.UserColorNameManager
|
||||
import org.mariotaku.twidere.view.holder.SimpleUserListViewHolder
|
||||
|
||||
fun SimpleUserListViewHolder.display(userList: ParcelableUserList, mediaLoader: MediaLoaderWrapper,
|
||||
userColorNameManager: UserColorNameManager,
|
||||
displayProfileImage: Boolean) {
|
||||
nameView.text = userList.name
|
||||
createdByView.text = createdByView.context.getString(R.string.created_by,
|
||||
userColorNameManager.getDisplayName(userList, false))
|
||||
profileImageView.visibility = if (displayProfileImage) View.VISIBLE else View.GONE
|
||||
if (displayProfileImage) {
|
||||
mediaLoader.displayProfileImage(profileImageView, userList)
|
||||
} else {
|
||||
mediaLoader.cancelDisplayTask(profileImageView)
|
||||
}
|
||||
}
|
|
@ -167,6 +167,7 @@
|
|||
android:background="?colorToolbar"
|
||||
android:elevation="@dimen/toolbar_elevation"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:popupTheme="?actionBarPopupTheme"
|
||||
tools:elevation="0dp">
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
<android.support.design.widget.AppBarLayout
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:layout_height="180dp"
|
||||
android:background="?colorToolbar"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<android.support.design.widget.CollapsingToolbarLayout
|
||||
|
@ -18,14 +19,23 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="?attr/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||
app:titleEnabled="false">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="pin"/>
|
||||
android:background="?colorToolbar"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_collapseMode="pin">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_toolbar_message_conversation_title"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
</android.support.design.widget.CollapsingToolbarLayout>
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
@ -48,8 +58,42 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/element_spacing_large"
|
||||
android:src="@drawable/ic_action_edit"
|
||||
app:backgroundTint="?android:colorBackground"
|
||||
app:layout_anchor="@+id/appBar"
|
||||
app:layout_anchorGravity="bottom|end"
|
||||
tools:backgroundTint="?colorAccent"/>
|
||||
tools:tint="?android:colorForeground"/>
|
||||
|
||||
<org.mariotaku.twidere.view.ProfileImageView
|
||||
android:id="@+id/appBarConversationAvatar"
|
||||
android:layout_width="@dimen/icon_size_conversation_info"
|
||||
android:layout_height="@dimen/icon_size_conversation_info"
|
||||
app:behavior_avatarMarginBottom="@dimen/element_spacing_large"
|
||||
app:behavior_avatarMarginStart="@dimen/element_spacing_large"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.ConversationInfoAvatarBehavior"
|
||||
app:sivDrawShadow="false"
|
||||
app:sivElevation="8dp"
|
||||
app:sivShape="circle"
|
||||
tools:src="@drawable/ic_profile_image_default_group"/>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/appBarConversationName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="8dp"
|
||||
android:ellipsize="end"
|
||||
android:minLines="1"
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="normal"
|
||||
app:behavior_titleMarginStart="@dimen/element_spacing_normal"
|
||||
app:layout_behavior="org.mariotaku.twidere.util.view.ConversationInfoTitleBehavior"
|
||||
tools:text="Title"/>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/appBarConversationSummary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Subtitle"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
|
@ -21,6 +21,7 @@
|
|||
<LinearLayout
|
||||
android:id="@+id/conversationTitleContainer"
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -35,6 +36,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/element_spacing_normal"
|
||||
android:layout_weight="0"
|
||||
app:sivShape="circle"
|
||||
tools:src="@drawable/ic_profile_image_default_group"/>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -43,7 +45,8 @@
|
|||
android:layout_marginLeft="@dimen/element_spacing_normal"
|
||||
android:layout_marginStart="@dimen/element_spacing_normal"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical">
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/conversationName"
|
||||
|
@ -52,5 +55,13 @@
|
|||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
tools:text="Conversation name"/>
|
||||
|
||||
<org.mariotaku.twidere.view.FixedTextView
|
||||
android:id="@+id/conversationSummary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Conversation summary"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -20,10 +20,6 @@
|
|||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/info"
|
||||
android:icon="@drawable/ic_action_info"
|
||||
android:title="@string/action_conversation_info"/>
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:enabled="@bool/debug"
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
<attr name="sivElevation" format="dimension"/>
|
||||
<attr name="sivCornerRadius" format="dimension"/>
|
||||
<attr name="sivCornerRadiusRatio" format="fraction"/>
|
||||
<attr name="sivDrawShadow" format="boolean"/>
|
||||
<attr name="sivShape"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="HeaderDrawerLayout">
|
||||
|
@ -144,6 +145,14 @@
|
|||
<declare-styleable name="ProfileBannerImageView">
|
||||
<attr name="bannerAspectRatio" format="fraction"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ConversationInfoAvatarBehavior">
|
||||
<attr name="behavior_avatarMarginBottom" format="dimension"/>
|
||||
<attr name="behavior_avatarMarginStart" format="dimension"/>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ConversationInfoTitleBehavior">
|
||||
<attr name="behavior_titleMarginBottom" format="dimension"/>
|
||||
<attr name="behavior_titleMarginStart" format="dimension"/>
|
||||
</declare-styleable>
|
||||
<attr name="notificationType">
|
||||
<flag name="none" value="0"/>
|
||||
<flag name="ringtone" value="1"/>
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
<dimen name="icon_size_profile_type_user_profile">26dp</dimen>
|
||||
<dimen name="icon_size_status_profile_image">48dp</dimen>
|
||||
<dimen name="icon_size_user_profile">84dp</dimen>
|
||||
<dimen name="icon_size_conversation_info">64dp</dimen>
|
||||
|
||||
<dimen name="line_width_compose_account_profile_image">2dp</dimen>
|
||||
|
||||
|
|
Loading…
Reference in New Issue