/*
* Copyright 2015 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.Intent;
import android.content.res.Resources;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.transition.TransitionManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.text.NumberFormat;
import java.util.List;
import butterknife.BindInt;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.plaidapp.R;
import io.plaidapp.data.api.dribbble.PlayerShotsDataManager;
import io.plaidapp.data.api.dribbble.model.Shot;
import io.plaidapp.data.api.dribbble.model.User;
import io.plaidapp.data.pocket.PocketUtils;
import io.plaidapp.data.prefs.DribbblePrefs;
import io.plaidapp.ui.recyclerview.InfiniteScrollListener;
import io.plaidapp.ui.recyclerview.SlideInItemAnimator;
import io.plaidapp.ui.transitions.MorphTransform;
import io.plaidapp.ui.widget.ElasticDragDismissFrameLayout;
import io.plaidapp.util.DribbbleUtils;
import io.plaidapp.util.ViewUtils;
import io.plaidapp.util.glide.CircleTransform;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* A screen displaying a player's details and their shots.
*/
public class PlayerActivity extends Activity {
public static final String EXTRA_PLAYER = "EXTRA_PLAYER";
public static final String EXTRA_PLAYER_NAME = "EXTRA_PLAYER_NAME";
public static final String EXTRA_PLAYER_ID = "EXTRA_PLAYER_ID";
public static final String EXTRA_PLAYER_USERNAME = "EXTRA_PLAYER_USERNAME";
User player;
PlayerShotsDataManager dataManager;
FeedAdapter adapter;
GridLayoutManager layoutManager;
Boolean following;
private ElasticDragDismissFrameLayout.SystemChromeFader chromeFader;
private CircleTransform circleTransform;
private int followerCount;
@BindView(R.id.draggable_frame) ElasticDragDismissFrameLayout draggableFrame;
@BindView(R.id.container) ViewGroup container;
@BindView(R.id.avatar) ImageView avatar;
@BindView(R.id.player_name) TextView playerName;
@BindView(R.id.follow) Button follow;
@BindView(R.id.player_bio) TextView bio;
@BindView(R.id.shot_count) TextView shotCount;
@BindView(R.id.followers_count) TextView followersCount;
@BindView(R.id.likes_count) TextView likesCount;
@BindView(R.id.loading) ProgressBar loading;
@BindView(R.id.player_shots) RecyclerView shots;
@BindInt(R.integer.num_columns) int columns;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dribbble_player);
ButterKnife.bind(this);
circleTransform = new CircleTransform(this);
chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(this);
final Intent intent = getIntent();
if (intent.hasExtra(EXTRA_PLAYER)) {
player = intent.getParcelableExtra(EXTRA_PLAYER);
bindPlayer();
} else if (intent.hasExtra(EXTRA_PLAYER_NAME)) {
String name = intent.getStringExtra(EXTRA_PLAYER_NAME);
playerName.setText(name);
if (intent.hasExtra(EXTRA_PLAYER_ID)) {
long userId = intent.getLongExtra(EXTRA_PLAYER_ID, 0L);
loadPlayer(userId);
} else if (intent.hasExtra(EXTRA_PLAYER_USERNAME)) {
String username = intent.getStringExtra(EXTRA_PLAYER_USERNAME);
loadPlayer(username);
}
} else if (intent.getData() != null) {
// todo support url intents
}
// setup immersive mode i.e. draw behind the system chrome & adjust insets
draggableFrame.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
draggableFrame.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
final ViewGroup.MarginLayoutParams lpFrame = (ViewGroup.MarginLayoutParams)
draggableFrame.getLayoutParams();
lpFrame.leftMargin += insets.getSystemWindowInsetLeft(); // landscape
lpFrame.rightMargin += insets.getSystemWindowInsetRight(); // landscape
((ViewGroup.MarginLayoutParams) avatar.getLayoutParams()).topMargin
+= insets.getSystemWindowInsetTop();
ViewUtils.setPaddingTop(container, insets.getSystemWindowInsetTop());
ViewUtils.setPaddingBottom(shots, insets.getSystemWindowInsetBottom());
// clear this listener so insets aren't re-applied
draggableFrame.setOnApplyWindowInsetsListener(null);
return insets;
}
});
setExitSharedElementCallback(FeedAdapter.createSharedElementReenterCallback(this));
}
@Override
protected void onResume() {
super.onResume();
draggableFrame.addListener(chromeFader);
}
@Override
protected void onPause() {
draggableFrame.removeListener(chromeFader);
super.onPause();
}
@Override
protected void onDestroy() {
if (dataManager != null) {
dataManager.cancelLoading();
}
super.onDestroy();
}
@Override
public void onActivityReenter(int resultCode, Intent data) {
if (data == null || resultCode != RESULT_OK
|| !data.hasExtra(DribbbleShot.RESULT_EXTRA_SHOT_ID)) return;
// When reentering, if the shared element is no longer on screen (e.g. after an
// orientation change) then scroll it into view.
final long sharedShotId = data.getLongExtra(DribbbleShot.RESULT_EXTRA_SHOT_ID, -1L);
if (sharedShotId != -1L // returning from a shot
&& adapter.getDataItemCount() > 0 // grid populated
&& shots.findViewHolderForItemId(sharedShotId) == null) { // view not attached
final int position = adapter.getItemPosition(sharedShotId);
if (position == RecyclerView.NO_POSITION) return;
// delay the transition until our shared element is on-screen i.e. has been laid out
postponeEnterTransition();
shots.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int l, int t, int r, int b,
int oL, int oT, int oR, int oB) {
shots.removeOnLayoutChangeListener(this);
startPostponedEnterTransition();
}
});
shots.scrollToPosition(position);
}
}
void bindPlayer() {
if (player == null) return;
final Resources res = getResources();
final NumberFormat nf = NumberFormat.getInstance();
Glide.with(this)
.load(player.getHighQualityAvatarUrl())
.placeholder(R.drawable.avatar_placeholder)
.transform(circleTransform)
.into(avatar);
playerName.setText(player.name.toLowerCase());
if (!TextUtils.isEmpty(player.bio)) {
DribbbleUtils.parseAndSetText(bio, player.bio);
} else {
bio.setVisibility(View.GONE);
}
shotCount.setText(res.getQuantityString(R.plurals.shots, player.shots_count,
nf.format(player.shots_count)));
if (player.shots_count == 0) {
shotCount.setCompoundDrawablesRelativeWithIntrinsicBounds(
null, getDrawable(R.drawable.avd_no_shots), null, null);
}
setFollowerCount(player.followers_count);
likesCount.setText(res.getQuantityString(R.plurals.likes, player.likes_count,
nf.format(player.likes_count)));
// load the users shots
dataManager = new PlayerShotsDataManager(this, player) {
@Override
public void onDataLoaded(List<Shot> data) {
if (data != null && data.size() > 0) {
if (adapter.getDataItemCount() == 0) {
loading.setVisibility(View.GONE);
ViewUtils.setPaddingTop(shots, likesCount.getBottom());
}
adapter.addAndResort(data);
}
}
};
adapter = new FeedAdapter(this, dataManager, columns, PocketUtils.isPocketInstalled(this));
shots.setAdapter(adapter);
shots.setItemAnimator(new SlideInItemAnimator());
shots.setVisibility(View.VISIBLE);
layoutManager = new GridLayoutManager(this, columns);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return adapter.getItemColumnSpan(position);
}
});
shots.setLayoutManager(layoutManager);
shots.addOnScrollListener(new InfiniteScrollListener(layoutManager, dataManager) {
@Override
public void onLoadMore() {
dataManager.loadData();
}
});
shots.setHasFixedSize(true);
// forward on any clicks above the first item in the grid (i.e. in the paddingTop)
// to 'pass through' to the view behind
shots.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int firstVisible = layoutManager.findFirstVisibleItemPosition();
if (firstVisible > 0) return false;
// if no data loaded then pass through
if (adapter.getDataItemCount() == 0) {
return container.dispatchTouchEvent(event);
}
final RecyclerView.ViewHolder vh = shots.findViewHolderForAdapterPosition(0);
if (vh == null) return false;
final int firstTop = vh.itemView.getTop();
if (event.getY() < firstTop) {
return container.dispatchTouchEvent(event);
}
return false;
}
});
// check if following
if (dataManager.getDribbblePrefs().isLoggedIn()) {
if (player.id == dataManager.getDribbblePrefs().getUserId()) {
TransitionManager.beginDelayedTransition(container);
follow.setVisibility(View.GONE);
ViewUtils.setPaddingTop(shots, container.getHeight() - follow.getHeight()
- ((ViewGroup.MarginLayoutParams) follow.getLayoutParams()).bottomMargin);
} else {
final Call<Void> followingCall = dataManager.getDribbbleApi().following(player.id);
followingCall.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
following = response.isSuccessful();
if (!following) return;
TransitionManager.beginDelayedTransition(container);
follow.setText(R.string.following);
follow.setActivated(true);
}
@Override public void onFailure(Call<Void> call, Throwable t) { }
});
}
}
if (player.shots_count > 0) {
dataManager.loadData(); // kick off initial load
} else {
loading.setVisibility(View.GONE);
}
}
@OnClick(R.id.follow)
void follow() {
if (DribbblePrefs.get(this).isLoggedIn()) {
if (following != null && following) {
final Call<Void> unfollowCall = dataManager.getDribbbleApi().unfollow(player.id);
unfollowCall.enqueue(new Callback<Void>() {
@Override public void onResponse(Call<Void> call, Response<Void> response) { }
@Override public void onFailure(Call<Void> call, Throwable t) { }
});
following = false;
TransitionManager.beginDelayedTransition(container);
follow.setText(R.string.follow);
follow.setActivated(false);
setFollowerCount(followerCount - 1);
} else {
final Call<Void> followCall = dataManager.getDribbbleApi().follow(player.id);
followCall.enqueue(new Callback<Void>() {
@Override public void onResponse(Call<Void> call, Response<Void> response) { }
@Override public void onFailure(Call<Void> call, Throwable t) { }
});
following = true;
TransitionManager.beginDelayedTransition(container);
follow.setText(R.string.following);
follow.setActivated(true);
setFollowerCount(followerCount + 1);
}
} else {
Intent login = new Intent(this, DribbbleLogin.class);
MorphTransform.addExtras(login,
ContextCompat.getColor(this, R.color.dribbble),
getResources().getDimensionPixelSize(R.dimen.dialog_corners));
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation
(this, follow, getString(R.string.transition_dribbble_login));
startActivity(login, options.toBundle());
}
}
@OnClick({R.id.shot_count, R.id.followers_count, R.id.likes_count})
void playerActionClick(TextView view) {
((AnimatedVectorDrawable) view.getCompoundDrawables()[1]).start();
switch (view.getId()) {
case R.id.followers_count:
PlayerSheet.start(PlayerActivity.this, player);
break;
}
}
private void loadPlayer(long userId) {
final Call<User> userCall = DribbblePrefs.get(this).getApi().getUser(userId);
userCall.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
player = response.body();
bindPlayer();
}
@Override public void onFailure(Call<User> call, Throwable t) { }
});
}
private void loadPlayer(String username) {
final Call<User> userCall = DribbblePrefs.get(this).getApi().getUser(username);
userCall.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
player = response.body();
bindPlayer();
}
@Override public void onFailure(Call<User> call, Throwable t) { }
});
}
private void setFollowerCount(int count) {
followerCount = count;
followersCount.setText(getResources().getQuantityString(R.plurals.follower_count,
followerCount, NumberFormat.getInstance().format(followerCount)));
if (followerCount == 0) {
followersCount.setBackground(null);
}
}
}