/* * Copyright 2015 Hippo Seven * * 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.hippo.nimingban.ui; import android.Manifest; import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.support.v7.widget.Toolbar; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; import android.text.style.URLSpan; import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.hippo.app.CheckBoxDialogBuilder; import com.hippo.drawerlayout.DrawerLayout; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.MarginItemDecoration; import com.hippo.easyrecyclerview.RawMarginItemDecoration; import com.hippo.nimingban.Constants; import com.hippo.nimingban.GuideHelper; import com.hippo.nimingban.NMBApplication; import com.hippo.nimingban.PermissionRequester; import com.hippo.nimingban.R; import com.hippo.nimingban.client.NMBClient; import com.hippo.nimingban.client.NMBException; import com.hippo.nimingban.client.NMBRequest; import com.hippo.nimingban.client.Notice; import com.hippo.nimingban.client.UpdateHelper; import com.hippo.nimingban.client.ac.ACUrl; import com.hippo.nimingban.client.data.ACSite; import com.hippo.nimingban.client.data.CommonPost; import com.hippo.nimingban.client.data.DisplayForum; import com.hippo.nimingban.client.data.DumpSite; import com.hippo.nimingban.client.data.Forum; import com.hippo.nimingban.client.data.Post; import com.hippo.nimingban.client.data.Reply; import com.hippo.nimingban.client.data.UpdateStatus; import com.hippo.nimingban.util.Crash; import com.hippo.nimingban.util.DB; import com.hippo.nimingban.util.LinkMovementMethod2; import com.hippo.nimingban.util.ReadableTime; import com.hippo.nimingban.util.Settings; import com.hippo.nimingban.widget.ContentLayout; import com.hippo.nimingban.widget.FontTextView; import com.hippo.nimingban.widget.LeftDrawer; import com.hippo.nimingban.widget.LoadImageView; import com.hippo.nimingban.widget.MarqueeReplyView; import com.hippo.nimingban.widget.RightDrawer; import com.hippo.ripple.Ripple; import com.hippo.text.Html; import com.hippo.text.URLImageGetter; import com.hippo.unifile.UniFile; import com.hippo.util.ActivityHelper; import com.hippo.util.DrawableManager; import com.hippo.view.SimpleDoubleTapListener; import com.hippo.view.SimpleGestureListener; import com.hippo.widget.AutoWrapLayout; import com.hippo.widget.drawerlayout.ActionBarDrawerToggle; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.Messenger; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.ResourcesUtils; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import jp.wasabeef.recyclerview.animators.SlideInUpAnimator; public final class ListActivity extends AbsActivity implements RightDrawer.RightDrawerHelper, LeftDrawer.Helper { private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0; private static final int BACK_PRESSED_INTERVAL = 2000; public static final int REQUEST_CODE_SETTINGS = 0; public static final int REQUEST_CODE_SORT_FORUMS = 1; public static final int RESULT_CODE_REFRESH = 10; private NMBClient mNMBClient; private DrawerLayout mDrawerLayout; private ContentLayout mContentLayout; private EasyRecyclerView mRecyclerView; private LeftDrawer mLeftDrawer; private RightDrawer mRightDrawer; private ActionBarDrawerToggle mDrawerToggle; private MenuItem mRule; private MenuItem mCreatePost; private MenuItem mRefresh; private MenuItem mSortForumsMenu; private @Nullable Forum mCurrentForum; private PostHelper mPostHelper; private PostAdapter mPostAdapter; private RecyclerView.OnScrollListener mOnScrollListener; private NMBRequest mNMBRequest; private NMBRequest mUpdateRequest; private NMBRequest mCommonPostsRequest; private NMBRequest mNoticeRequest; // Double click back exit private long mPressBackTime = 0; private List<WeakReference<ListHolder>> mListHolderList = new LinkedList<>(); @Override protected int getLightThemeResId() { return Settings.getColorStatusBar() ? R.style.NormalActivity : R.style.NormalActivity_NoStatus; } @Override protected int getDarkThemeResId() { return Settings.getColorStatusBar() ? R.style.NormalActivity_Dark : R.style.NormalActivity_Dark_NoStatus; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list); mNMBClient = NMBApplication.getNMBClient(this); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mContentLayout = (ContentLayout) mDrawerLayout.findViewById(R.id.content_layout); mRecyclerView = mContentLayout.getRecyclerView(); mLeftDrawer = (LeftDrawer) mDrawerLayout.findViewById(R.id.left_drawer); mRightDrawer = (RightDrawer) mDrawerLayout.findViewById(R.id.right_drawer); final GestureDetector gestureDetector = new GestureDetector(this, new SimpleGestureListener()); gestureDetector.setOnDoubleTapListener(new SimpleDoubleTapListener() { @Override public boolean onDoubleTap(MotionEvent e) { mPostHelper.refresh(); return true; } }); toolbar.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); return true; } }); setSupportActionBar(toolbar); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { @Override public void onDrawerSlide(View drawerView, float slideOffset) { if (mLeftDrawer == drawerView) { super.onDrawerSlide(drawerView, slideOffset); } } private void setMenuItemVisible(MenuItem item, boolean visible) { if (item != null) { item.setVisible(visible); } } @Override public void onDrawerClosed(View view) { if (mLeftDrawer == view) { super.onDrawerClosed(view); } if (mRightDrawer == view) { setMenuItemVisible(mRule, true); setMenuItemVisible(mCreatePost, true); setMenuItemVisible(mRefresh, true); setMenuItemVisible(mSortForumsMenu, false); } } @Override public void onDrawerOpened(View view) { if (mLeftDrawer == view) { super.onDrawerOpened(view); } if (mRightDrawer == view) { setMenuItemVisible(mRule, false); setMenuItemVisible(mCreatePost, false); setMenuItemVisible(mRefresh, false); setMenuItemVisible(mSortForumsMenu, true); } } }; mDrawerLayout.setDrawerListener(mDrawerToggle); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); } mDrawerLayout.setDrawerShadow(ContextCompat.getDrawable(this, R.drawable.drawer_shadow_left), Gravity.LEFT); mDrawerLayout.setDrawerShadow(ContextCompat.getDrawable(this, R.drawable.drawer_shadow_right), Gravity.RIGHT); mDrawerLayout.setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark)); mPostHelper = new PostHelper(); mPostHelper.setEmptyString(getString(R.string.no_post)); mContentLayout.setHelper(mPostHelper); if (Settings.getFastScroller()) { mContentLayout.showFastScroll(); } else { mContentLayout.hideFastScroll(); } mPostAdapter = new PostAdapter(); mRecyclerView.setAdapter(mPostAdapter); mRecyclerView.setSelector(Ripple.generateRippleDrawable( this, ResourcesUtils.getAttrBoolean(this, R.attr.dark))); mRecyclerView.setDrawSelectorOnTop(true); mRecyclerView.setOnItemClickListener(new ClickPostListener()); mRecyclerView.hasFixedSize(); mRecyclerView.setClipToPadding(false); mOnScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (RecyclerView.SCROLL_STATE_DRAGGING == newState) { pauseHolders(); } else if (RecyclerView.SCROLL_STATE_IDLE == newState) { resumeHolders(); } } }; mRecyclerView.addOnScrollListener(mOnScrollListener); int interval = getResources().getDimensionPixelOffset(R.dimen.card_interval); if (getResources().getBoolean(R.bool.two_way)) { mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); MarginItemDecoration decoration = new MarginItemDecoration(interval, interval, interval, interval, interval); mRecyclerView.addItemDecoration(decoration); decoration.applyPaddings(mRecyclerView); mRecyclerView.setItemAnimator(new SlideInUpAnimator()); } else { int halfInterval = interval / 2; mRecyclerView.addItemDecoration(new RawMarginItemDecoration(0, halfInterval, 0, halfInterval)); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.setPadding(0, halfInterval, 0, halfInterval); } mLeftDrawer.setHelper(this); mRightDrawer.setRightDrawerHelper(this); if (savedInstanceState == null) { int ils = Settings.getImageLoadingStrategy(); if (ils == Settings.IMAGE_LOADING_STRATEGY_ALL || (ils == Settings.IMAGE_LOADING_STRATEGY_WIFI && NMBApplication.isConnectedWifi(ListActivity.this))) { mLeftDrawer.loadHeaderImageView(); } } updateForums(true); checkForAppStart(); Messenger.getInstance().register(Constants.MESSENGER_ID_CREATE_POST, this); Messenger.getInstance().register(Constants.MESSENGER_ID_FAST_SCROLLER, this); // Check permission PermissionRequester.request(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, getString(R.string.write_storage_permission_tip), PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE); if (Settings.getGuideListActivity()) { showLeftDrawerGuide(); } } private void showLeftDrawerGuide() { new GuideHelper.Builder(this) .setColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimary)) .setPadding(LayoutUtils.dp2pix(this, 16)) .setMessagePosition(Gravity.LEFT) .setMessage(getString(R.string.swipe_right_open_menu)) .setButton(getString(R.string.get_it)) .setBackgroundColor(0x73000000) .setOnDissmisListener(new View.OnClickListener() { @Override public void onClick(View v) { showRightDrawerGuide(); } }).show(); } private void showRightDrawerGuide() { new GuideHelper.Builder(this) .setColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimary)) .setPadding(LayoutUtils.dp2pix(this, 16)) .setMessagePosition(Gravity.RIGHT) .setMessage(getString(R.string.swipe_left_open_forum_list)) .setButton(getString(R.string.get_it)) .setBackgroundColor(0x73000000) .setOnDissmisListener(new View.OnClickListener() { @Override public void onClick(View v) { showToolbarGuide(); } }).show(); } private void showToolbarGuide() { new GuideHelper.Builder(this) .setColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimary)) .setPadding(LayoutUtils.dp2pix(this, 16)) .setMessagePosition(Gravity.TOP) .setMessage(getString(R.string.double_click_toolbar_refresh)) .setButton(getString(R.string.get_it)) .setBackgroundColor(0x73000000) .setOnDissmisListener(new View.OnClickListener() { @Override public void onClick(View v) { Settings.putGuideListActivity(false); } }).show(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) { if (grantResults.length == 1 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, R.string.you_rejected_me, Toast.LENGTH_SHORT).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void resumeHolders() { Iterator<WeakReference<ListHolder>> iterator = mListHolderList.iterator(); while (iterator.hasNext()) { ListHolder holder = iterator.next().get(); if (holder != null) { // Only resume attached view holder if (holder.itemView.getParent() != null) { holder.thumb.start(); } } else { iterator.remove(); } } } @Override protected void onResume() { super.onResume(); resumeHolders(); } private void pauseHolders() { Iterator<WeakReference<ListHolder>> iterator = mListHolderList.iterator(); while (iterator.hasNext()) { ListHolder holder = iterator.next().get(); if (holder != null) { holder.thumb.stop(); } else { iterator.remove(); } } } @Override protected void onPause() { super.onPause(); pauseHolders(); } @Override protected void onDestroy() { super.onDestroy(); Messenger.getInstance().unregister(Constants.MESSENGER_ID_CREATE_POST, this); Messenger.getInstance().unregister(Constants.MESSENGER_ID_FAST_SCROLLER, this); if (mUpdateRequest != null) { mUpdateRequest.cancel(); mUpdateRequest = null; } if (mNMBRequest != null) { mNMBRequest.cancel(); mNMBRequest = null; } if (mCommonPostsRequest != null) { mCommonPostsRequest.cancel(); mCommonPostsRequest = null; } if (mNoticeRequest != null) { mNoticeRequest.cancel(); mNoticeRequest = null; } mRecyclerView.removeOnScrollListener(mOnScrollListener); for (WeakReference<ListHolder> ref : mListHolderList) { ListHolder holder = ref.get(); if (holder != null) { holder.thumb.unload(); } } mListHolderList.clear(); } private void checkForAppStart() { // Check crash if (Crash.hasCrashFile()) { new AlertDialog.Builder(this) .setTitle(R.string.it_is_important) .setMessage(R.string.crash_tip) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String content = Crash.getCrashContent(); ActivityHelper.sendEmail(ListActivity.this, "hipposeven332$gmail.com".replaceAll("\\$", "@"), "I found a bug in nimingban", content); } }).setNegativeButton(android.R.string.cancel, null) .setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { Crash.resetCrashFile(); } }) .show(); } // Check image save location UniFile uniFile = Settings.getImageSaveLocation(); if (uniFile == null || !uniFile.ensureDir()) { Toast.makeText(this, R.string.cant_make_sure_image_save_location, Toast.LENGTH_SHORT).show(); } // Check new icon if (Settings.getNewIcon()) { new AlertDialog.Builder(this) .setTitle(R.string.app_new_icon_title) .setMessage(R.string.app_new_icon_msg) .setPositiveButton(android.R.string.ok, null) .setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { Settings.putNewIcon(false); } }).show(); } // Check analysis if (!Settings.getSetAnalysis()) { DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Settings.putSetAnalysis(true); Settings.putAnalysis(which == DialogInterface.BUTTON_POSITIVE); } }; try { CharSequence message = Html.fromHtml(IOUtils.readString( getResources().openRawResource(R.raw.analysis_plain), "UTF-8")); Dialog dialog = new AlertDialog.Builder(this) .setTitle(R.string.data_analysis) .setMessage(message) .setCancelable(false) .setPositiveButton(R.string.agree, listener) .setNegativeButton(R.string.disagree, listener) .show(); TextView messageView = (TextView) dialog.findViewById(android.R.id.message); if (messageView != null) { messageView.setMovementMethod(new LinkMovementMethod2(ListActivity.this)); } } catch (IOException e) { e.printStackTrace(); } } NMBRequest request; // Check update request = new NMBRequest(); mUpdateRequest = request; request.setMethod(NMBClient.METHOD_UPDATE); request.setCallback(new NMBClient.Callback<UpdateStatus>() { @Override public void onSuccess(UpdateStatus result) { Log.d("UpdateStatus", ObjectUtils.toString(result)); mUpdateRequest = null; UpdateHelper.showUpdateDialog(ListActivity.this, result); } @Override public void onFailure(Exception e) { mUpdateRequest = null; e.printStackTrace(); } @Override public void onCancel() { mUpdateRequest = null; } }); mNMBClient.execute(request); // Get common post request = new NMBRequest(); mCommonPostsRequest = request; request.setSite(ACSite.getInstance()); request.setMethod(NMBClient.METHOD_COMMON_POSTS); request.setCallback(new NMBClient.Callback<List<CommonPost>>() { @Override public void onSuccess(List<CommonPost> result) { mCommonPostsRequest = null; DB.setACCommonPost(result); } @Override public void onFailure(Exception e) { mCommonPostsRequest = null; e.printStackTrace(); } @Override public void onCancel() { mCommonPostsRequest = null; } }); mNMBClient.execute(request); // Get Notice request = new NMBRequest(); mNoticeRequest = request; request.setSite(ACSite.getInstance()); request.setMethod(NMBClient.METHOD_NOTICE); request.setCallback(new NMBClient.Callback<Notice>() { @Override public void onSuccess(Notice result) { mNoticeRequest = null; if (isFinishing()) { return; } if (!result.enable) { return; } long oldDate = Settings.getNoticeDate(); final long newDate = result.date; if (newDate <= oldDate) { return; } final CheckBoxDialogBuilder builder = new CheckBoxDialogBuilder( ListActivity.this, Html.fromHtml(result.content), getString(R.string.get_it), false); Dialog dialog = builder.setTitle(R.string.notice).setOnDismissListener( new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (builder.isChecked()) { Settings.putNoticeDate(newDate); } } }).setPositiveButton(android.R.string.ok, null).show(); ((TextView) dialog.findViewById(R.id.message)).setMovementMethod( new LinkMovementMethod2(ListActivity.this)); } @Override public void onFailure(Exception e) { e.printStackTrace(); mNoticeRequest = null; } @Override public void onCancel() { mNoticeRequest = null; } }); mNMBClient.execute(request); } // Update current forum and update UI private void updateCurrentForum(Forum forum) { mCurrentForum = forum; mRightDrawer.setActivatedForum(forum); updateTitleByForum(mCurrentForum); } private void updateForums(boolean firstTime) { Forum currentForum = mCurrentForum; List<DisplayForum> forums = DB.getACForums(true); // TODO DB.getForums mRightDrawer.setForums(forums); // Try to find the same forum if (currentForum != null) { for (DisplayForum forum : forums) { if (currentForum.getNMBSite() == forum.getNMBSite() && currentForum.getNMBId().equals(forum.getNMBId())) { updateCurrentForum(forum); return; } } } if (forums.size() > 0) { updateCurrentForum(forums.get(0)); } else { updateCurrentForum(null); } if (firstTime) { mPostHelper.firstRefresh(); } else { mPostHelper.refresh(); } } @Override public void onReceive(int id, Object obj) { if (Constants.MESSENGER_ID_CREATE_POST == id) { if (mCurrentForum != null && mCurrentForum.getNMBId().equals(obj)) { int currentPage = mPostHelper.getPageForBottom(); if (currentPage == 0) { mPostHelper.refresh(); } } } else if (Constants.MESSENGER_ID_FAST_SCROLLER == id) { if (obj instanceof Boolean) { if ((Boolean) obj) { mContentLayout.showFastScroll(); } else { mContentLayout.hideFastScroll(); } } } else { super.onReceive(id, obj); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_SETTINGS) { if (resultCode == RESULT_OK) { mPostAdapter.notifyDataSetChanged(); } else if (resultCode == RESULT_CODE_REFRESH) { mPostHelper.refresh(); } } else if (requestCode == REQUEST_CODE_SORT_FORUMS) { if (resultCode == RESULT_OK) { updateForums(true); } } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onBackPressed() { if (mDrawerLayout != null && (mDrawerLayout.isDrawerOpen(Gravity.LEFT) || mDrawerLayout.isDrawerOpen(Gravity.RIGHT))) { mDrawerLayout.closeDrawers(); } else { long time = System.currentTimeMillis(); if (time - mPressBackTime > BACK_PRESSED_INTERVAL) { // It is the last scene mPressBackTime = time; Toast.makeText(this, R.string.press_twice_exit, Toast.LENGTH_SHORT).show(); } else { super.onBackPressed(); } } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_list, menu); mRule = menu.findItem(R.id.action_rule); mCreatePost = menu.findItem(R.id.action_create_post); mRefresh = menu.findItem(R.id.action_refresh); mSortForumsMenu = menu.findItem(R.id.action_sort_forums); if (mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) { mRule.setVisible(false); mCreatePost.setVisible(false); mRefresh.setVisible(false); mSortForumsMenu.setVisible(true); } else { mRule.setVisible(true); mCreatePost.setVisible(true); mRefresh.setVisible(true); mSortForumsMenu.setVisible(false); } return true; } private Spanned fixURLSpan(Spanned spanned) { Spannable spannable; if (spanned instanceof Spannable) { spannable = (Spannable) spanned; } else { spannable = new SpannableString(spanned); } URLSpan[] urlSpans = spannable.getSpans(0, spanned.length(), URLSpan.class); if (urlSpans == null) { return spanned; } for (URLSpan urlSpan : urlSpans) { String url = urlSpan.getURL(); if (TextUtils.isEmpty(url)) { spannable.removeSpan(urlSpan); } try { new URL(url); } catch (MalformedURLException e) { URL absoluteUrl; // It might be relative path try { // Use absolute url absoluteUrl = new URL(new URL(ACUrl.HOST), url); int start = spannable.getSpanStart(urlSpan); int end = spannable.getSpanEnd(urlSpan); spannable.removeSpan(urlSpan); spannable.setSpan(new URLSpan(absoluteUrl.toString()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } catch (MalformedURLException e1) { // Can't get url spannable.removeSpan(urlSpan); } } } return spannable; } @Override public boolean onOptionsItemSelected(MenuItem item) { Intent intent; switch (item.getItemId()) { case android.R.id.home: if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) { mDrawerLayout.closeDrawer(Gravity.LEFT); } else { mDrawerLayout.openDrawer(Gravity.LEFT); } mDrawerLayout.closeDrawer(Gravity.RIGHT); return true; case R.id.action_rule: if (mCurrentForum != null && mCurrentForum.getNMBMsg() != null) { View view = getLayoutInflater().inflate(R.layout.dialog_rule, null); TextView tv = (TextView) view.findViewById(R.id.text); tv.setText(fixURLSpan(Html.fromHtml(mCurrentForum.getNMBMsg(), new URLImageGetter(tv, NMBApplication.getConaco(this)), null))); tv.setMovementMethod(new LinkMovementMethod2(ListActivity.this)); new AlertDialog.Builder(this).setTitle(R.string.rule).setView(view).show(); } return true; case R.id.action_create_post: if (mCurrentForum != null) { intent = new Intent(this, TypeSendActivity.class); intent.setAction(TypeSendActivity.ACTION_CREATE_POST); intent.putExtra(TypeSendActivity.KEY_SITE, mCurrentForum.getNMBSite().getId()); intent.putExtra(TypeSendActivity.KEY_ID, mCurrentForum.getNMBId()); startActivity(intent); } return true; case R.id.action_refresh: mPostHelper.refresh(); return true; case R.id.action_sort_forums: intent = new Intent(this, SortForumsActivity.class); intent.putExtra(SortForumsActivity.KEY_SITE, ACSite.getInstance().getId()); // TODO support other site startActivityForResult(intent, REQUEST_CODE_SORT_FORUMS); return true; default: return super.onOptionsItemSelected(item); } } private void updateTitleByForum(Forum forum) { if (forum != null) { setTitle(forum.getNMBDisplayname()); } else { setTitle(getString(R.string.app_name)); } } @Override public void onClickCommonPosts() { AutoWrapLayout layout = new AutoWrapLayout(ListActivity.this); final Dialog dialog = new AlertDialog.Builder(this).setView(layout).show(); List<CommonPost> list = DB.getAllACCommentPost(); int padding = LayoutUtils.dp2pix(this, 4); layout.setPadding(padding, 0, padding, 0); LayoutInflater inflater = getLayoutInflater(); for (final CommonPost cp : list) { inflater.inflate(R.layout.item_dialog_comment_post, layout); TextView tv = (TextView) layout.getChildAt(layout.getChildCount() - 1); tv.setText(cp.name); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); Intent intent = new Intent(ListActivity.this, PostActivity.class); intent.setAction(PostActivity.ACTION_SITE_ID); intent.putExtra(PostActivity.KEY_SITE, ACSite.getInstance().getId()); intent.putExtra(PostActivity.KEY_ID, cp.id); startActivity(intent); } }); } } @Override public void onSelectForum(Forum forum) { if (mCurrentForum == null || (mCurrentForum.getNMBSite() != forum.getNMBSite() || !mCurrentForum.getNMBId().equals(forum.getNMBId()))) { updateCurrentForum(forum); mDrawerLayout.closeDrawer(Gravity.RIGHT); mPostHelper.refresh(); } } @Override public void OnLongClickImage(UniFile imageFile) { Intent intent = new Intent(this, GalleryActivity2.class); intent.setAction(GalleryActivity2.ACTION_IMAGE_FILE); intent.putExtra(GalleryActivity2.KEY_UNI_FILE_URI, imageFile.getUri()); startActivity(intent); } public class SearchDialogHelper implements View.OnClickListener { public View mView; public EditText mEditText; public View mPositive; public View mNegative; public View mNeutral; public Dialog mDialog; @SuppressLint("InflateParams") public SearchDialogHelper() { mView = getLayoutInflater().inflate(R.layout.dialog_search, null); mEditText = (EditText) mView.findViewById(R.id.edit_text); } public View getView() { return mView; } public void setDialog(AlertDialog dialog) { mDialog = dialog; mPositive = dialog.getButton(DialogInterface.BUTTON_POSITIVE); mNegative = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); mNeutral = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); mPositive.setOnClickListener(this); mNegative.setOnClickListener(this); mNeutral.setOnClickListener(this); } @Override public void onClick(View v) { if (mNegative == v) { Intent intent = new Intent(ListActivity.this, PostActivity.class); intent.setAction(PostActivity.ACTION_SITE_REPLY_ID); intent.putExtra(PostActivity.KEY_SITE, ACSite.getInstance().getId()); intent.putExtra(PostActivity.KEY_ID, Integer.toString(MathUtils.random(1, 6666667))); // TODO how to get the max id startActivity(intent); mDialog.dismiss(); return; } String keyword = mEditText.getText().toString().trim(); if (TextUtils.isEmpty(keyword)) { Toast.makeText(ListActivity.this, R.string.keyword_empty, Toast.LENGTH_SHORT).show(); return; } if (mPositive == v) { Intent intent = new Intent(ListActivity.this, SearchActivity.class); intent.setAction(SearchActivity.ACTION_SEARCH); intent.putExtra(SearchActivity.KEY_KEYWORD, keyword); startActivity(intent); mDialog.dismiss(); } else if (mNeutral == v) { Intent intent = new Intent(ListActivity.this, PostActivity.class); intent.setAction(PostActivity.ACTION_SITE_REPLY_ID); intent.putExtra(PostActivity.KEY_SITE, ACSite.getInstance().getId()); intent.putExtra(PostActivity.KEY_ID, keyword); startActivity(intent); mDialog.dismiss(); } } } @Override public void onClickSearch() { SearchDialogHelper helper = new SearchDialogHelper(); AlertDialog dialog = new AlertDialog.Builder(this) .setTitle(R.string.search) .setView(helper.getView()) .setPositiveButton(R.string.search, null) .setNegativeButton(R.string.lucky, null) .setNeutralButton(R.string.go_to_post, null) .show(); helper.setDialog(dialog); } @Override public void onClickFeed() { Intent intent = new Intent(this, FeedActivity.class); startActivity(intent); } @Override public void onClickRecord() { Intent intent = new Intent(this, RecordActivity.class); startActivity(intent); } @Override public void onClickSettings() { Intent intent = new Intent(this, SettingsActivity.class); startActivityForResult(intent, REQUEST_CODE_SETTINGS); } private class ClickPostListener implements EasyRecyclerView.OnItemClickListener { @Override public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { Intent intent = new Intent(ListActivity.this, PostActivity.class); intent.setAction(PostActivity.ACTION_POST); intent.putExtra(PostActivity.KEY_POST, mPostHelper.getDataAt(position)); startActivity(intent); return true; } } private class ListHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView leftText; public TextView centerText; public TextView rightText; public FontTextView content; public TextView bottomText; public LoadImageView thumb; public View bottom; public MarqueeReplyView reply; public ListHolder(View itemView) { super(itemView); leftText = (TextView) itemView.findViewById(R.id.left_text); centerText = (TextView) itemView.findViewById(R.id.center_text); rightText = (TextView) itemView.findViewById(R.id.right_text); content = (FontTextView) itemView.findViewById(R.id.content); bottomText = (TextView) itemView.findViewById(R.id.bottom_text); thumb = (LoadImageView) itemView.findViewById(R.id.thumb); reply = (MarqueeReplyView) itemView.findViewById(R.id.reply); bottom = itemView.findViewById(R.id.bottom); thumb.setOnClickListener(this); Drawable drawable = DrawableManager.getDrawable(ListActivity.this, R.drawable.v_comment_multiple_outline_x16); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); bottomText.setCompoundDrawables(drawable, null, null, null); } @Override public void onClick(View v) { int position = getAdapterPosition(); if (position >= 0 && position < mPostHelper.size()) { Post post = mPostHelper.getDataAt(position); String key = post.getNMBImageKey(); String image = post.getNMBImageUrl(); if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(image)) { Intent intent = new Intent(ListActivity.this, GalleryActivity2.class); intent.setAction(GalleryActivity2.ACTION_SINGLE_IMAGE); intent.putExtra(GalleryActivity2.KEY_SITE, post.getNMBSite().getId()); intent.putExtra(GalleryActivity2.KEY_ID, post.getNMBId()); intent.putExtra(GalleryActivity2.KEY_KEY, key); intent.putExtra(GalleryActivity2.KEY_IMAGE, image); ListActivity.this.startActivity(intent); } } } public boolean setReplies(Reply[] replies) { if (replies == null || replies.length == 0) { reply.setVisibility(View.INVISIBLE); return false; } else { reply.setVisibility(View.VISIBLE); reply.setReplies(replies); return true; } } } private class PostAdapter extends RecyclerView.Adapter<ListHolder> { @Override public ListHolder onCreateViewHolder(ViewGroup parent, int viewType) { ListHolder holder = new ListHolder(getLayoutInflater().inflate(R.layout.item_list, parent, false)); mListHolderList.add(new WeakReference<>(holder)); return holder; } @Override public void onBindViewHolder(ListHolder holder, int position) { Post post = mPostHelper.getDataAt(position); holder.leftText.setText(post.getNMBDisplayUsername()); holder.centerText.setText("No." + post.getNMBId()); holder.rightText.setText(ReadableTime.getDisplayTime(post.getNMBTime())); holder.content.setText(post.getNMBDisplayContent()); holder.bottomText.setText(post.getNMBReplyDisplayCount()); View bottom = holder.bottom; RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) bottom.getLayoutParams(); String thumbKey = post.getNMBThumbKey(); String thumbUrl = post.getNMBThumbUrl(); boolean tryShowImage; boolean loadFromNetwork; int ils = Settings.getImageLoadingStrategy(); if (ils == Settings.IMAGE_LOADING_STRATEGY_ALL || (ils == Settings.IMAGE_LOADING_STRATEGY_WIFI && NMBApplication.isConnectedWifi(ListActivity.this))) { tryShowImage = true; loadFromNetwork = true; } else { tryShowImage = Settings.getImageLoadingStrategy2(); loadFromNetwork = false; } boolean showImage; if (!TextUtils.isEmpty(thumbKey) && !TextUtils.isEmpty(thumbUrl) && tryShowImage) { showImage = true; holder.thumb.setVisibility(View.VISIBLE); holder.thumb.unload(); holder.thumb.load(thumbKey, thumbUrl, loadFromNetwork); } else { showImage = false; holder.thumb.setVisibility(View.GONE); holder.thumb.unload(); } boolean showReplies; Reply[] replies = post.getNMBReplies(); if (Settings.getDynamicComments()) { showReplies = holder.setReplies(replies); } else { showReplies = holder.setReplies(null); } if (showImage && !showReplies) { lp.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.thumb); lp.addRule(RelativeLayout.BELOW, 0); bottom.setLayoutParams(lp); } else if (showImage) { lp.addRule(RelativeLayout.ALIGN_BOTTOM, 0); lp.addRule(RelativeLayout.BELOW, R.id.thumb); bottom.setLayoutParams(lp); } else { lp.addRule(RelativeLayout.ALIGN_BOTTOM, 0); lp.addRule(RelativeLayout.BELOW, R.id.content); bottom.setLayoutParams(lp); } holder.content.setTextSize(Settings.getFontSize()); holder.content.setLineSpacing(LayoutUtils.dp2pix(ListActivity.this, Settings.getLineSpacing()), 1.0f); if (Settings.getFixEmojiDisplay()) { holder.content.useCustomTypeface(); } else { holder.content.useOriginalTypeface(); } } @Override public int getItemCount() { return mPostHelper.size(); } @Override public void onViewAttachedToWindow(ListHolder holder) { holder.thumb.start(); } @Override public void onViewDetachedFromWindow(ListHolder holder) { holder.thumb.stop(); } } private class PostHelper extends ContentLayout.ContentHelper<Post> { @Override protected Context getContext() { return ListActivity.this; } @Override protected void onScrollToPosition() { } @Override protected void onShowProgress() { } @Override protected void onShowText() { } @Override protected void notifyDataSetChanged() { mPostAdapter.notifyDataSetChanged(); } @Override protected void notifyItemRangeRemoved(int positionStart, int itemCount) { mPostAdapter.notifyItemRangeRemoved(positionStart, itemCount); } @Override protected void notifyItemRangeInserted(int positionStart, int itemCount) { mPostAdapter.notifyItemRangeInserted(positionStart, itemCount); } @Override protected void getPageData(int taskId, int type, int page) { if (mNMBRequest != null) { mNMBRequest.cancel(); mNMBRequest = null; } if (mCurrentForum == null) { onGetExpection(taskId, new NMBException(DumpSite.getInstance(), getString(R.string.no_forum))); } else { String forumId = mCurrentForum.getNMBId(); NMBRequest request = new NMBRequest(); mNMBRequest = request; request.setSite(mCurrentForum.getNMBSite()); request.setMethod(NMBClient.METHOD_GET_POST_LIST); request.setArgs(forumId, page); request.setCallback(new ListListener(taskId, page, forumId, request)); mNMBClient.execute(request); } } } private class ListListener implements NMBClient.Callback<List<Post>> { private int mTaskId; private int mTaskPage; private String mForumId; private NMBRequest mRequest; public ListListener(int taskId, int taskPage, String forumId, NMBRequest request) { mTaskId = taskId; mTaskPage = taskPage; mForumId = forumId; mRequest = request; } @Override public void onSuccess(List<Post> result) { if (mNMBRequest == mRequest) { // It is current request // Clear mNMBRequest = null; if (result.isEmpty()) { mPostHelper.setPages(mTaskPage); mPostHelper.onGetEmptyData(mTaskId); } else { mPostHelper.setPages(Integer.MAX_VALUE); mPostHelper.onGetPageData(mTaskId, result); } } // Clear mRequest = null; } @Override public void onFailure(Exception e) { if (mNMBRequest == mRequest) { // It is current request // Clear mNMBRequest = null; mPostHelper.onGetExpection(mTaskId, e); } // Clear mRequest = null; } @Override public void onCancel() { if (mNMBRequest == mRequest) { // It is current request // Clear mNMBRequest = null; } // Clear mRequest = null; } } }