io.plaidapp.ui.PlayerSheet.java Source code

Java tutorial

Introduction

Here is the source code for io.plaidapp.ui.PlayerSheet.java

Source

/*
 * Copyright 2016 Google Inc.
 *
 * 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 io.plaidapp.ui;

import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import butterknife.BindDimen;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.plaidapp.R;
import io.plaidapp.data.DataLoadingSubject;
import io.plaidapp.data.PaginatedDataManager;
import io.plaidapp.data.api.dribbble.FollowersDataManager;
import io.plaidapp.data.api.dribbble.ShotLikesDataManager;
import io.plaidapp.data.api.dribbble.model.Follow;
import io.plaidapp.data.api.dribbble.model.Like;
import io.plaidapp.data.api.dribbble.model.PlayerListable;
import io.plaidapp.data.api.dribbble.model.Shot;
import io.plaidapp.data.api.dribbble.model.User;
import io.plaidapp.ui.recyclerview.InfiniteScrollListener;
import io.plaidapp.ui.recyclerview.SlideInItemAnimator;
import io.plaidapp.ui.widget.BottomSheet;
import io.plaidapp.util.DribbbleUtils;
import io.plaidapp.util.glide.CircleTransform;

import static io.plaidapp.util.AnimUtils.getLinearOutSlowInInterpolator;

public class PlayerSheet extends Activity {

    private static final int MODE_SHOT_LIKES = 1;
    private static final int MODE_FOLLOWERS = 2;
    private static final int DISMISS_DOWN = 0;
    private static final int DISMISS_CLOSE = 1;
    private static final String EXTRA_MODE = "EXTRA_MODE";
    private static final String EXTRA_SHOT = "EXTRA_SHOT";
    private static final String EXTRA_USER = "EXTRA_USER";

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ MODE_SHOT_LIKES, MODE_FOLLOWERS })
    @interface PlayerSheetMode {
    }

    @BindView(R.id.bottom_sheet)
    BottomSheet bottomSheet;
    @BindView(R.id.bottom_sheet_content)
    ViewGroup content;
    @BindView(R.id.title_bar)
    ViewGroup titleBar;
    @BindView(R.id.close)
    ImageView close;
    @BindView(R.id.title)
    TextView title;
    @BindView(R.id.player_list)
    RecyclerView playerList;
    @BindDimen(R.dimen.large_avatar_size)
    int largeAvatarSize;
    private @Nullable Shot shot;
    private @Nullable User player;
    private PaginatedDataManager dataManager;
    private LinearLayoutManager layoutManager;
    private PlayerAdapter adapter;
    private int dismissState = DISMISS_DOWN;

    public static void start(Activity launching, Shot shot) {
        Intent starter = new Intent(launching, PlayerSheet.class);
        starter.putExtra(EXTRA_MODE, MODE_SHOT_LIKES);
        starter.putExtra(EXTRA_SHOT, shot);
        launching.startActivity(starter, ActivityOptions.makeSceneTransitionAnimation(launching).toBundle());
    }

    public static void start(Activity launching, User player) {
        Intent starter = new Intent(launching, PlayerSheet.class);
        starter.putExtra(EXTRA_MODE, MODE_FOLLOWERS);
        starter.putExtra(EXTRA_USER, player);
        launching.startActivity(starter, ActivityOptions.makeSceneTransitionAnimation(launching).toBundle());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.player_sheet);
        ButterKnife.bind(this);

        final Intent intent = getIntent();
        final @PlayerSheetMode int mode = intent.getIntExtra(EXTRA_MODE, -1);
        switch (mode) {
        case MODE_SHOT_LIKES:
            shot = intent.getParcelableExtra(EXTRA_SHOT);
            title.setText(getResources().getQuantityString(R.plurals.fans, (int) shot.likes_count,
                    NumberFormat.getInstance().format(shot.likes_count)));
            dataManager = new ShotLikesDataManager(this, shot.id) {
                @Override
                public void onDataLoaded(List<Like> likes) {
                    adapter.addItems(likes);
                }
            };
            break;
        case MODE_FOLLOWERS:
            player = intent.getParcelableExtra(EXTRA_USER);
            title.setText(getResources().getQuantityString(R.plurals.follower_count, player.followers_count,
                    NumberFormat.getInstance().format(player.followers_count)));
            dataManager = new FollowersDataManager(this, player.id) {
                @Override
                public void onDataLoaded(List<Follow> followers) {
                    adapter.addItems(followers);
                }
            };
            break;
        default:
            throw new IllegalArgumentException("Unknown launch mode.");
        }

        bottomSheet.registerCallback(new BottomSheet.Callbacks() {
            @Override
            public void onSheetDismissed() {
                finishAfterTransition();
            }

            @Override
            public void onSheetPositionChanged(int sheetTop, boolean interacted) {
                if (interacted && close.getVisibility() != View.VISIBLE) {
                    close.setVisibility(View.VISIBLE);
                    close.setAlpha(0f);
                    close.animate().alpha(1f).setDuration(400L)
                            .setInterpolator(getLinearOutSlowInInterpolator(PlayerSheet.this)).start();
                }
                if (sheetTop == 0) {
                    showClose();
                } else {
                    showDown();
                }
            }
        });

        layoutManager = new LinearLayoutManager(this);
        playerList.setLayoutManager(layoutManager);
        playerList.setItemAnimator(new SlideInItemAnimator());
        adapter = new PlayerAdapter(this);
        dataManager.registerCallback(adapter);
        playerList.setAdapter(adapter);
        playerList.addOnScrollListener(new InfiniteScrollListener(layoutManager, dataManager) {
            @Override
            public void onLoadMore() {
                dataManager.loadData();
            }
        });
        playerList.addOnScrollListener(titleElevation);
        dataManager.loadData(); // kick off initial load
    }

    @Override
    protected void onDestroy() {
        dataManager.cancelLoading();
        super.onDestroy();
    }

    private RecyclerView.OnScrollListener titleElevation = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            final boolean raiseTitleBar = dy > 0 || playerList.computeVerticalScrollOffset() != 0;
            titleBar.setActivated(raiseTitleBar); // animated via a StateListAnimator
        }
    };

    private void showClose() {
        if (dismissState == DISMISS_CLOSE)
            return;
        dismissState = DISMISS_CLOSE;
        final AnimatedVectorDrawable downToClose = (AnimatedVectorDrawable) ContextCompat.getDrawable(this,
                R.drawable.avd_down_to_close);
        close.setImageDrawable(downToClose);
        downToClose.start();
    }

    private void showDown() {
        if (dismissState == DISMISS_DOWN)
            return;
        dismissState = DISMISS_DOWN;
        final AnimatedVectorDrawable closeToDown = (AnimatedVectorDrawable) ContextCompat.getDrawable(this,
                R.drawable.avd_close_to_down);
        close.setImageDrawable(closeToDown);
        closeToDown.start();
    }

    @OnClick({ R.id.bottom_sheet, R.id.close })
    public void dismiss(View view) {
        if (view.getVisibility() != View.VISIBLE)
            return;
        bottomSheet.dismiss();
    }

    private class PlayerAdapter<T extends PlayerListable> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
            implements DataLoadingSubject.DataLoadingCallbacks {

        private static final int TYPE_PLAYER = 7;
        private static final int TYPE_LOADING = -1;
        private List<T> items;
        private boolean loading = true;
        private LayoutInflater layoutInflater;
        private CircleTransform circleTransform;

        PlayerAdapter(Context context) {
            layoutInflater = LayoutInflater.from(context);
            items = new ArrayList<>();
            circleTransform = new CircleTransform(context);
            setHasStableIds(true);
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            switch (viewType) {
            case TYPE_PLAYER:
                return createPlayerViewHolder(parent);
            default: // TYPE_LOADING
                return new LoadingViewHolder(layoutInflater.inflate(R.layout.list_loading, parent, false));
            }
        }

        @NonNull
        private PlayerViewHolder createPlayerViewHolder(ViewGroup parent) {
            final PlayerViewHolder holder = new PlayerViewHolder(
                    layoutInflater.inflate(R.layout.player_item, parent, false));
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    final User user = items.get(holder.getAdapterPosition()).getPlayer();
                    final Intent player = new Intent(PlayerSheet.this, PlayerActivity.class);
                    player.putExtra(PlayerActivity.EXTRA_PLAYER, user);
                    final ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(PlayerSheet.this,
                            Pair.create((View) holder.playerAvatar, getString(R.string.transition_player_avatar)),
                            Pair.create(holder.itemView, getString(R.string.transition_player_background)));
                    startActivity(player, options.toBundle());
                }
            });
            return holder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (position == getLoadingMoreItemPosition())
                return;
            bindPlayer(((PlayerViewHolder) holder), items.get(position));
        }

        private void bindPlayer(PlayerViewHolder holder, T player) {
            Glide.with(holder.itemView.getContext()).load(player.getPlayer().getHighQualityAvatarUrl())
                    .transform(circleTransform).placeholder(R.drawable.avatar_placeholder)
                    .override(largeAvatarSize, largeAvatarSize).into(holder.playerAvatar);
            holder.playerName.setText(player.getPlayer().name.toLowerCase());
            if (!TextUtils.isEmpty(player.getPlayer().bio)) {
                DribbbleUtils.parseAndSetText(holder.playerBio, player.getPlayer().bio);
            } else if (!TextUtils.isEmpty(player.getPlayer().location)) {
                holder.playerBio.setText(player.getPlayer().location);
            }
            holder.timeAgo
                    .setText(DateUtils
                            .getRelativeTimeSpanString(player.getDateCreated().getTime(),
                                    System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS)
                            .toString().toLowerCase());
        }

        @Override
        public int getItemViewType(int position) {
            if (position < getDataItemCount() && getDataItemCount() > 0) {
                return TYPE_PLAYER;
            }
            return TYPE_LOADING;
        }

        @Override
        public long getItemId(int position) {
            if (getItemViewType(position) == TYPE_LOADING) {
                return -1L;
            }
            return items.get(position).getId();
        }

        public int getDataItemCount() {
            return items.size();
        }

        private int getLoadingMoreItemPosition() {
            return loading ? getItemCount() - 1 : RecyclerView.NO_POSITION;
        }

        @Override
        public int getItemCount() {
            return getDataItemCount() + (loading ? 1 : 0);
        }

        @Override
        public void dataStartedLoading() {
            if (loading)
                return;
            loading = true;
            notifyItemInserted(getLoadingMoreItemPosition());
        }

        @Override
        public void dataFinishedLoading() {
            if (!loading)
                return;
            final int loadingPos = getLoadingMoreItemPosition();
            loading = false;
            notifyItemRemoved(loadingPos);
        }

        public void addItems(List<T> newItems) {
            final int insertRangeStart = getDataItemCount();
            items.addAll(newItems);
            notifyItemRangeInserted(insertRangeStart, newItems.size());
        }
    }

    /* package */ static class PlayerViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.player_avatar)
        ImageView playerAvatar;
        @BindView(R.id.player_name)
        TextView playerName;
        @BindView(R.id.player_bio)
        TextView playerBio;
        @BindView(R.id.time_ago)
        TextView timeAgo;

        public PlayerViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    private static class LoadingViewHolder extends RecyclerView.ViewHolder {

        ProgressBar progress;

        public LoadingViewHolder(View itemView) {
            super(itemView);
            progress = (ProgressBar) itemView;
        }

    }
}