package com.ubergeek42.WeechatAndroid.adapters; import android.annotation.SuppressLint; import android.os.Handler; import android.os.Looper; import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.View; import android.view.ViewGroup; import com.ubergeek42.WeechatAndroid.fragments.BufferFragment; import com.ubergeek42.WeechatAndroid.relay.Buffer; import com.ubergeek42.WeechatAndroid.relay.BufferList; import com.ubergeek42.WeechatAndroid.service.P; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; public class MainPagerAdapter extends PagerAdapter { final private static Logger logger = LoggerFactory.getLogger("MainPagerAdapter"); final private ArrayList<String> names = new ArrayList<>(); final private ViewPager pager; final private FragmentManager manager; final private Handler handler; private FragmentTransaction transaction = null; public MainPagerAdapter(FragmentManager manager, ViewPager pager) { super(); this.manager = manager; this.pager = pager; handler = new Handler(Looper.getMainLooper()); } //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// @MainThread public void openBuffer(final String name) { logger.debug("openBuffer({}); names = {} ", name, names); if (names.contains(name)) return; Buffer buffer = BufferList.findByFullName(name); if (buffer != null) buffer.setOpen(true); names.add(name); notifyDataSetChanged(); P.setBufferOpen(name, true); } @MainThread public void closeBuffer(String name) { logger.debug("closeBuffer({})", name); if (!names.remove(name)) return; notifyDataSetChanged(); Buffer buffer = BufferList.findByFullName(name); if (buffer != null) buffer.setOpen(false); P.setBufferOpen(name, false); } public void focusBuffer(String name) { pager.setCurrentItem(names.indexOf(name)); } public void setBufferInputText(@NonNull final String name, @NonNull final String text) { BufferFragment bufferFragment = getBufferFragment(names.indexOf(name)); if (bufferFragment == null) { logger.info("Tried to set input text of unknown buffer; name = {}", name); return; } bufferFragment.setText(text); } // returns whether a buffer is inside the pager public boolean isBufferOpen(String name) { return names.contains(name); } // returns full name of the buffer that is currently focused or null if there's no buffers public @Nullable String getCurrentBufferFullName() { int i = pager.getCurrentItem(); return (names.size() > i) ? names.get(i) : null; } // returns BufferFragment that is currently focused or null public @Nullable BufferFragment getCurrentBufferFragment() { return getBufferFragment(pager.getCurrentItem()); } private @Nullable BufferFragment getBufferFragment(int i) { if (names.size() <= i) return null; return (BufferFragment) manager.findFragmentByTag(names.get(i)); } //////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////// overrides //////////////////////////////////////////////////////////////////////////////////////////////// // attach a fragment if it's in the FragmentManager, create and add a new one if it's not @Override @SuppressLint("CommitTransaction") public Object instantiateItem(ViewGroup container, int i) { if (transaction == null) transaction = manager.beginTransaction(); String tag = names.get(i); Fragment frag = manager.findFragmentByTag(tag); logger.debug("instantiateItem(..., {}/{}): {}", i, tag, frag == null ? "add" : "attach"); if (frag == null) { transaction.add(container.getId(), frag = BufferFragment.newInstance(tag), tag); } else { transaction.attach(frag); } return frag; } // detach fragment if it went off-screen or remove it completely if it's been closed by user @Override @SuppressLint("CommitTransaction") public void destroyItem(ViewGroup container, int i, Object object) { if (transaction == null) transaction = manager.beginTransaction(); Fragment frag = (Fragment) object; logger.debug("destroyItem(..., {}, {}): {}", i, frag.getTag(), names.contains(frag.getTag()) ? "detach" : "remove"); if (names.contains(frag.getTag())) { transaction.detach(frag); } else { transaction.remove(frag); } } //////////////////////////////////////////////////////////////////////////////////////////////// @Override public int getCount() { return names.size(); } @Override public CharSequence getPageTitle(int i) { String name = names.get(i); Buffer buffer = BufferList.findByFullName(names.get(i)); return buffer == null ? name : buffer.shortName; } @Override public boolean isViewFromObject(View view, Object object) { return ((Fragment) object).getView() == view; } private Fragment oldFrag; @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { if (object == oldFrag) return; Fragment frag = (Fragment) object; if (oldFrag != null) { oldFrag.setMenuVisibility(false); oldFrag.setUserVisibleHint(false); } if (frag != null) { frag.setMenuVisibility(true); frag.setUserVisibleHint(true); } oldFrag = frag; } // this should return index for fragments or POSITION_NONE if a fragment has been removed // providing proper indexes instead of POSITION_NONE allows buffers not to be // fully recreated on every uiBuffer list change @Override public int getItemPosition(Object object) { logger.debug("getItemPosition({})", object); int idx = names.indexOf(((Fragment) object).getTag()); return (idx >= 0) ? idx : POSITION_NONE; } // this one's empty because instantiateItem and destroyItem create transactions as needed // this function is called too frequently to create a transaction inside it @Override public void startUpdate(ViewGroup container) {} // commit the transaction and execute it ASAP, but NOT on the current loop // this way the drawer will wait for the fragment to appear @Override public void finishUpdate(ViewGroup container) { if (transaction == null) return; transaction.commitAllowingStateLoss(); transaction = null; handler.postAtFrontOfQueue(new Runnable() { @Override public void run() { manager.executePendingTransactions(); } }); } //////////////////////////////////////////////////////////////////////////////////////////////// public boolean canRestoreBuffers() { return P.openBuffers.size() > 0 && names.size() == 0 && BufferList.hasData(); } @MainThread public void restoreBuffers() { for (String fullName : P.openBuffers) openBuffer(fullName); } }