/*
* Copyright (c) 2015 Ha Duy Trung
*
* 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.github.hidroh.materialistic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.text.AllCapsTransformationMethod;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import javax.inject.Inject;
import javax.inject.Named;
import io.github.hidroh.materialistic.accounts.UserServices;
import io.github.hidroh.materialistic.annotation.Synthetic;
import io.github.hidroh.materialistic.data.FavoriteManager;
import io.github.hidroh.materialistic.data.Item;
import io.github.hidroh.materialistic.data.ItemManager;
import io.github.hidroh.materialistic.data.MaterialisticProvider;
import io.github.hidroh.materialistic.data.ResponseListener;
import io.github.hidroh.materialistic.data.SessionManager;
import io.github.hidroh.materialistic.data.WebItem;
import io.github.hidroh.materialistic.widget.ItemPagerAdapter;
import io.github.hidroh.materialistic.widget.NavFloatingActionButton;
import io.github.hidroh.materialistic.widget.PopupMenu;
import io.github.hidroh.materialistic.widget.ViewPager;
public class ItemActivity extends InjectableActivity implements ItemFragment.ItemChangedListener {
public static final String EXTRA_ITEM = ItemActivity.class.getName() + ".EXTRA_ITEM";
public static final String EXTRA_CACHE_MODE = ItemActivity.class.getName() + ".EXTRA_CACHE_MODE";
public static final String EXTRA_OPEN_COMMENTS = ItemActivity.class.getName() + ".EXTRA_OPEN_COMMENTS";
private static final String PARAM_ID = "id";
private static final String STATE_ITEM = "state:item";
private static final String STATE_ITEM_ID = "state:itemId";
private static final String STATE_FULLSCREEN = "state:fullscreen";
@Synthetic WebItem mItem;
@Synthetic String mItemId = null;
@Synthetic ImageView mBookmark;
private boolean mExternalBrowser;
private Preferences.StoryViewMode mStoryViewMode;
@Inject @Named(ActivityModule.HN) ItemManager mItemManager;
@Inject FavoriteManager mFavoriteManager;
@Inject AlertDialogBuilder mAlertDialogBuilder;
@Inject PopupMenu mPopupMenu;
@Inject UserServices mUserServices;
@Inject SessionManager mSessionManager;
@Inject CustomTabsDelegate mCustomTabsDelegate;
@Inject KeyDelegate mKeyDelegate;
private TabLayout mTabLayout;
@Synthetic AppBarLayout mAppBar;
@Synthetic CoordinatorLayout mCoordinatorLayout;
private ImageButton mVoteButton;
private FloatingActionButton mReplyButton;
private NavFloatingActionButton mNavButton;
private ItemPagerAdapter mAdapter;
private ViewPager mViewPager;
@Synthetic boolean mFullscreen;
private final ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (mItem == null) {
return;
}
if (FavoriteManager.isCleared(uri)) {
mItem.setFavorite(false);
bindFavorite();
} else if (TextUtils.equals(mItemId, uri.getLastPathSegment())) {
mItem.setFavorite(FavoriteManager.isAdded(uri));
bindFavorite();
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mFullscreen = intent.getBooleanExtra(WebFragment.EXTRA_FULLSCREEN, false);
setFullscreen();
}
};
private final Preferences.Observable mPreferenceObservable = new Preferences.Observable();
private AppUtils.SystemUiHelper mSystemUiHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mExternalBrowser = Preferences.externalBrowserEnabled(this);
if (getIntent().getBooleanExtra(EXTRA_OPEN_COMMENTS, false)) {
mStoryViewMode = Preferences.StoryViewMode.Comment;
} else {
mStoryViewMode = Preferences.getDefaultStoryView(this);
}
setContentView(R.layout.activity_item);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
//noinspection ConstantConditions
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME |
ActionBar.DISPLAY_HOME_AS_UP);
mSystemUiHelper = new AppUtils.SystemUiHelper(getWindow());
mReplyButton = (FloatingActionButton) findViewById(R.id.reply_button);
mNavButton = (NavFloatingActionButton) findViewById(R.id.navigation_button);
mNavButton.setNavigable(direction ->
// if callback is fired navigable should not be null
AppUtils.navigate(direction, mAppBar, (Navigable) mAdapter.getItem(0)));
mVoteButton = (ImageButton) findViewById(R.id.vote_button);
mBookmark = (ImageView) findViewById(R.id.bookmarked);
mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.content_frame);
mAppBar = (AppBarLayout) findViewById(R.id.appbar);
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
AppUtils.toggleFab(mNavButton, false);
AppUtils.toggleFab(mReplyButton, false);
final Intent intent = getIntent();
getContentResolver().registerContentObserver(MaterialisticProvider.URI_FAVORITE,
true, mObserver);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
new IntentFilter(WebFragment.ACTION_FULLSCREEN));
mPreferenceObservable.subscribe(this, this::onPreferenceChanged,
R.string.pref_navigation);
if (savedInstanceState != null) {
mItem = savedInstanceState.getParcelable(STATE_ITEM);
mItemId = savedInstanceState.getString(STATE_ITEM_ID);
mFullscreen = savedInstanceState.getBoolean(STATE_FULLSCREEN);
} else {
if (Intent.ACTION_VIEW.equalsIgnoreCase(intent.getAction())) {
mItemId = AppUtils.getDataUriId(intent, PARAM_ID);
} else if (intent.hasExtra(EXTRA_ITEM)) {
mItem = intent.getParcelableExtra(EXTRA_ITEM);
mItemId = mItem.getId();
}
}
if (mItem != null) {
bindData(mItem);
} else if (!TextUtils.isEmpty(mItemId)) {
mItemManager.getItem(mItemId,
getIntent().getIntExtra(EXTRA_CACHE_MODE, ItemManager.MODE_DEFAULT),
new ItemResponseListener(this));
}
if (!AppUtils.hasConnection(this)) {
Snackbar.make(mCoordinatorLayout, R.string.offline_notice, Snackbar.LENGTH_LONG).show();
}
}
@Override
protected void onStart() {
super.onStart();
mCustomTabsDelegate.bindCustomTabsService(this);
mKeyDelegate.attach(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_item, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.menu_share).setVisible(mItem != null);
return mItem != null;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
if (item.getItemId() == R.id.menu_external) {
View anchor = findViewById(R.id.menu_external);
AppUtils.openExternal(this, mPopupMenu, anchor == null ?
findViewById(R.id.toolbar) : anchor, mItem,
mCustomTabsDelegate.getSession());
return true;
}
if (item.getItemId() == R.id.menu_share) {
View anchor = findViewById(R.id.menu_share);
AppUtils.share(this, mPopupMenu, anchor == null ?
findViewById(R.id.toolbar) : anchor, mItem);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(STATE_ITEM, mItem);
outState.putString(STATE_ITEM_ID, mItemId);
outState.putBoolean(STATE_FULLSCREEN, mFullscreen);
}
@Override
protected void onStop() {
super.onStop();
mCustomTabsDelegate.unbindCustomTabsService(this);
mKeyDelegate.detach(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
mPreferenceObservable.unsubscribe(this);
}
@Override
public void onBackPressed() {
if (!mFullscreen) {
super.onBackPressed();
} else {
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(
WebFragment.ACTION_FULLSCREEN).putExtra(WebFragment.EXTRA_FULLSCREEN, false));
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mKeyDelegate.setScrollable(getCurrent(Scrollable.class), mAppBar);
mKeyDelegate.setBackInterceptor(getCurrent(KeyDelegate.BackInterceptor.class));
return mKeyDelegate.onKeyDown(keyCode, event) ||
super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return mKeyDelegate.onKeyUp(keyCode, event) ||
super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return mKeyDelegate.onKeyLongPress(keyCode, event) ||
super.onKeyLongPress(keyCode, event);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mSystemUiHelper.setFullscreen(hasFocus && mFullscreen);
}
@Override
public void onItemChanged(@NonNull Item item) {
mItem = item;
if (mTabLayout.getTabCount() > 0) {
//noinspection ConstantConditions
mTabLayout.getTabAt(0).setText(getResources()
.getQuantityString(R.plurals.comments_count, item.getKidCount(), item.getKidCount()));
}
}
@Synthetic
void setFullscreen() {
mSystemUiHelper.setFullscreen(mFullscreen);
mAppBar.setExpanded(!mFullscreen, true);
mKeyDelegate.setAppBarEnabled(!mFullscreen);
mViewPager.setSwipeEnabled(!mFullscreen);
AppUtils.toggleFab(mReplyButton, !mFullscreen);
}
@Synthetic
void onItemLoaded(@Nullable Item response) {
mItem = response;
supportInvalidateOptionsMenu();
bindData(mItem);
}
@Synthetic
void bindFavorite() {
if (mItem == null) {
return;
}
if (!mItem.isStoryType()) {
return;
}
mBookmark.setVisibility(View.VISIBLE);
decorateFavorite(mItem.isFavorite());
mBookmark.setOnClickListener(new View.OnClickListener() {
private boolean mUndo;
@Override
public void onClick(View v) {
final int toastMessageResId;
if (!mItem.isFavorite()) {
mFavoriteManager.add(ItemActivity.this, mItem);
toastMessageResId = R.string.toast_saved;
} else {
mFavoriteManager.remove(ItemActivity.this, mItem.getId());
toastMessageResId = R.string.toast_removed;
}
if (!mUndo) {
Snackbar.make(mCoordinatorLayout, toastMessageResId, Snackbar.LENGTH_SHORT)
.setAction(R.string.undo, v1 -> {
mUndo = true;
mBookmark.performClick();
})
.show();
}
mUndo = false;
}
});
}
@SuppressWarnings("ConstantConditions")
private void bindData(@Nullable final WebItem story) {
if (story == null) {
return;
}
mCustomTabsDelegate.mayLaunchUrl(Uri.parse(story.getUrl()), null, null);
bindFavorite();
mSessionManager.view(this, story.getId());
mVoteButton.setVisibility(View.VISIBLE);
mVoteButton.setOnClickListener(v -> vote(story));
final TextView titleTextView = (TextView) findViewById(android.R.id.text2);
if (story.isStoryType()) {
titleTextView.setText(story.getDisplayedTitle());
setTaskTitle(story.getDisplayedTitle());
if (!TextUtils.isEmpty(story.getSource())) {
TextView sourceTextView = (TextView) findViewById(R.id.source);
sourceTextView.setText(story.getSource());
sourceTextView.setVisibility(View.VISIBLE);
}
} else {
AppUtils.setTextAppearance(titleTextView, R.style.TextAppearance_App_Small);
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(AppUtils.getThemedResId(this, R.attr.contentTextSize)));
CharSequence title = AppUtils.fromHtml(story.getDisplayedTitle(), true);
titleTextView.setText(title);
setTaskTitle(title);
}
final TextView postedTextView = (TextView) findViewById(R.id.posted);
postedTextView.setText(story.getDisplayedTime(this));
postedTextView.append(story.getDisplayedAuthor(this, true, 0));
postedTextView.setMovementMethod(LinkMovementMethod.getInstance());
switch (story.getType()) {
case Item.JOB_TYPE:
postedTextView.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_work_white_18dp, 0, 0, 0);
break;
case Item.POLL_TYPE:
postedTextView.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_poll_white_18dp, 0, 0, 0);
break;
}
mAdapter = new ItemPagerAdapter(this, getSupportFragmentManager(),
new ItemPagerAdapter.Builder()
.setItem(story)
.setShowArticle(!mExternalBrowser)
.setCacheMode(getIntent().getIntExtra(EXTRA_CACHE_MODE, ItemManager.MODE_DEFAULT))
.setRetainInstance(true)
.setDefaultViewMode(mStoryViewMode));
mAdapter.bind(mViewPager, mTabLayout, mNavButton, mReplyButton);
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager) {
@Override
public void onTabReselected(TabLayout.Tab tab) {
mAppBar.setExpanded(true, true);
}
});
if (story.isStoryType() && mExternalBrowser) {
TextView buttonArticle = (TextView) findViewById(R.id.button_article);
buttonArticle.setTransformationMethod(new AllCapsTransformationMethod(this));
buttonArticle.setVisibility(View.VISIBLE);
buttonArticle.setOnClickListener(v ->
AppUtils.openWebUrlExternal(ItemActivity.this,
story, story.getUrl(), mCustomTabsDelegate.getSession()));
}
if (mFullscreen) {
setFullscreen();
}
}
private void decorateFavorite(boolean isFavorite) {
mBookmark.setImageResource(isFavorite ?
R.drawable.ic_bookmark_white_24dp : R.drawable.ic_bookmark_border_white_24dp);
}
private <T> T getCurrent(Class<T> clazz) {
if (mAdapter == null) {
return null;
}
Fragment currentItem = mAdapter.getItem(mViewPager.getCurrentItem());
if (clazz.isInstance(currentItem)) {
//noinspection unchecked
return (T) currentItem;
} else {
return null;
}
}
private void vote(final WebItem story) {
mUserServices.voteUp(ItemActivity.this, story.getId(), new VoteCallback(this));
}
@Synthetic
void onVoted(Boolean successful) {
if (successful == null) {
Toast.makeText(this, R.string.vote_failed, Toast.LENGTH_SHORT).show();
} else if (successful) {
Drawable drawable = DrawableCompat.wrap(mVoteButton.getDrawable());
DrawableCompat.setTint(drawable, ContextCompat.getColor(this, R.color.greenA700));
Toast.makeText(this, R.string.voted, Toast.LENGTH_SHORT).show();
} else {
AppUtils.showLogin(this, mAlertDialogBuilder);
}
}
private void onPreferenceChanged(int key, boolean contextChanged) {
if (!Preferences.navigationEnabled(this)) {
NavFloatingActionButton.resetPosition(this);
}
AppUtils.toggleFab(mNavButton, navigationVisible());
}
private boolean navigationVisible() {
return mViewPager.getCurrentItem() == 0 && Preferences.navigationEnabled(this);
}
static class ItemResponseListener implements ResponseListener<Item> {
private final WeakReference<ItemActivity> mItemActivity;
@Synthetic
ItemResponseListener(ItemActivity itemActivity) {
mItemActivity = new WeakReference<>(itemActivity);
}
@Override
public void onResponse(@Nullable Item response) {
if (mItemActivity.get() != null && !mItemActivity.get().isActivityDestroyed()) {
mItemActivity.get().onItemLoaded(response);
}
}
@Override
public void onError(String errorMessage) {
// do nothing
}
}
static class VoteCallback extends UserServices.Callback {
private final WeakReference<ItemActivity> mItemActivity;
@Synthetic
VoteCallback(ItemActivity itemActivity) {
mItemActivity = new WeakReference<>(itemActivity);
}
@Override
public void onDone(boolean successful) {
if (mItemActivity.get() != null && !mItemActivity.get().isActivityDestroyed()) {
mItemActivity.get().onVoted(successful);
}
}
@Override
public void onError(Throwable throwable) {
if (mItemActivity.get() != null && !mItemActivity.get().isActivityDestroyed()) {
mItemActivity.get().onVoted(null);
}
}
}
}