/*
* Copyright (C) 2016 Thomas Robert Altstidl
*
* 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 com.tr4android.support.extension.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.design.internal.NavigationMenuView;
import android.support.v7.view.ContextThemeWrapper;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tr4android.support.extension.animation.AnimationUtils;
import com.tr4android.support.extension.drawable.RotationTransitionDrawable;
import com.tr4android.support.extension.internal.Account;
import com.tr4android.support.extension.internal.AccountAdapter;
import com.tr4android.appcompat.extension.R;
import com.tr4android.support.extension.utils.ThemeUtils;
import java.util.ArrayList;
/**
* An account header layout build specifically for the AppCompat Design Library NavigationView
*/
public class AccountHeaderView extends RelativeLayout {
private static final String LOG_TAG = "AccountHeaderView";
// The RecyclerView subclass used in the AppCompat Design Library NavigationView
// Note: this is prone to changes in the official support library
private NavigationMenuView mNavigationMenuView;
// The parent of this view (for removing and adding itself)
private ViewGroup mParent;
// The menu adapter used by the NavigationView
private RecyclerView.Adapter mMenuAdapter;
// The account list adapter
private AccountAdapter mAccountListAdapter;
// Whether or not the account list is currently shown
private boolean mIsShowingAccountList;
// The click listener for showing the account list
private OnClickListener mShowAccountClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsShowingAccountList) {
hideAccountList();
} else {
showAccountList();
}
}
};
// The listener for account related actions
private OnAccountSelectedListener mListener;
// The CircleImageViews used
private CircleImageView mPrimaryIconView;
private CircleImageView mSecondaryFirstIconView;
private CircleImageView mSecondarySecondIconView;
// The TextVies used
private TextView mNameView;
private TextView mEmailView;
// The ImageView used for toggling the account list
private ImageView mDropdownView;
private final RotationTransitionDrawable mDropdownDrawable;
public AccountHeaderView(Context context) {
this(context, null);
}
public AccountHeaderView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AccountHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(new ContextThemeWrapper(context, R.style.Widget_Design_AccountHeaderView), attrs, defStyleAttr);
// Provide default background if none is set
if (getBackground() == null) {
setBackgroundColor(ThemeUtils.getThemeAttrColor(context, R.attr.colorPrimary));
}
// Retrieve the style attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AccountHeaderView, defStyleAttr, 0);
boolean mAddAccountEnabled = a.getBoolean(R.styleable.AccountHeaderView_accountHeaderAddEnabled, true);
boolean mManageAccountEnabled = a.getBoolean(R.styleable.AccountHeaderView_accountHeaderManageEnabled, true);
boolean mCheckableAccountsEnabled = a.getBoolean(R.styleable.AccountHeaderView_accountHeaderCheckableAccountsEnabled, false);
a.recycle();
// Inflate the layout
LayoutInflater.from(context).inflate(R.layout.appcompat_extension_account_header, this, true);
// Create an empty adapter (for "Add account" and "Manage accounts" entries)
mAccountListAdapter = new AccountAdapter(new ArrayList<Account>(), this,
mAddAccountEnabled, mManageAccountEnabled, mCheckableAccountsEnabled);
// Retrieve and setup the CircleImageViews used
mPrimaryIconView = (CircleImageView) findViewById(R.id.account_header_icon_primary);
mSecondaryFirstIconView = (CircleImageView) findViewById(R.id.account_header_icon_secondary_first);
mSecondaryFirstIconView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleAccountClick(1);
}
});
mSecondarySecondIconView = (CircleImageView) findViewById(R.id.account_header_icon_secondary_second);
mSecondarySecondIconView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleAccountClick(2);
}
});
// Retrieve the TextViews used
mNameView = (TextView) findViewById(R.id.account_header_text_name);
mEmailView = (TextView) findViewById(R.id.account_header_text_email);
// Retrieve and setup the ImageView
mDropdownView = (ImageView) findViewById(R.id.account_header_dropdown);
mDropdownDrawable = new RotationTransitionDrawable(mDropdownView.getDrawable());
mDropdownDrawable.setMaxRotation(-180f);
mDropdownDrawable.setStartInterpolator(AnimationUtils.ACCELERATE_DECELERATE_INTERPOLATOR);
mDropdownDrawable.setReverseInterpolator(AnimationUtils.ACCELERATE_DECELERATE_INTERPOLATOR);
mDropdownView.setImageDrawable(mDropdownDrawable);
mDropdownView.setOnClickListener(mShowAccountClickListener);
}
// Handles an account click passed on by the account list adapter
public void handleAccountClick(int position) {
hideAccountList();
int count = mAccountListAdapter.getItemCount();
if (position < mAccountListAdapter.getAccountCount()) {
Account selectedAccount = mAccountListAdapter.get(position);
boolean makePrimary = true;
if (mListener != null) makePrimary = mListener.onAccountSelected(selectedAccount);
if (makePrimary) switchPrimaryAccount(selectedAccount);
} else if (position == count - 1 && mAccountListAdapter.isAccountManageEnabled()) {
if (mListener != null) mListener.onAccountManageSelected();
} else {
if (mListener != null) mListener.onAccountAddSelected();
}
}
// Handles an account check passed on by the account list adapter
public void handleAccountCheck(int position, boolean isChecked) {
Account checkedAccount = mAccountListAdapter.get(position);
if (mListener != null) mListener.onAccountChecked(checkedAccount, isChecked);
}
public void addAccounts(Account... accounts) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
for (Account account : accounts) {
mAccountListAdapter.add(account);
}
} else {
mAccountListAdapter.addAll(accounts);
}
updateAccountHeader();
}
public void addAccount(Account account) {
mAccountListAdapter.add(account);
updateAccountHeader();
}
public void removeAccount(Account account) {
mAccountListAdapter.remove(account);
updateAccountHeader();
}
public void insertAccount(Account account, int position) {
mAccountListAdapter.insert(account, position);
updateAccountHeader();
}
public void clearAccounts() {
mAccountListAdapter.clear();
updateAccountHeader();
}
public ArrayList<Account> getAccounts() {
return mAccountListAdapter.getAll();
}
public void checkAccount(Account account, boolean checked) {
mAccountListAdapter.setChecked(mAccountListAdapter.indexOf(account), checked);
}
public ArrayList<Account> getCheckedAccounts() {
return mAccountListAdapter.getChecked();
}
public void setAccountSelectedListener(OnAccountSelectedListener listener) {
mListener = listener;
}
public boolean isShowingAccountList() {
return mIsShowingAccountList;
}
public void showAccountList() {
if (!mIsShowingAccountList) {
mDropdownDrawable.startTransition(200);
// cache menu adapter for later
removeViewFromParent();
mMenuAdapter = mNavigationMenuView.getAdapter();
mNavigationMenuView.setAdapter(mAccountListAdapter);
}
mIsShowingAccountList = true;
}
public void hideAccountList() {
if (mIsShowingAccountList) {
mDropdownDrawable.reverseTransition(200);
// reset previously cached menu adapter
mNavigationMenuView.setAdapter(mMenuAdapter);
mMenuAdapter = null;
addViewToParent();
}
mIsShowingAccountList = false;
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w > 0 && h > 0 && mNavigationMenuView == null)
mNavigationMenuView = (NavigationMenuView) getParent().getParent();
}
private void switchPrimaryAccount(Account newPrimaryAccount) {
mAccountListAdapter.move(newPrimaryAccount, 0);
updateAccountHeader();
}
private void updateAccountHeader() {
int accountCount = mAccountListAdapter.getAccountCount();
// primary account
if (accountCount == 0) {
mPrimaryIconView.setVisibility(GONE);
mNameView.setVisibility(GONE);
mEmailView.setVisibility(GONE);
} else {
Account primaryAccount = mAccountListAdapter.get(0);
primaryAccount.applyAccountIcon(mPrimaryIconView);
primaryAccount.applyAccountName(mNameView);
primaryAccount.applyAccountEmail(mEmailView);
mPrimaryIconView.setVisibility(VISIBLE);
mNameView.setVisibility(VISIBLE);
mEmailView.setVisibility(VISIBLE);
}
// first secondary account
if (accountCount <= 1) {
mSecondaryFirstIconView.setVisibility(GONE);
} else {
mAccountListAdapter.get(1).applyAccountIcon(mSecondaryFirstIconView);
mSecondaryFirstIconView.setVisibility(VISIBLE);
}
// second secondary account
if (accountCount <= 2) {
mSecondarySecondIconView.setVisibility(GONE);
} else {
mAccountListAdapter.get(2).applyAccountIcon(mSecondarySecondIconView);
mSecondarySecondIconView.setVisibility(VISIBLE);
}
}
private void removeViewFromParent() {
getViewParent().removeView(this);
}
private void addViewToParent() {
getViewParent().addView(this);
}
private ViewGroup getViewParent() {
if (mParent == null) {
mParent = (ViewGroup) getParent();
}
return mParent;
}
/**
* Listener for handling events on accounts
*/
public interface OnAccountSelectedListener {
/**
* Called when an account in the account header is selected.
*
* @param account The selected account
*
* @return true to display the selected account as the primary account
*/
boolean onAccountSelected(Account account);
/**
* Called when an account in the account header is checked
*
* @param account The checked account
* @param isChecked True if the account is now checked
*/
void onAccountChecked(Account account, boolean isChecked);
/**
* Called when the "Add account" item is selected.
*/
void onAccountAddSelected();
/**
* Called when the "Manage accounts" item is selected.
*/
void onAccountManageSelected();
}
/**
* Adapter class for listener
*/
public static class OnAccountSelectedListenerAdapter implements OnAccountSelectedListener {
@Override
public boolean onAccountSelected(Account account) {
return true;
}
@Override
public void onAccountChecked(Account account, boolean isChecked) {
}
@Override
public void onAccountAddSelected() {
}
@Override
public void onAccountManageSelected() {
}
}
}