redesigned account select button

improved actionbar shadow appearance on android L
This commit is contained in:
Mariotaku Lee 2014-12-08 23:16:33 +08:00
parent cc2f794025
commit 10ac7ccf52
26 changed files with 821 additions and 2851 deletions

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mariotaku.twidere.test"
android:versionCode="1"
android:versionName="1.0">
<!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
<!-- We add an application tag here just so that we can indicate that
this package needs to link against the android.test library,
which is needed when building test cases. -->
<application>
<uses-library android:name="android.test.runner"/>
</application>
<!--
Specifies the instrumentation test runner used to run the tests.
-->
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="org.mariotaku.twidere"
android:label="Tests for org.mariotaku.twidere"/>
</manifest>

View File

@ -1,13 +0,0 @@
package org.mariotaku.myapplication;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,20 @@
package org.mariotaku.twidere.test;
import android.test.ApplicationTestCase;
import org.mariotaku.twidere.app.TwidereApplication;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class TwidereApplicationTest extends ApplicationTestCase<TwidereApplication> {
@Override
protected void setUp() throws Exception {
super.setUp();
}
public TwidereApplicationTest() {
super(TwidereApplication.class);
}
}

View File

@ -97,12 +97,12 @@ public abstract class BaseSupportThemedActivity extends FragmentActivity impleme
@Override
protected void onTitleChanged(CharSequence title, int color) {
final SpannableStringBuilder builder = new SpannableStringBuilder(title);
super.onTitleChanged(title, color);
final int themeResId = getCurrentThemeResourceId();
final int themeColor = getThemeColor(), contrastColor = Utils.getContrastYIQ(themeColor, 192);
if (!ThemeUtils.isDarkTheme(themeResId)) {
builder.setSpan(new ForegroundColorSpan(contrastColor), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
super.onTitleChanged(title, color);
}
@Override

View File

@ -105,7 +105,7 @@ import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.accessor.ViewAccessor;
import org.mariotaku.twidere.view.ColorLabelFrameLayout;
import org.mariotaku.twidere.view.ComposeSelectAccountButton;
import org.mariotaku.twidere.view.StatusTextCountView;
import org.mariotaku.twidere.view.TwidereMenuBar;
import org.mariotaku.twidere.view.holder.StatusListViewHolder;
@ -132,7 +132,6 @@ import static org.mariotaku.twidere.util.ThemeUtils.getWindowContentOverlayForCo
import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserColor;
import static org.mariotaku.twidere.util.Utils.addIntentToMenu;
import static org.mariotaku.twidere.util.Utils.copyStream;
import static org.mariotaku.twidere.util.Utils.getAccountColors;
import static org.mariotaku.twidere.util.Utils.getAccountIds;
import static org.mariotaku.twidere.util.Utils.getAccountName;
import static org.mariotaku.twidere.util.Utils.getAccountScreenName;
@ -180,7 +179,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
private ProgressBar mProgress;
private View mSendView;
private StatusTextCountView mSendTextCountView;
private ColorLabelFrameLayout mSelectAccountButton;
private ComposeSelectAccountButton mSelectAccountAccounts;
private MediaPreviewAdapter mMediaPreviewAdapter;
@ -438,7 +437,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
final View composeBottomBar = findViewById(R.id.compose_bottombar);
mSendView = composeBottomBar.findViewById(R.id.send);
mSendTextCountView = (StatusTextCountView) mSendView.findViewById(R.id.status_text_count);
mSelectAccountButton = (ColorLabelFrameLayout) composeActionBar.findViewById(R.id.select_account);
mSelectAccountAccounts = (ComposeSelectAccountButton) composeActionBar.findViewById(R.id.select_account);
ViewAccessor.setBackground(findViewById(R.id.compose_content), getWindowContentOverlayForCompose(this));
ViewAccessor.setBackground(composeActionBar, getActionBarBackground(this, getCurrentThemeResourceId()));
}
@ -581,12 +580,12 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
mAccountSelectorPopup.setModal(true);
mAccountSelectorPopup.setContentWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width));
mAccountSelectorPopup.setAdapter(accountAdapter);
mAccountSelectorPopup.setAnchorView(mSelectAccountButton);
mAccountSelectorPopup.setAnchorView(mSelectAccountAccounts);
// mSelectAccountButton.setOnTouchListener(ListPopupWindowCompat.createDragToOpenListener(
// mAccountSelectorPopup, mSelectAccountButton));
mSelectAccountButton.setOnClickListener(this);
mSelectAccountButton.setOnLongClickListener(this);
mSelectAccountAccounts.setOnClickListener(this);
mSelectAccountAccounts.setOnLongClickListener(this);
mMediaPreviewAdapter = new MediaPreviewAdapter(this);
mMediaPreviewGrid.setAdapter(mMediaPreviewAdapter);
@ -1032,7 +1031,7 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
editor.putString(KEY_COMPOSE_ACCOUNTS, ArrayUtils.toString(mSendAccountIds, ',', false));
editor.apply();
}
mSelectAccountButton.drawEnd(getAccountColors(this, mSendAccountIds));
mSelectAccountAccounts.setSelectedAccounts(mSendAccountIds);
}
private void updateMediaPreview() {
@ -1492,4 +1491,6 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
return Collections.unmodifiableList(getObjects());
}
}
}

View File

