package com.wangdaye.mysplash.photo.view.activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import com.wangdaye.mysplash.R;
import com.wangdaye.mysplash.common._basic.ReadWriteActivity;
import com.wangdaye.mysplash.common.data.entity.item.DownloadMission;
import com.wangdaye.mysplash.common.data.entity.table.DownloadMissionEntity;
import com.wangdaye.mysplash.common.data.entity.unsplash.Photo;
import com.wangdaye.mysplash.common.i.presenter.MessageManagePresenter;
import com.wangdaye.mysplash.common.i.view.MessageManageView;
import com.wangdaye.mysplash.common.ui.adapter.PhotoInfoAdapter;
import com.wangdaye.mysplash.common.ui.dialog.DownloadRepeatDialog;
import com.wangdaye.mysplash.common.ui.widget.PhotoDownloadView;
import com.wangdaye.mysplash.common.ui.widget.coordinatorView.StatusBarView;
import com.wangdaye.mysplash.common.ui.widget.SwipeBackCoordinatorLayout;
import com.wangdaye.mysplash.common.utils.FileUtils;
import com.wangdaye.mysplash.common.utils.helper.NotificationHelper;
import com.wangdaye.mysplash.common.utils.helper.DatabaseHelper;
import com.wangdaye.mysplash.common.utils.helper.DownloadHelper;
import com.wangdaye.mysplash.common.i.model.BrowsableModel;
import com.wangdaye.mysplash.common.i.model.DownloadModel;
import com.wangdaye.mysplash.common.i.model.PhotoInfoModel;
import com.wangdaye.mysplash.common.i.presenter.BrowsablePresenter;
import com.wangdaye.mysplash.common.i.presenter.DownloadPresenter;
import com.wangdaye.mysplash.common.i.presenter.PhotoInfoPresenter;
import com.wangdaye.mysplash.common.i.presenter.PopupManagePresenter;
import com.wangdaye.mysplash.common.i.view.BrowsableView;
import com.wangdaye.mysplash.common.i.view.PhotoInfoView;
import com.wangdaye.mysplash.common.i.view.PopupManageView;
import com.wangdaye.mysplash.common.ui.dialog.RequestBrowsableDataDialog;
import com.wangdaye.mysplash.common.ui.dialog.StatsDialog;
import com.wangdaye.mysplash.common.ui.popup.PhotoMenuPopupWindow;
import com.wangdaye.mysplash.common.utils.AnimUtils;
import com.wangdaye.mysplash.common.utils.DisplayUtils;
import com.wangdaye.mysplash.common.ui.widget.freedomSizeView.FreedomImageView;
import com.wangdaye.mysplash.common.utils.helper.ImageHelper;
import com.wangdaye.mysplash.common.utils.helper.IntentHelper;
import com.wangdaye.mysplash.common.utils.manager.ThemeManager;
import com.wangdaye.mysplash.common.utils.manager.ThreadManager;
import com.wangdaye.mysplash.common._basic.FlagRunnable;
import com.wangdaye.mysplash.common.utils.widget.SafeHandler;
import com.wangdaye.mysplash.photo.model.BorwsableObject;
import com.wangdaye.mysplash.photo.model.DownloadObject;
import com.wangdaye.mysplash.photo.model.PhotoInfoObject;
import com.wangdaye.mysplash.photo.presenter.BrowsableImplementor;
import com.wangdaye.mysplash.photo.presenter.DownloadImplementor;
import com.wangdaye.mysplash.photo.presenter.MessageManageImplementor;
import com.wangdaye.mysplash.photo.presenter.PhotoActivityPopupManageImplementor;
import com.wangdaye.mysplash.photo.presenter.PhotoInfoImplementor;
import com.wangdaye.mysplash.photo.view.holder.BaseHolder;
import com.wangdaye.mysplash.photo.view.holder.BaseLandscapeHolder;
import com.wangdaye.mysplash.photo.view.holder.MoreHolder;
import com.wangdaye.mysplash.photo.view.holder.ProgressHolder;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Photo activity.
*
* This activity is used to show details of a photo.
*
* */
public class PhotoActivity extends ReadWriteActivity
implements PhotoInfoView, PopupManageView, BrowsableView, MessageManageView,
DownloadRepeatDialog.OnCheckOrDownloadListener, SwipeBackCoordinatorLayout.OnSwipeListener,
SafeHandler.HandlerContainer {
@BindView(R.id.activity_photo_container)
CoordinatorLayout container;
@BindView(R.id.activity_photo_image)
FreedomImageView photoImage;
@BindView(R.id.activity_photo_recyclerView)
RecyclerView recyclerView;
@BindView(R.id.activity_photo_translucentStatusBar)
StatusBarView translucentStatusBar;
@BindView(R.id.activity_photo_statusBar)
StatusBarView statusBar;
private RequestBrowsableDataDialog requestDialog;
private SafeHandler<PhotoActivity> handler;
private PhotoInfoModel photoInfoModel;
private PhotoInfoPresenter photoInfoPresenter;
private DownloadModel downloadModel;
private DownloadPresenter downloadPresenter;
private PopupManagePresenter popupManagePresenter;
private BrowsableModel browsableModel;
private BrowsablePresenter browsablePresenter;
private MessageManagePresenter messageManagePresenter;
public static final String KEY_PHOTO_ACTIVITY_PHOTO = "photo_activity_photo";
public static final String KEY_PHOTO_ACTIVITY_ID = "photo_activity_id";
/**
* This runnable is used to poll download progress.
* */
private FlagRunnable progressRunnable = new FlagRunnable(false) {
@Override
public void run() {
while (isRunning()) {
DownloadMissionEntity entity = DatabaseHelper.getInstance(PhotoActivity.this)
.readDownloadingEntity(photoInfoPresenter.getPhoto().id);
if (entity != null && entity.missionId != -1
&& entity.result == DownloadHelper.RESULT_DOWNLOADING) {
DownloadMission mission = DownloadHelper.getInstance(PhotoActivity.this)
.getDownloadMission(PhotoActivity.this, entity.missionId);
if (mission == null || mission.entity.result != DownloadHelper.RESULT_DOWNLOADING) {
messageManagePresenter.sendMessage(-1, null);
} else {
messageManagePresenter.sendMessage((int) mission.process, null);
}
} else {
messageManagePresenter.sendMessage(-1, null);
}
SystemClock.sleep(200);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DisplayUtils.setStatusBarStyle(this, true);
setContentView(R.layout.activity_photo);
initModel();
initPresenter();
}
@Override
protected void onStart() {
super.onStart();
if (!isStarted()) {
setStarted();
ButterKnife.bind(this);
initView(true);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
browsablePresenter.cancelRequest();
photoInfoPresenter.cancelRequest();
progressRunnable.setRunning(false);
}
@Override
protected void setTheme() {
if (ThemeManager.getInstance(this).isLightTheme()) {
setTheme(R.style.MysplashTheme_light_Translucent_Photo);
} else {
setTheme(R.style.MysplashTheme_dark_Translucent_Photo);
}
}
@Override
protected boolean operateStatusBarBySelf() {
return true;
}
@Override
public void onSaveInstanceState(Bundle outState) {
// do nothing.
}
@Override
public void handleBackPressed() {
finishActivity(SwipeBackCoordinatorLayout.DOWN_DIR);
}
@Override
protected void backToTop() {
// do nothing.
}
@Override
public void finishActivity(int dir) {
Intent result = new Intent();
result.putExtra(
KEY_PHOTO_ACTIVITY_PHOTO,
getIntent().getParcelableExtra(KEY_PHOTO_ACTIVITY_PHOTO));
setResult(RESULT_OK, result);
recyclerView.setAlpha(0f);
SwipeBackCoordinatorLayout.hideBackgroundShadow(container);
if (!browsablePresenter.isBrowsable()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAfterTransition();
} else {
finish();
switch (dir) {
case SwipeBackCoordinatorLayout.UP_DIR:
overridePendingTransition(0, R.anim.activity_slide_out_top);
break;
case SwipeBackCoordinatorLayout.DOWN_DIR:
overridePendingTransition(0, R.anim.activity_slide_out_bottom);
break;
}
}
}
@Override
public CoordinatorLayout getSnackbarContainer() {
return container;
}
// init.
private void initModel() {
this.photoInfoModel = new PhotoInfoObject(
this, (Photo) getIntent().getParcelableExtra(KEY_PHOTO_ACTIVITY_PHOTO));
this.downloadModel = new DownloadObject(photoInfoModel.getPhoto());
this.browsableModel = new BorwsableObject(getIntent());
}
private void initPresenter() {
this.photoInfoPresenter = new PhotoInfoImplementor(photoInfoModel, this);
this.downloadPresenter = new DownloadImplementor(downloadModel);
this.popupManagePresenter = new PhotoActivityPopupManageImplementor(this);
this.browsablePresenter = new BrowsableImplementor(browsableModel, this);
this.messageManagePresenter = new MessageManageImplementor(this);
}
@SuppressLint({"SetTextI18n", "CutPasteId"})
private void initView(boolean init) {
this.handler = new SafeHandler<>(this);
if (init && browsablePresenter.isBrowsable() && photoInfoPresenter.getPhoto() == null) {
browsablePresenter.requestBrowsableData();
} else {
SwipeBackCoordinatorLayout swipeBackView = ButterKnife.findById(
this, R.id.activity_photo_swipeBackView);
swipeBackView.setOnSwipeListener(this);
photoImage.setSize(
photoInfoPresenter.getPhoto().width, photoInfoPresenter.getPhoto().height);
ImageHelper.loadRegularPhoto(
this, photoImage, photoInfoPresenter.getPhoto(),
new ImageHelper.OnLoadImageListener() {
@Override
public void onLoadSucceed() {
photoInfoPresenter.getPhoto().loadPhotoSuccess = true;
if (!photoInfoPresenter.getPhoto().hasFadedIn) {
photoInfoPresenter.getPhoto().hasFadedIn = true;
ImageHelper.startSaturationAnimation(PhotoActivity.this, photoImage);
}
}
@Override
public void onLoadFailed() {
// do nothing.
}
});
recyclerView.setAdapter(photoInfoPresenter.getAdapter());
int columnCount;
if (DisplayUtils.isLandscape(this)) {
columnCount = 2;
} else {
columnCount = 1;
}
GridLayoutManager layoutManager = new GridLayoutManager(this, columnCount);
layoutManager.setSpanSizeLookup(
new PhotoInfoAdapter.SpanSizeLookup(
photoInfoPresenter.getAdapter(), columnCount));
recyclerView.setLayoutManager(layoutManager);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
recyclerView.addOnScrollListener(new OnScrollListener((photoInfoPresenter.getPhoto())));
} else {
recyclerView.addOnScrollListener(new MScrollListener((photoInfoPresenter.getPhoto())));
}
statusBar.setAlpha(0f);
if (!photoInfoPresenter.getPhoto().complete) {
initRefresh();
}
}
}
// control.
public void visitParentActivity() {
browsablePresenter.visitPreviousPage();
}
public boolean isBrowsable() {
return browsablePresenter.isBrowsable();
}
public Photo getPhoto() {
return photoInfoPresenter.getPhoto();
}
// HTTP request.
public void initRefresh() {
photoInfoPresenter.requestPhoto(this);
}
public boolean isLoadFailed() {
return photoInfoPresenter.isFailed();
}
// UI.
@Nullable
private PhotoDownloadView getPhotoDownloadView() {
GridLayoutManager manager = (GridLayoutManager) recyclerView.getLayoutManager();
int firstVisibleItemPosition = manager.findFirstVisibleItemPosition();
int lastVisibleItemPosition = manager.findLastVisibleItemPosition();
if (firstVisibleItemPosition <= 1 && 1 <= lastVisibleItemPosition) {
PhotoInfoAdapter.ViewHolder holder
= (PhotoInfoAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(1);
if (holder instanceof BaseHolder) {
return ((BaseHolder) holder).getDownloadView();
} else if (holder instanceof BaseLandscapeHolder) {
return ((BaseLandscapeHolder) holder).getDownloadView();
} else {
return null;
}
} else {
return null;
}
}
@Nullable
private ViewPager getMoreImageContainer() {
if (!photoInfoPresenter.getAdapter().isComplete()) {
return null;
} else {
int lastVisibleItemPosition = ((GridLayoutManager) recyclerView.getLayoutManager())
.findLastVisibleItemPosition();
if (lastVisibleItemPosition == photoInfoPresenter.getAdapter().getItemCount() - 1) {
RecyclerView.ViewHolder holder
= recyclerView.findViewHolderForAdapterPosition(lastVisibleItemPosition);
if (holder instanceof MoreHolder) {
return ((MoreHolder) holder).getViewPager();
} else {
return null;
}
} else {
return null;
}
}
}
public void showPopup(Context c, View anchor, String value, int position) {
popupManagePresenter.showPopup(c, anchor, value, position);
}
// download.
public void readyToDownload(int type) {
if (DatabaseHelper.getInstance(this)
.readDownloadingEntityCount(photoInfoPresenter.getPhoto().id) > 0) {
NotificationHelper.showSnackbar(
getString(R.string.feedback_download_repeat),
Snackbar.LENGTH_SHORT);
} else if (FileUtils.isPhotoExists(this, photoInfoPresenter.getPhoto().id)) {
DownloadRepeatDialog dialog = new DownloadRepeatDialog();
dialog.setDownloadKey(type);
dialog.setOnCheckOrDownloadListener(this);
dialog.show(getFragmentManager(), null);
} else {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
downloadByType(type);
} else {
requestReadWritePermission(type);
}
}
}
public void downloadByType(int type) {
switch (type) {
case DownloadHelper.DOWNLOAD_TYPE:
downloadPresenter.download(this);
break;
case DownloadHelper.SHARE_TYPE:
downloadPresenter.share(this);
break;
case DownloadHelper.WALLPAPER_TYPE:
downloadPresenter.setWallpaper(this);
break;
}
PhotoDownloadView downloadView = getPhotoDownloadView();
if (downloadView != null) {
downloadView.setProgressState();
}
startCheckDownloadProgressThread();
}
public void startCheckDownloadProgressThread() {
if (!progressRunnable.isRunning()) {
progressRunnable.setRunning(true);
ThreadManager.getInstance().execute(progressRunnable);
}
}
// permission.
@Override
protected void requestReadWritePermissionSucceed(int requestCode) {
downloadByType(requestCode);
}
// interface.
// on check or download listener.
@Override
public void onCheck(Object obj) {
IntentHelper.startCheckPhotoActivity(
this,
((Photo) downloadPresenter.getDownloadKey()).id);
}
@Override
public void onDownload(Object obj) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
downloadByType((Integer) obj);
} else {
requestReadWritePermission((Integer) obj);
}
}
// on scroll changed listener.
/**
* This listener is used to set footer image position and control the style of status bars.
* */
private class OnScrollListener extends RecyclerView.OnScrollListener {
int scrollY;
float statusBarHeight;
float screenHeight;
float footerHeight;
float showFlowStatusBarTrigger;
// life cycle.
OnScrollListener(Photo photo) {
statusBarHeight = DisplayUtils.getStatusBarHeight(getResources());
screenHeight = getResources().getDisplayMetrics().heightPixels;
footerHeight = getResources().getDimensionPixelSize(R.dimen.item_photo_more_vertical_height);
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
float limitHeight = screenHeight
- getResources().getDimensionPixelSize(R.dimen.photo_info_base_view_height);
if (DisplayUtils.isLandscape(PhotoActivity.this)) {
showFlowStatusBarTrigger = getResources().getDisplayMetrics().heightPixels - statusBarHeight;
} else {
if (1.0 * photo.height / photo.width * screenWidth <= limitHeight) {
showFlowStatusBarTrigger = limitHeight - statusBarHeight;
} else {
showFlowStatusBarTrigger = screenWidth * photo.height / photo.width - statusBarHeight;
}
}
}
// interface.
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
scrollY += dy;
// image.
if (scrollY + statusBarHeight < photoImage.getMeasuredHeight()) {
photoImage.setTranslationY((float) (-scrollY * 0.5));
}
// status bar & toolbar.
if (scrollY - dy < showFlowStatusBarTrigger && scrollY >= showFlowStatusBarTrigger) {
translucentStatusBar.animToDarkerAlpha();
AnimUtils.animShow(statusBar, 150, statusBar.getAlpha(), 1);
} else if (scrollY - dy >= showFlowStatusBarTrigger && scrollY < showFlowStatusBarTrigger) {
translucentStatusBar.animToInitAlpha();
AnimUtils.animHide(statusBar, 150, statusBar.getAlpha(), 0, false);
}
// more.
ViewPager moreContainer = getMoreImageContainer();
if (moreContainer != null) {
moreContainer.setTranslationY(
(float) (0.5 * (recyclerView.getBottom()
- ((View) moreContainer.getParent()).getTop()
- footerHeight)));
}
}
}
/**
* This listener is used to control the text color for status bar. Only for Android M.
* */
@RequiresApi(api = Build.VERSION_CODES.M)
private class MScrollListener extends OnScrollListener {
MScrollListener(Photo photo) {
super(photo);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (scrollY - dy < showFlowStatusBarTrigger && scrollY >= showFlowStatusBarTrigger) {
DisplayUtils.setStatusBarStyle(PhotoActivity.this, false);
} else if (scrollY - dy >= showFlowStatusBarTrigger && scrollY < showFlowStatusBarTrigger) {
DisplayUtils.setStatusBarStyle(PhotoActivity.this, true);
}
}
}
// on swipe listener.
@Override
public boolean canSwipeBack(int dir) {
return SwipeBackCoordinatorLayout.canSwipeBack(recyclerView, dir);
}
@Override
public void onSwipeProcess(float percent) {
container.setBackgroundColor(SwipeBackCoordinatorLayout.getBackgroundColor(percent));
}
@Override
public void onSwipeFinish(int dir) {
finishActivity(dir);
}
// handler.
@Override
public void handleMessage(Message message) {
messageManagePresenter.responseMessage(message.what, message.obj);
}
// view.
// photo info view.
@Override
public void touchMenuItem(int itemId) {
switch (itemId) {
case PhotoMenuPopupWindow.ITEM_STATS:
StatsDialog dialog = new StatsDialog();
dialog.setPhoto(photoInfoPresenter.getPhoto());
dialog.show(getFragmentManager(), null);
break;
case PhotoMenuPopupWindow.ITEM_DOWNLOAD_PAGE:
IntentHelper.startWebActivity(this, photoInfoPresenter.getPhoto().links.download);
break;
case PhotoMenuPopupWindow.ITEM_STORY_PAGE:
if (photoInfoPresenter.getPhoto() != null
&& photoInfoPresenter.getPhoto().story != null
&& !TextUtils.isEmpty(photoInfoPresenter.getPhoto().story.image_url)) {
IntentHelper.startWebActivity(this, photoInfoPresenter.getPhoto().story.image_url);
} else {
NotificationHelper.showSnackbar(
getString(R.string.feedback_story_is_null),
Snackbar.LENGTH_SHORT);
}
break;
}
}
@Override
public void requestPhotoSuccess(Photo photo) {
getIntent().putExtra(KEY_PHOTO_ACTIVITY_PHOTO, photo);
int oldCount = photoInfoPresenter.getAdapter().getItemCount() - 1;
photoInfoPresenter.getAdapter().notifyItemRemoved(oldCount);
photoInfoPresenter.getAdapter().updatePhoto(photo);
photoInfoPresenter.getAdapter().notifyItemRangeInserted(
oldCount, photoInfoPresenter.getAdapter().getItemCount());
}
@Override
public void requestPhotoFailed() {
if (((GridLayoutManager) recyclerView.getLayoutManager())
.findLastVisibleItemPosition() == 2) {
ProgressHolder holder = (ProgressHolder) recyclerView.findViewHolderForAdapterPosition(2);
holder.setFailedState();
}
}
// popup manage view.
@Override
public void responsePopup(String value, int position) {
photoInfoPresenter.touchMenuItem(position);
}
// browsable view.
@Override
public void showRequestDialog() {
requestDialog = new RequestBrowsableDataDialog();
requestDialog.show(getFragmentManager(), null);
}
@Override
public void dismissRequestDialog() {
requestDialog.dismiss();
requestDialog = null;
}
@Override
public void drawBrowsableView(Object result) {
getIntent().putExtra(KEY_PHOTO_ACTIVITY_PHOTO, (Photo) result);
initModel();
initPresenter();
initView(false);
}
@Override
public void visitPreviousPage() {
IntentHelper.startMainActivity(this);
}
// message manage view.
@Override
public void sendMessage(int what, Object o) {
handler.obtainMessage(what, o).sendToTarget();
}
@Override
public void responseMessage(int what, Object o) {
PhotoDownloadView downloadView = getPhotoDownloadView();
if (downloadView != null) {
if (0 <= what && what <= 100) {
downloadView.setProcess(what);
} else {
progressRunnable.setRunning(false);
downloadView.setButtonState();
}
}
}
}