@ -105,9 +105,6 @@ import org.mariotaku.twidere.view.TabPagerIndicator;
import org.mariotaku.twidere.view.TintedStatusFrameLayout;
import org.mariotaku.twidere.view.iface.IHomeActionButton;
import java.util.ArrayList;
import java.util.List;
import edu.ucdavis.earlybird.ProfilingUtil;
import static org.mariotaku.twidere.util.CompareUtils.classEquals;
@ -132,8 +129,6 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
private final ContentObserver mAccountChangeObserver = new AccountChangeObserver(this, mHandler);
private final ArrayList<SupportTabSpec> mCustomTabs = new ArrayList<>();
private final SparseArray<Fragment> mAttachedFragments = new SparseArray<>();
private ParcelableAccount mSelectedAccountToSearch;
@ -645,7 +640,7 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
resolver.registerContentObserver(Accounts.CONTENT_URI, true, mAccountChangeObserver);
final Bus bus = TwidereApplication.getInstance(this).getMessageBus();
bus.register(this);
if (isTabsChanged(getHomeTabs(this)) || getTabDisplayOptionInt(this) != mTabDisplayOption) {
if (getTabDisplayOptionInt(this) != mTabDisplayOption) {
restart();
}
// UCD
@ -725,13 +720,11 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
}
private void setupHomeTabs() {
final List<SupportTabSpec> tabs = getHomeTabs(this);
mCustomTabs.clear();
mCustomTabs.addAll(tabs);
mPagerAdapter.clear();
mPagerAdapter.addTabs(tabs);
mEmptyTabHint.setVisibility(tabs.isEmpty() ? View.VISIBLE : View.GONE);
mViewPager.setVisibility(tabs.isEmpty() ? View.GONE : View.VISIBLE);
mPagerAdapter.addTabs(getHomeTabs(this));
final boolean hasNoTab = mPagerAdapter.getCount() == 0;
mEmptyTabHint.setVisibility(hasNoTab ? View.VISIBLE : View.GONE);
mViewPager.setVisibility(hasNoTab ? View.GONE : View.VISIBLE);
}
private void initUnreadCount() {
@ -740,15 +733,6 @@ public class HomeActivity extends BaseSupportActivity implements OnClickListener
}
}
private boolean isTabsChanged(final List<SupportTabSpec> tabs) {
if (mCustomTabs.size() == 0 && tabs == null) return false;
if (mCustomTabs.size() != tabs.size()) return true;
for (int i = 0, size = mCustomTabs.size(); i < size; i++) {
if (!mCustomTabs.get(i).equals(tabs.get(i))) return true;
}
return false;
}
private void openAccountsDrawer() {
if (mSlidingMenu == null) return;
mSlidingMenu.showMenu();

View File

@ -211,10 +211,10 @@ public class LinkHandlerActivity extends BaseSupportActivity implements OnClickL
transitionRes = R.transition.transition_user;
break;
}
case LINK_ID_STATUS: {
transitionRes = R.transition.transition_status;
break;
}
// case LINK_ID_STATUS: {
// transitionRes = R.transition.transition_status;
// break;
// }
default: {
transitionRes = 0;
break;

View File

@ -40,7 +40,7 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
private final int mCardLayoutResource;
private boolean mLoadMoreIndicatorEnabled;
private EventListener mEventListener;
private StatusAdapterListener mStatusAdapterListener;
public AbsStatusesAdapter(Context context, boolean compact) {
mContext = context;
@ -127,8 +127,8 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
@Override
public final void onStatusClick(StatusViewHolder holder, int position) {
if (mEventListener != null) {
mEventListener.onStatusClick(holder, position);
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusClick(holder, position);
}
}
@ -150,15 +150,15 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
@Override
public void onItemActionClick(ViewHolder holder, int id, int position) {
if (mEventListener != null) {
mEventListener.onStatusActionClick((StatusViewHolder) holder, id, position);
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusActionClick((StatusViewHolder) holder, id, position);
}
}
@Override
public void onItemMenuClick(ViewHolder holder, int position) {
if (mEventListener != null) {
mEventListener.onStatusMenuClick((StatusViewHolder) holder, position);
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusMenuClick((StatusViewHolder) holder, position);
}
}
@ -166,18 +166,18 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
public abstract D getData();
public void setEventListener(EventListener listener) {
mEventListener = listener;
public void setEventListener(StatusAdapterListener listener) {
mStatusAdapterListener = listener;
}
@Override
public final void onGapClick(ViewHolder holder, int position) {
if (mEventListener != null) {
mEventListener.onGapClick((GapViewHolder) holder, position);
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onGapClick((GapViewHolder) holder, position);
}
}
public static interface EventListener {
public static interface StatusAdapterListener {
void onStatusActionClick(StatusViewHolder holder, int id, int position);
void onStatusClick(StatusViewHolder holder, int position);

View File

@ -8,10 +8,8 @@ import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.LinearLayoutManager;
@ -23,7 +21,7 @@ import android.view.ViewGroup;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter.EventListener;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter.StatusAdapterListener;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.constant.IntentConstants;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
@ -40,7 +38,7 @@ import org.mariotaku.twidere.view.holder.StatusViewHolder;
* Created by mariotaku on 14/11/5.
*/
public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment implements LoaderCallbacks<Data>,
OnRefreshListener, DrawerCallback, RefreshScrollTopInterface, EventListener {
OnRefreshListener, DrawerCallback, RefreshScrollTopInterface, StatusAdapterListener {
private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() {
@ -204,19 +202,7 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
final FragmentActivity activity = getActivity();
final Bundle activityOption = Utils.makeSceneTransitionOption(activity,
new Pair<View, String>(holder.getProfileImageView(), StatusFragment.TRANSITION_NAME_PROFILE_IMAGE),
new Pair<View, String>(holder.getProfileTypeView(), StatusFragment.TRANSITION_NAME_PROFILE_TYPE));
Utils.openStatus(getActivity(), mAdapter.getStatus(position), activityOption);
// final View cardView = holder.getCardView();
// if (cardView != null && context instanceof FragmentActivity) {
// final Bundle options = Utils.makeSceneTransitionOption((FragmentActivity) context,
// new Pair<>(cardView, StatusFragment.TRANSITION_NAME_CARD));
// Utils.openStatus(context, getStatus(position), options);
// } else {
// Utils.openStatus(context, getStatus(position), null);
// }
Utils.openStatus(getActivity(), mAdapter.getStatus(position), null);
}
@Override

View File

@ -494,7 +494,7 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
@Override
public AccountProfileImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = mInflater.inflate(R.layout.adapter_item_compose_account, parent, false);
final View view = mInflater.inflate(R.layout.adapter_item_dashboard_account, parent, false);
return new AccountProfileImageViewHolder(this, view);
}

View File

@ -19,11 +19,13 @@
package org.mariotaku.twidere.fragment.support;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
@ -32,7 +34,6 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@ -73,6 +74,7 @@ import org.mariotaku.twidere.task.TwidereAsyncTask.Status;
import org.mariotaku.twidere.text.method.StatusContentMovementMethod;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ClipboardUtils;
import org.mariotaku.twidere.util.CompareUtils;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.MediaPreviewUtils;
@ -94,15 +96,18 @@ import java.util.Locale;
import twitter4j.TwitterException;
import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.util.UserColorNicknameUtils.clearUserColor;
import static org.mariotaku.twidere.util.UserColorNicknameUtils.clearUserNickname;
import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserColor;
import static org.mariotaku.twidere.util.UserColorNicknameUtils.getUserNickname;
import static org.mariotaku.twidere.util.UserColorNicknameUtils.setUserColor;
import static org.mariotaku.twidere.util.Utils.cancelRetweet;
import static org.mariotaku.twidere.util.Utils.findStatus;
import static org.mariotaku.twidere.util.Utils.formatToLongTimeString;
import static org.mariotaku.twidere.util.Utils.getLocalizedNumber;
import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes;
import static org.mariotaku.twidere.util.Utils.isMyRetweet;
import static org.mariotaku.twidere.util.Utils.openStatus;
import static org.mariotaku.twidere.util.Utils.openUserProfile;
import static org.mariotaku.twidere.util.Utils.setMenuForStatus;
import static org.mariotaku.twidere.util.Utils.showErrorMessage;
@ -115,9 +120,6 @@ import static org.mariotaku.twidere.util.Utils.startStatusShareChooser;
public class StatusFragment extends BaseSupportFragment
implements LoaderCallbacks<SingleResponse<ParcelableStatus>>, OnMediaClickListener {
public static final String TRANSITION_NAME_PROFILE_IMAGE = "profile_image";
public static final String TRANSITION_NAME_PROFILE_TYPE = "profile_type";
private static final int LOADER_ID_DETAIL_STATUS = 1;
private static final int LOADER_ID_STATUS_REPLIES = 2;
@ -153,6 +155,34 @@ public class StatusFragment extends BaseSupportFragment
};
private LinearLayoutManager mLayoutManager;
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_SET_COLOR: {
final ParcelableStatus status = mStatusAdapter.getStatus();
if (status == null) return;
if (resultCode == Activity.RESULT_OK) {
if (data == null) return;
final int color = data.getIntExtra(EXTRA_COLOR, Color.TRANSPARENT);
setUserColor(getActivity(), status.user_id, color);
} else if (resultCode == ColorPickerDialogActivity.RESULT_CLEARED) {
clearUserColor(getActivity(), status.user_id);
}
break;
}
case REQUEST_SELECT_ACCOUNT: {
final ParcelableStatus status = mStatusAdapter.getStatus();
if (status == null) return;
if (resultCode == Activity.RESULT_OK) {
if (data == null || !data.hasExtra(EXTRA_ID)) return;
final long accountId = data.getLongExtra(EXTRA_ID, -1);
openStatus(getActivity(), accountId, status.id);
}
break;
}
}
}
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
@ -174,8 +204,7 @@ public class StatusFragment extends BaseSupportFragment
if (view == null) throw new AssertionError();
final Context context = view.getContext();
final boolean compact = Utils.isCompactCards(context);
mLayoutManager = new MyLinearLayoutManager(context, mRecyclerView);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mLayoutManager = new StatusListLinearLayoutManager(context, mRecyclerView);
if (compact) {
mRecyclerView.addItemDecoration(new DividerItemDecoration(context, mLayoutManager.getOrientation()));
}
@ -189,24 +218,31 @@ public class StatusFragment extends BaseSupportFragment
}
@Override
public void onLoadFinished(final Loader<SingleResponse<ParcelableStatus>> loader,
final SingleResponse<ParcelableStatus> data) {
if (data.hasData()) {
final ParcelableStatus status = data.getData();
mStatusAdapter.setConversation(null);
mStatusAdapter.setReplies(null);
mStatusAdapter.setStatus(status);
mLayoutManager.scrollToPositionWithOffset(1, 0);
loadReplies(status);
loadConversation(status);
} else {
//TODO show errors
}
public void onMediaClick(View view, ParcelableMedia media) {
}
@Override
public void onMediaClick(View view, ParcelableMedia media) {
public void onLoadFinished(final Loader<SingleResponse<ParcelableStatus>> loader,
final SingleResponse<ParcelableStatus> data) {
if (data.hasData()) {
final long itemId = mStatusAdapter.getItemId(mLayoutManager.findFirstVisibleItemPosition());
final View firstChild = mLayoutManager.getChildAt(0);
final int top = firstChild != null ? firstChild.getTop() : 0;
final ParcelableStatus status = data.getData();
if (mStatusAdapter.setStatus(status)) {
mLayoutManager.scrollToPositionWithOffset(1, 0);
mStatusAdapter.setConversation(null);
mStatusAdapter.setReplies(null);
loadReplies(status);
loadConversation(status);
} else {
final int position = mStatusAdapter.findPositionById(itemId);
mLayoutManager.scrollToPositionWithOffset(position, top);
}
} else {
//TODO show errors
}
}
@Override
@ -222,11 +258,6 @@ public class StatusFragment extends BaseSupportFragment
getView().setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
@Override
public void onLoaderReset(final Loader<SingleResponse<ParcelableStatus>> loader) {
}
private void loadConversation(ParcelableStatus status) {
if (mLoadConversationTask != null && mLoadConversationTask.getStatus() == Status.RUNNING) {
mLoadConversationTask.cancel(true);
@ -249,6 +280,11 @@ public class StatusFragment extends BaseSupportFragment
mRepliesLoaderInitialized = true;
}
@Override
public void onLoaderReset(final Loader<SingleResponse<ParcelableStatus>> loader) {
}
private void setConversation(List<ParcelableStatus> data) {
if (mLayoutManager.getChildCount() != 0) {
final long itemId = mStatusAdapter.getItemId(mLayoutManager.findFirstVisibleItemPosition());
@ -356,7 +392,7 @@ public class StatusFragment extends BaseSupportFragment
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
openStatus(mFragment.getActivity(), getStatus(position), null);
}
@Override
@ -369,6 +405,17 @@ public class StatusFragment extends BaseSupportFragment
}
public ParcelableStatus getStatus() {
return mStatus;
}
public boolean setStatus(ParcelableStatus status) {
final ParcelableStatus old = mStatus;
mStatus = status;
notifyDataSetChanged();
return !CompareUtils.objectEquals(old, status);
}
public boolean isDetailMediaExpanded() {
return mDetailMediaExpanded;
}
@ -491,11 +538,6 @@ public class StatusFragment extends BaseSupportFragment
notifyDataSetChanged();
}
public void setStatus(ParcelableStatus status) {
mStatus = status;
notifyDataSetChanged();
}
private int getConversationCount() {
return mConversation != null ? mConversation.size() : 1;
}
@ -505,6 +547,7 @@ public class StatusFragment extends BaseSupportFragment
}
}
static class LoadConversationTask extends TwidereAsyncTask<ParcelableStatus, ParcelableStatus,
ListResponse<ParcelableStatus>> {
@ -666,13 +709,13 @@ public class StatusFragment extends BaseSupportFragment
case MENU_QUOTE: {
final Intent intent = new Intent(INTENT_ACTION_QUOTE);
intent.putExtra(EXTRA_STATUS, status);
activity.startActivity(intent);
fragment.startActivity(intent);
break;
}
case MENU_REPLY: {
final Intent intent = new Intent(INTENT_ACTION_REPLY);
intent.putExtra(EXTRA_STATUS, status);
activity.startActivity(intent);
fragment.startActivity(intent);
break;
}
case MENU_FAVORITE: {
@ -726,13 +769,13 @@ public class StatusFragment extends BaseSupportFragment
final Intent intent = new Intent(INTENT_ACTION_SELECT_ACCOUNT);
intent.setClass(activity, AccountSelectorActivity.class);
intent.putExtra(EXTRA_SINGLE_SELECTION, true);
activity.startActivityForResult(intent, REQUEST_SELECT_ACCOUNT);
fragment.startActivityForResult(intent, REQUEST_SELECT_ACCOUNT);
break;
}
default: {
if (item.getIntent() != null) {
try {
activity.startActivity(item.getIntent());
fragment.startActivity(item.getIntent());
} catch (final ActivityNotFoundException e) {
Log.w(LOGTAG, e);
return false;
@ -744,11 +787,6 @@ public class StatusFragment extends BaseSupportFragment
return true;
}
@Override
public void onMediaClick(View view, ParcelableMedia media) {
adapter.mFragment.onMediaClick(view, media);
}
public void showStatus(ParcelableStatus status) {
if (status == null) return;
progressContainer.setVisibility(View.GONE);
@ -830,28 +868,39 @@ public class StatusFragment extends BaseSupportFragment
menuBar.show();
}
@Override
public void onMediaClick(View view, ParcelableMedia media) {
adapter.mFragment.onMediaClick(view, media);
}
private void initViews() {
menuBar.setOnMenuItemClickListener(this);
menuBar.inflate(R.menu.menu_status);
mediaPreviewLoad.setOnClickListener(this);
profileContainer.setOnClickListener(this);
ViewCompat.setTransitionName(profileImageView, TRANSITION_NAME_PROFILE_IMAGE);
ViewCompat.setTransitionName(profileTypeView, TRANSITION_NAME_PROFILE_TYPE);
}
}
private static class MyLinearLayoutManager extends LinearLayoutManager {
private static class StatusListLinearLayoutManager extends LinearLayoutManager {
private final RecyclerView recyclerView;
public MyLinearLayoutManager(Context context, RecyclerView recyclerView) {
public StatusListLinearLayoutManager(Context context, RecyclerView recyclerView) {
super(context);
setOrientation(LinearLayoutManager.VERTICAL);
this.recyclerView = recyclerView;
}
@Override
public void setOrientation(int orientation) {
if (orientation != VERTICAL)
throw new IllegalArgumentException("Only VERTICAL orientation supported");
super.setOrientation(orientation);
}
@Override
public int getDecoratedMeasuredHeight(View child) {
final int height = super.getDecoratedMeasuredHeight(child);

View File

@ -78,6 +78,7 @@ import com.squareup.otto.Subscribe;
import org.mariotaku.menucomponent.internal.menu.MenuUtils;
import org.mariotaku.querybuilder.Expression;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IThemedActivity;
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
import org.mariotaku.twidere.activity.support.LinkHandlerActivity;
@ -87,6 +88,7 @@ import org.mariotaku.twidere.adapter.support.SupportTabsAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.graphic.ActionBarColorDrawable;
import org.mariotaku.twidere.loader.support.ParcelableUserLoader;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList;
@ -204,9 +206,8 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
@Subscribe
public void notifyFriendshipUpdated(FriendshipUpdatedEvent event) {
if (event.user != null && event.user.equals(mUser)) {
getFriendship();
}
if (!event.user.equals(mUser)) return;
getFriendship();
}
private void updateRefreshState() {
@ -344,7 +345,6 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
};
public void displayUser(final ParcelableUser user) {
mRelationship = null;
mUser = null;
if (user == null || user.id <= 0 || getActivity() == null) return;
final Resources res = getResources();
@ -853,7 +853,15 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
protected void fitSystemWindows(Rect insets) {
super.fitSystemWindows(insets);
mHeaderDrawerLayout.setPadding(insets.left, insets.top, insets.right, insets.bottom);
mHeaderDrawerLayout.setClipToPadding(ThemeUtils.isTransparentBackground(getActivity()));
final FragmentActivity activity = getActivity();
final boolean isTransparentBackground;
if (activity instanceof IThemedActivity) {
final int themeRes = ((IThemedActivity) activity).getCurrentThemeResourceId();
isTransparentBackground = ThemeUtils.isTransparentBackground(themeRes);
} else {
isTransparentBackground = ThemeUtils.isTransparentBackground(getActivity());
}
mHeaderDrawerLayout.setClipToPadding(isTransparentBackground);
}
public void setListShown(boolean shown) {
@ -867,6 +875,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
}
private void getFriendship() {
mRelationship = null;
final ParcelableUser user = mUser;
final LoaderManager lm = getLoaderManager();
lm.destroyLoader(LOADER_ID_FRIENDSHIP);
@ -1225,7 +1234,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
public ActionBarDrawable(Resources resources, Drawable shadow, Drawable background,
boolean colorLineOnly) {
super(new Drawable[]{shadow, background, new LineBackgroundDrawable(resources, 2.0f),
new ColorDrawable()});
new ActionBarColorDrawable()});
mShadowDrawable = shadow;
mBackgroundDrawable = getDrawable(1);
mLineDrawable = (LineBackgroundDrawable) getDrawable(2);
@ -1243,11 +1252,11 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
} else {
mBackgroundDrawable.getOutline(outline);
}
outline.setAlpha(mFactor * 0.99f);
}
@Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
mAlpha = alpha;
setFactor(mFactor);
}

View File

@ -0,0 +1,50 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.graphic;
import android.annotation.TargetApi;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
/**
* Created by mariotaku on 14/12/8.
*/
public class ActionBarColorDrawable extends ColorDrawable {
public ActionBarColorDrawable() {
super();
}
public ActionBarColorDrawable(int color) {
super(color);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void getOutline(Outline outline) {
final Rect bounds = getBounds();
// Very very dirty hack to make outline shadow in action bar not visible beneath status bar
outline.setRect(bounds.left - bounds.width() / 2, -bounds.height(),
bounds.right + bounds.width() / 2, bounds.bottom);
outline.setAlpha(getAlpha() / 255f);
}
}

View File

@ -19,247 +19,257 @@
package org.mariotaku.twidere.util;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
public final class ArrayUtils {
private ArrayUtils() {
throw new AssertionError("You are trying to create an instance for this utility class!");
}
private ArrayUtils() {
throw new AssertionError("You are trying to create an instance for this utility class!");
}
public static boolean contains(final int[] array, final int value) {
if (array == null) return false;
for (final int item : array) {
if (item == value) return true;
}
return false;
}
public static boolean contains(final int[] array, final int value) {
if (array == null) return false;
for (final int item : array) {
if (item == value) return true;
}
return false;
}
public static boolean contains(final long[] array, final long value) {
if (array == null) return false;
for (final long item : array) {
if (item == value) return true;
}
return false;
}
public static boolean contains(final long[] array, final long value) {
if (array == null) return false;
for (final long item : array) {
if (item == value) return true;
}
return false;
}
public static boolean contains(final Object[] array, final Object value) {
if (array == null || value == null) return false;
return contains(array, new Object[] { value });
}
public static boolean contains(final Object[] array, final Object value) {
if (array == null || value == null) return false;
return contains(array, new Object[]{value});
}
public static boolean contains(final Object[] array, final Object[] values) {
if (array == null || values == null) return false;
for (final Object item : array) {
for (final Object value : values) {
if (item == null || value == null) {
if (item == value) return true;
continue;
}
if (item.equals(value)) return true;
}
}
return false;
}
public static boolean contains(final Object[] array, final Object[] values) {
if (array == null || values == null) return false;
for (final Object item : array) {
for (final Object value : values) {
if (item == null || value == null) {
if (item == value) return true;
continue;
}
if (item.equals(value)) return true;
}
}
return false;
}
public static boolean contentMatch(final Object[] array1, final Object[] array2) {
if (array1 == null || array2 == null) return array1 == array2;
if (array1.length != array2.length) return false;
final int length = array1.length;
for (int i = 0; i < length; i++) {
if (!contains(array2, array1[i])) return false;
}
return true;
}
public static boolean contentMatch(final Object[] array1, final Object[] array2) {
if (array1 == null || array2 == null) return array1 == array2;
if (array1.length != array2.length) return false;
final int length = array1.length;
for (int i = 0; i < length; i++) {
if (!contains(array2, array1[i])) return false;
}
return true;
}
public static long[] fromList(final List<Long> list) {
if (list == null) return null;
final int count = list.size();
final long[] array = new long[count];
for (int i = 0; i < count; i++) {
array[i] = list.get(i);
}
return array;
}
public static long[] fromList(final List<Long> list) {
if (list == null) return null;
final int count = list.size();
final long[] array = new long[count];
for (int i = 0; i < count; i++) {
array[i] = list.get(i);
}
return array;
}
public static int indexOf(final int[] array, final int value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i] == value) return i;
}
return -1;
}
public static int indexOf(final int[] array, final int value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i] == value) return i;
}
return -1;
}
public static int indexOf(final long[] array, final long value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i] == value) return i;
}
return -1;
}
public static int indexOf(final long[] array, final long value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i] == value) return i;
}
return -1;
}
public static int indexOf(final Object[] array, final Object value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i].equals(value)) return i;
}
return -1;
}
public static int indexOf(final Object[] array, final Object value) {
final int length = array.length;
for (int i = 0; i < length; i++) {
if (array[i].equals(value)) return i;
}
return -1;
}
public static long[] intersection(final long[] array1, final long[] array2) {
if (array1 == null || array2 == null) return new long[0];
final List<Long> list1 = new ArrayList<Long>();
for (final long item : array1) {
list1.add(item);
}
final List<Long> list2 = new ArrayList<Long>();
for (final long item : array2) {
list2.add(item);
}
list1.retainAll(list2);
return fromList(list1);
}
public static long[] intersection(final long[] array1, final long[] array2) {
if (array1 == null || array2 == null) return new long[0];
final List<Long> list1 = new ArrayList<Long>();
for (final long item : array1) {
list1.add(item);
}
final List<Long> list2 = new ArrayList<Long>();
for (final long item : array2) {
list2.add(item);
}
list1.retainAll(list2);
return fromList(list1);
}
public static void mergeArray(final Object[] dest, final Object[]... arrays) {
if (arrays == null || arrays.length == 0) return;
if (arrays.length == 1) {
final Object[] array = arrays[0];
System.arraycopy(array, 0, dest, 0, array.length);
return;
}
for (int i = 0, j = arrays.length - 1; i < j; i++) {
final Object[] array1 = arrays[i], array2 = arrays[i + 1];
System.arraycopy(array1, 0, dest, 0, array1.length);
System.arraycopy(array2, 0, dest, array1.length, array2.length);
}
}
public static void mergeArray(final Object[] dest, final Object[]... arrays) {
if (arrays == null || arrays.length == 0) return;
if (arrays.length == 1) {
final Object[] array = arrays[0];
System.arraycopy(array, 0, dest, 0, array.length);
return;
}
for (int i = 0, j = arrays.length - 1; i < j; i++) {
final Object[] array1 = arrays[i], array2 = arrays[i + 1];
System.arraycopy(array1, 0, dest, 0, array1.length);
System.arraycopy(array2, 0, dest, array1.length, array2.length);
}
}
public static String mergeArrayToString(final String[] array) {
if (array == null) return null;
final StringBuilder builder = new StringBuilder();
for (final String c : array) {
builder.append(c);
}
return builder.toString();
}
public static String mergeArrayToString(final String[] array) {
if (array == null) return null;
final StringBuilder builder = new StringBuilder();
for (final String c : array) {
builder.append(c);
}
return builder.toString();
}
public static long min(final long[] array) {
if (array == null || array.length == 0) throw new IllegalArgumentException();
long min = array[0];
for (int i = 1, j = array.length; i < j; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
public static long min(final long[] array) {
if (array == null || array.length == 0) throw new IllegalArgumentException();
long min = array[0];
for (int i = 1, j = array.length; i < j; i++) {
if (min > array[i]) {
min = array[i];
}
}
return min;
}
public static long[] parseLongArray(final String string, final char token) {
if (string == null) return new long[0];
final String[] items_string_array = string.split(String.valueOf(token));
final ArrayList<Long> items_list = new ArrayList<Long>();
for (final String id_string : items_string_array) {
try {
items_list.add(Long.parseLong(id_string));
} catch (final NumberFormatException e) {
// Ignore.
}
}
final int list_size = items_list.size();
final long[] array = new long[list_size];
for (int i = 0; i < list_size; i++) {
array[i] = items_list.get(i);
}
return array;
}
public static long[] parseLongArray(final String string, final char token) {
if (string == null) return new long[0];
final String[] items_string_array = string.split(String.valueOf(token));
final ArrayList<Long> items_list = new ArrayList<Long>();
for (final String id_string : items_string_array) {
try {
items_list.add(Long.parseLong(id_string));
} catch (final NumberFormatException e) {
// Ignore.
}
}
final int list_size = items_list.size();
final long[] array = new long[list_size];
for (int i = 0; i < list_size; i++) {
array[i] = items_list.get(i);
}
return array;
}
public static int[] subArray(final int[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final int[] result = new int[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static void reverse(@NonNull Object[] array) {
for (int i = 0; i < array.length / 2; i++) {
Object temp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = temp;
}
}
public static long[] subArray(final long[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final long[] result = new long[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static int[] subArray(final int[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final int[] result = new int[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static Object[] subArray(final Object[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final Object[] result = new Object[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static long[] subArray(final long[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final long[] result = new long[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static String[] subArray(final String[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final String[] result = new String[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static Object[] subArray(final Object[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final Object[] result = new Object[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static String toString(final long[] array, final char token, final boolean include_space) {
final StringBuilder builder = new StringBuilder();
final int length = array.length;
for (int i = 0; i < length; i++) {
final String id_string = String.valueOf(array[i]);
if (id_string != null) {
if (i > 0) {
builder.append(include_space ? token + " " : token);
}
builder.append(id_string);
}
}
return builder.toString();
}
public static String[] subArray(final String[] array, final int start, final int end) {
final int length = end - start;
if (length < 0) throw new IllegalArgumentException();
final String[] result = new String[length];
System.arraycopy(array, start, result, 0, length);
return result;
}
public static String toString(final Object[] array, final char token, final boolean include_space) {
final StringBuilder builder = new StringBuilder();
final int length = array.length;
for (int i = 0; i < length; i++) {
final String id_string = String.valueOf(array[i]);
if (id_string != null) {
if (i > 0) {
builder.append(include_space ? token + " " : token);
}
builder.append(id_string);
}
}
return builder.toString();
}
public static String toString(final long[] array, final char token, final boolean include_space) {
final StringBuilder builder = new StringBuilder();
final int length = array.length;
for (int i = 0; i < length; i++) {
final String id_string = String.valueOf(array[i]);
if (id_string != null) {
if (i > 0) {
builder.append(include_space ? token + " " : token);
}
builder.append(id_string);
}
}
return builder.toString();
}
public static String[] toStringArray(final Object[] array) {
if (array == null) return null;
final int length = array.length;
final String[] string_array = new String[length];
for (int i = 0; i < length; i++) {
string_array[i] = ParseUtils.parseString(array[i]);
}
return string_array;
}
public static String toString(final Object[] array, final char token, final boolean include_space) {
final StringBuilder builder = new StringBuilder();
final int length = array.length;
for (int i = 0; i < length; i++) {
final String id_string = String.valueOf(array[i]);
if (id_string != null) {
if (i > 0) {
builder.append(include_space ? token + " " : token);
}
builder.append(id_string);
}
}
return builder.toString();
}
public static String[] toStringArray(final String s) {
if (s == null) return null;
return s.split("(?!^)");
}
public static String[] toStringArray(final Object[] array) {
if (array == null) return null;
final int length = array.length;
final String[] string_array = new String[length];
for (int i = 0; i < length; i++) {
string_array[i] = ParseUtils.parseString(array[i]);
}
return string_array;
}
public static String toStringForSQL(final String[] array) {
final int size = array != null ? array.length : 0;
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
if (i > 0) {
builder.append(',');
}
builder.append('?');
}
return builder.toString();
}
public static String[] toStringArray(final String s) {
if (s == null) return null;
return s.split("(?!^)");
}
public static String toStringForSQL(final String[] array) {
final int size = array != null ? array.length : 0;
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < size; i++) {
if (i > 0) {
builder.append(',');
}
builder.append('?');
}
return builder.toString();
}
}

View File

@ -26,43 +26,42 @@ import java.lang.reflect.Method;
public final class FlymeUtils {
private static String[] SMARTBAR_SUPPORTED_DEVICES = { "mx2", "mx3" };
private static String[] SMARTBAR_SUPPORTED_DEVICES = {"mx2", "mx3"};
public static boolean hasSmartBar() {
try {
// Invoke Build.hasSmartBar()
final Method method = Build.class.getMethod("hasSmartBar");
return ((Boolean) method.invoke(null)).booleanValue();
} catch (final Exception e) {
}
// Detect by Build.DEVICE
if (isDeviceWithSmartBar(Build.DEVICE)) return true;
return false;
}
public static boolean hasSmartBar() {
try {
// Invoke Build.hasSmartBar()
final Method method = Build.class.getMethod("hasSmartBar");
return (Boolean) method.invoke(null);
} catch (final Exception ignored) {
}
// Detect by Build.DEVICE
return isDeviceWithSmartBar(Build.DEVICE);
}
public static boolean isDeviceWithSmartBar(final String buildDevice) {
for (final String dev : SMARTBAR_SUPPORTED_DEVICES) {
if (dev.equals(buildDevice)) return true;
}
return false;
}
public static boolean isDeviceWithSmartBar(final String buildDevice) {
for (final String dev : SMARTBAR_SUPPORTED_DEVICES) {
if (dev.equals(buildDevice)) return true;
}
return false;
}
public static boolean isFlyme() {
try {
// Invoke Build.hasSmartBar()
final Method method = Build.class.getMethod("hasSmartBar");
return method != null;
} catch (final Exception e) {
return false;
}
}
public static boolean isFlyme() {
try {
// Invoke Build.hasSmartBar()
final Method method = Build.class.getMethod("hasSmartBar");
return method != null;
} catch (final Exception e) {
return false;
}
}
public static void setActionModeHeaderHidden(final ActionBar actionbar, final boolean hidden) {
try {
final Method method = ActionBar.class.getMethod("setActionModeHeaderHidden", new Class[] { boolean.class });
method.invoke(actionbar, hidden);
} catch (final Exception e) {
e.printStackTrace();
}
}
public static void setActionModeHeaderHidden(final ActionBar actionbar, final boolean hidden) {
try {
final Method method = ActionBar.class.getMethod("setActionModeHeaderHidden", new Class[]{boolean.class});
method.invoke(actionbar, hidden);
} catch (final Exception e) {
e.printStackTrace();
}
}
}

View File

@ -53,6 +53,7 @@ import org.mariotaku.refreshnow.widget.RefreshNowProgressIndicator.IndicatorConf
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IThemedActivity;
import org.mariotaku.twidere.graphic.ActionBarColorDrawable;
import org.mariotaku.twidere.text.ParagraphSpacingSpan;
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
@ -240,7 +241,7 @@ public class ThemeUtils implements Constants {
public static Drawable getActionBarBackground(final Context context, final int themeRes,
final int accentColor) {
if (!isDarkTheme(themeRes)) {
final ColorDrawable d = new ColorDrawable(accentColor);
final ColorDrawable d = new ActionBarColorDrawable(accentColor);
return applyActionBarDrawable(context, d, isTransparentBackground(themeRes));
}
final TypedArray a = context.obtainStyledAttributes(null, new int[]{android.R.attr.background},

View File

@ -1,363 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.layout;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
/**
* Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
* <p/>
* It is developed to easily support vertical and horizontal orientations in a LayoutManager but
* can also be used to abstract calls around view bounds and child measurements with margins and
* decorations.
*
* @see #createHorizontalHelper(RecyclerView.LayoutManager)
* @see #createVerticalHelper(RecyclerView.LayoutManager)
*/
public abstract class OrientationHelper {
private static final int INVALID_SIZE = Integer.MIN_VALUE;
protected final RecyclerView.LayoutManager mLayoutManager;
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private int mLastTotalSpace = INVALID_SIZE;
private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
/**
* Call this method after onLayout method is complete if state is NOT pre-layout.
* This method records information like layout bounds that might be useful in the next layout
* calculations.
*/
public void onLayoutComplete() {
mLastTotalSpace = getTotalSpace();
}
/**
* Returns the layout space change between the previous layout pass and current layout pass.
* <p/>
* Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
* {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
* RecyclerView.State)} method.
*
* @return The difference between the current total space and previous layout's total space.
* @see #onLayoutComplete()
*/
public int getTotalSpaceChange() {
return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
}
/**
* Returns the start of the view including its decoration and margin.
* <p/>
* For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
* decoration and 3px left margin, returned value will be 15px.
*
* @param view The view element to check
* @return The first pixel of the element
* @see #getDecoratedEnd(android.view.View)
*/
public abstract int getDecoratedStart(View view);
/**
* Returns the end of the view including its decoration and margin.
* <p/>
* For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
* decoration and 3px right margin, returned value will be 205.
*
* @param view The view element to check
* @return The last pixel of the element
* @see #getDecoratedStart(android.view.View)
*/
public abstract int getDecoratedEnd(View view);
/**
* Returns the space occupied by this View in the current orientation including decorations and
* margins.
*
* @param view The view element to check
* @return Total space occupied by this view
* @see #getDecoratedMeasurementInOther(View)
*/
public abstract int getDecoratedMeasurement(View view);
/**
* Returns the space occupied by this View in the perpendicular orientation including
* decorations and margins.
*
* @param view The view element to check
* @return Total space occupied by this view in the perpendicular orientation to current one
* @see #getDecoratedMeasurement(View)
*/
public abstract int getDecoratedMeasurementInOther(View view);
/**
* Returns the start position of the layout after the start padding is added.
*
* @return The very first pixel we can draw.
*/
public abstract int getStartAfterPadding();
/**
* Returns the end position of the layout after the end padding is removed.
*
* @return The end boundary for this layout.
*/
public abstract int getEndAfterPadding();
/**
* Returns the end position of the layout without taking padding into account.
*
* @return The end boundary for this layout without considering padding.
*/
public abstract int getEnd();
/**
* Offsets all children's positions by the given amount.
*
* @param amount Value to add to each child's layout parameters
*/
public abstract void offsetChildren(int amount);
/**
* Returns the total space to layout. This number is the difference between
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
*
* @return Total space to layout children
*/
public abstract int getTotalSpace();
/**
* Offsets the child in this orientation.
*
* @param view View to offset
* @param offset offset amount
*/
public abstract void offsetChild(View view, int offset);
/**
* Returns the padding at the end of the layout. For horizontal helper, this is the right
* padding and for vertical helper, this is the bottom padding. This method does not check
* whether the layout is RTL or not.
*
* @return The padding at the end of the layout.
*/
public abstract int getEndPadding();
/**
* Creates an OrientationHelper for the given LayoutManager and orientation.
*
* @param layoutManager LayoutManager to attach to
* @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
* @return A new OrientationHelper
*/
public static OrientationHelper createOrientationHelper(
RecyclerView.LayoutManager layoutManager, int orientation) {
switch (orientation) {
case HORIZONTAL:
return createHorizontalHelper(layoutManager);
case VERTICAL:
return createVerticalHelper(layoutManager);
}
throw new IllegalArgumentException("invalid orientation");
}
/**
* Creates a horizontal OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createHorizontalHelper(
RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
}
@Override
public int getEnd() {
return mLayoutManager.getWidth();
}
@Override
public void offsetChildren(int amount) {
mLayoutManager.offsetChildrenHorizontal(amount);
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingLeft();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- mLayoutManager.getPaddingRight();
}
@Override
public void offsetChild(View view, int offset) {
view.offsetLeftAndRight(offset);
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingRight();
}
};
}
/**
* Creates a vertical OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
int diff;
@Override
public int getEndAfterPadding() {
int heightSum = 0;
for (int i = 0, j = mLayoutManager.getChildCount(); i < j; i++) {
final View child = mLayoutManager.getChildAt(i);
if (mLayoutManager.getItemViewType(child) == 0 || heightSum != 0) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
child.getLayoutParams();
heightSum += mLayoutManager.getDecoratedMeasuredHeight(child)
+ params.topMargin + params.bottomMargin;
}
}
final int normalEnd = mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
if (heightSum == 0) {
diff = 0;
return normalEnd;
}
diff = (mLayoutManager.getPaddingTop() + heightSum) - normalEnd;
// return normalEnd;
return Math.min(mLayoutManager.getPaddingTop() + heightSum, normalEnd);
}
@Override
public int getEnd() {
return mLayoutManager.getHeight() + Math.max(0, diff);
}
@Override
public void offsetChildren(int amount) {
mLayoutManager.offsetChildrenVertical(amount);
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingTop();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedTop(view) - params.topMargin;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
- mLayoutManager.getPaddingBottom();
}
@Override
public void offsetChild(View view, int offset) {
view.offsetTopAndBottom(offset);
mLayoutManager.requestLayout();
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingBottom();
}
};
}
}

View File

@ -1,99 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.layout;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* A helper class to do scroll offset calculations.
*/
class ScrollbarHelper {
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled, boolean reverseLayout) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
final int minPosition = Math.min(lm.getPosition(startChild), lm.getPosition(endChild));
final int maxPosition = Math.max(lm.getPosition(startChild), lm.getPosition(endChild));
final int itemsBefore = reverseLayout
? Math.max(0, state.getItemCount() - maxPosition - 1)
: Math.max(0, minPosition);
if (!smoothScrollbarEnabled) {
return itemsBefore;
}
final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
orientation.getDecoratedStart(startChild));
final int itemRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
final float avgSizePerRow = (float) laidOutArea / itemRange;
return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
- orientation.getDecoratedStart(startChild)));
}
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
if (!smoothScrollbarEnabled) {
return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
}
final int extend = orientation.getDecoratedEnd(endChild)
- orientation.getDecoratedStart(startChild);
return Math.min(orientation.getTotalSpace(), extend);
}
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
if (!smoothScrollbarEnabled) {
return state.getItemCount();
}
// smooth scrollbar enabled. try to estimate better.
final int laidOutArea = orientation.getDecoratedEnd(endChild) -
orientation.getDecoratedStart(startChild);
final int laidOutRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild))
+ 1;
// estimate a size for full list.
return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
}
}

View File

@ -0,0 +1,249 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.Recycler;
import android.support.v7.widget.RecyclerView.State;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.view.iface.IColorLabelView.Helper;
/**
* Created by mariotaku on 14/12/8.
*/
public class ComposeSelectAccountButton extends ViewGroup {
private final AccountIconsAdapter mAccountIconsAdapter;
private final Helper mColorLabelHelper;
private OnClickListener mOnClickListener;
private OnLongClickListener mOnLongClickListener;
public ComposeSelectAccountButton(Context context) {
this(context, null);
}
public ComposeSelectAccountButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ComposeSelectAccountButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mColorLabelHelper = new Helper(this, context, attrs, defStyle);
mColorLabelHelper.setIgnorePaddings(true);
mAccountIconsAdapter = new AccountIconsAdapter(context);
final RecyclerView recyclerView = new InternalRecyclerView(context);
final LinearLayoutManager linearLayoutManager = new MyLinearLayoutManager(context);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// linearLayoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(mAccountIconsAdapter);
ViewCompat.setOverScrollMode(recyclerView, ViewCompat.OVER_SCROLL_NEVER);
addView(recyclerView);
}
@Override
protected void dispatchDraw(@NonNull final Canvas canvas) {
mColorLabelHelper.dispatchDrawBackground(canvas);
super.dispatchDraw(canvas);
mColorLabelHelper.dispatchDrawLabels(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = 0;
for (int i = 0, j = getChildCount(); i < j; i++) {
final View child = getChildAt(i);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
}
if (maxWidth == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0, j = getChildCount(); i < j; i++) {
final View child = getChildAt(i);
child.layout(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(),
b - t - getPaddingBottom());
}
}
static class AccountIconViewHolder extends ViewHolder {
private final ImageView iconView;
public AccountIconViewHolder(View itemView) {
super(itemView);
iconView = (ImageView) itemView.findViewById(android.R.id.icon);
}
public void showAccount(AccountIconsAdapter adapter, ParcelableAccount account) {
final ImageLoaderWrapper loader = adapter.getImageLoader();
loader.displayProfileImage(iconView, account.profile_image_url);
}
}
public void setSelectedAccounts(long[] accountIds) {
final ParcelableAccount[] accounts = ParcelableAccount.getAccounts(getContext(), accountIds);
if (accounts != null) {
final int[] colors = new int[accounts.length];
for (int i = 0, j = colors.length; i < j; i++) {
colors[i] = accounts[i].color;
}
mColorLabelHelper.drawEnd(colors);
} else {
mColorLabelHelper.drawEnd(null);
}
mAccountIconsAdapter.setSelectedAccounts(accounts);
}
private static class AccountIconsAdapter extends Adapter<AccountIconViewHolder> {
private final Context mContext;
private final LayoutInflater mInflater;
private final ImageLoaderWrapper mImageLoader;
private ParcelableAccount[] mAccounts;
public ImageLoaderWrapper getImageLoader() {
return mImageLoader;
}
@Override
public AccountIconViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = mInflater.inflate(R.layout.adapter_item_compose_account, parent, false);
return new AccountIconViewHolder(view);
}
@Override
public void onBindViewHolder(AccountIconViewHolder holder, int position) {
holder.showAccount(this, mAccounts[position]);
}
@Override
public int getItemCount() {
return mAccounts != null ? mAccounts.length : 0;
}
public void setSelectedAccounts(ParcelableAccount[] accounts) {
if (accounts != null) {
// ArrayUtils.reverse(accounts);
}
mAccounts = accounts;
notifyDataSetChanged();
}
public AccountIconsAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(context);
mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper();
}
}
private static class MyLinearLayoutManager extends LinearLayoutManager {
private int mWidth, mHeight;
public MyLinearLayoutManager(Context context) {
super(context);
}
@Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
final RecyclerView.LayoutParams layoutParams = super.generateLayoutParams(c, attrs);
return layoutParams;
}
@Override
public void onLayoutChildren(Recycler recycler, State state) {
state.getItemCount();
super.onLayoutChildren(recycler, state);
}
private int findChildIndex(View view) {
for (int i = 0, j = getChildCount(); i < j; i++) {
if (getChildAt(i) == view) return i;
}
return -1;
}
@Override
public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
final RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int contentWidth = mWidth - getPaddingLeft() - getPaddingRight();
final int contentHeight = mHeight - getPaddingTop() - getPaddingBottom();
final int itemCount = getItemCount();
final int firstVisibleItem = findFirstVisibleItemPosition();
final int idx = findChildIndex(child);
if (firstVisibleItem < 1 && idx == 0) {
// when firstVisibleItem is 0 or -1, assume view with idx == 0 is first view
layoutParams.leftMargin = 0;
} else {
layoutParams.leftMargin = -Math.min((contentHeight * itemCount - contentWidth)
/ (itemCount - 1) + child.getPaddingLeft() + child.getPaddingRight(), contentHeight);
}
super.measureChildWithMargins(child, widthUsed, heightUsed);
}
@Override
public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
final int height = MeasureSpec.getSize(heightSpec), width = Math.round(height * 1.5f);
mWidth = width;
mHeight = height;
super.onMeasure(recycler, state, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec);
}
}
private static class InternalRecyclerView extends RecyclerView {
public InternalRecyclerView(Context context) {
super(context);
setChildrenDrawingOrderEnabled(true);
}
@Override
protected int getChildDrawingOrder(int childCount, int i) {
return childCount - i - 1;
}
@Override
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
return false;
}
}
}

View File

@ -35,7 +35,6 @@ public class HomeSlidingMenu extends SlidingMenu implements Constants {
mActivity = (HomeActivity) context;
}
@Override
public boolean dispatchTouchEvent(@NonNull final MotionEvent ev) {
switch (ev.getActionMasked()) {

View File

@ -1,18 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<org.mariotaku.twidere.view.ColorLabelFrameLayout
<org.mariotaku.twidere.view.ComposeSelectAccountButton
android:id="@+id/select_account"
style="?android:actionButtonStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:ignorePadding="true">
<org.mariotaku.twidere.view.ActionIconView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="center"
android:src="@drawable/ic_action_accounts"/>
</org.mariotaku.twidere.view.ColorLabelFrameLayout>
android:layout_weight="0"
android:padding="@dimen/element_spacing_small"/>

View File

@ -6,6 +6,5 @@
android:id="@+id/home_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:viewAbove="@layout/activity_home_content"/>
</merge>

View File

@ -1,9 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2014 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/>.
-->
<org.mariotaku.twidere.view.SquareFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="@dimen/element_spacing_msmall">
android:padding="@dimen/element_spacing_small">
<org.mariotaku.twidere.view.CircularImageView
android:id="@android:id/icon"

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2014 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/>.
-->
<org.mariotaku.twidere.view.SquareFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="@dimen/element_spacing_msmall">
<org.mariotaku.twidere.view.CircularImageView
android:id="@android:id/icon"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</org.mariotaku.twidere.view.SquareFrameLayout>

View File

@ -18,6 +18,7 @@
<dimen name="element_spacing_minus_msmall">-6dp</dimen>
<dimen name="element_spacing_minus_normal">-8dp</dimen>
<dimen name="element_spacing_minus_large">-16dp</dimen>
<dimen name="element_spacing_minus_xlarge">-24dp</dimen>
<dimen name="icon_size_list_item">56dp</dimen>
<dimen name="icon_size_list_item_small">42dp</dimen>
<dimen name="icon_size_card_list_item">48dp</dimen>