Skip to content

Commit

Permalink
handling disconnections & stability fixes
Browse files Browse the repository at this point in the history
doesn't include ping/pong mechanism though
  • Loading branch information
oakkitten committed Jul 18, 2014
1 parent aeb568a commit dd5cd98
Show file tree
Hide file tree
Showing 15 changed files with 1,064 additions and 896 deletions.
10 changes: 8 additions & 2 deletions weechat-android/build.gradle
Expand Up @@ -47,21 +47,27 @@ android {
buildTypes {
// Debug version, only ever used locally
debug {
packageNameSuffix ".debug"
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
buildConfigField "String", "VERSION_BANNER", "\""+versionBanner()+"\""
}

// These are the real releases in the Google Play Store
release {
runProguard true
proguardFile file('proguard.txt')
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
buildConfigField "String", "VERSION_BANNER", "\"" + versionBanner() + "\""
signingConfig signingConfigs.releasePlayConfig
}

// Development releases in the Google Play Store(signed same as the cloudbees site)
devrelease.initWith(buildTypes.release)
devrelease {
packageNameSuffix ".dev"
runProguard true
proguardFile file('proguard.txt')
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
buildConfigField "String", "VERSION_BANNER", "\""+versionBanner()+"\""
signingConfig signingConfigs.devPlayConfig
Expand Down
26 changes: 26 additions & 0 deletions weechat-android/proguard.txt
@@ -0,0 +1,26 @@
# warnings prevent build from continuing
-ignorewarnings

# if we do not use obfuscation, everything will fail
# see http://stackoverflow.com/questions/5701126/compile-with-proguard-gives-exception-local-variable-type-mismatch
#-dontobfuscate

-dontskipnonpubliclibraryclasses
-forceprocessing
-optimizationpasses 5

# actionbarsherlock stuff
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }
-keepattributes *Annotation*

# strip all logger calls
# i hope the if (DEBUG) checks will get stripped too
-assumenosideeffects class org.slf4j.Logger {
public void error(...);
public void warn(...);
public void debug(...);
public void trace(...);
}
@@ -1,149 +1,201 @@
package com.ubergeek42.WeechatAndroid;

import java.util.ArrayList;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
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.fragments.BufferListFragment;

public class MainPagerAdapter extends FragmentStatePagerAdapter {
private static Logger logger = LoggerFactory.getLogger(MainPagerAdapter.class);
private BufferListFragment bufferListFragment = null;
private ArrayList<BufferFragment> buffers = new ArrayList<BufferFragment>();
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;

public class MainPagerAdapter extends PagerAdapter {

private static Logger logger = LoggerFactory.getLogger("MainPagerAdapter");
final private static boolean DEBUG = BuildConfig.DEBUG && true;

private boolean phone_mode = false;
private ArrayList<String> names = new ArrayList<String>();
private ArrayList<Fragment> fragments = new ArrayList<Fragment>();
private ViewPager pager;

public MainPagerAdapter(FragmentManager fm) throws Exception {
super(fm);
throw new Exception("Should not ever be used!");
}

public MainPagerAdapter(FragmentManager fm, ViewPager p) {
super(fm);
pager = p;
}
public void setBuffers(ArrayList<BufferFragment> frags) {
buffers = frags;
private FragmentManager manager;
private FragmentTransaction transaction = null;


public MainPagerAdapter(FragmentManager manager, ViewPager pager) {
super();
this.manager = manager;
this.pager = pager;
}

public void setBufferList(BufferListFragment blf) {
bufferListFragment = blf;
public void firstTimeInit(boolean phone_mode) {
this.phone_mode = phone_mode;
if (phone_mode) {
names.add("");
fragments.add(new BufferListFragment());
}
}

///////////////////////
///////////////////////
///////////////////////

@Override
public int getCount() {
if (bufferListFragment==null)
return buffers.size();
else
return 1+buffers.size();
return names.size();
}

/** this can be called either when a new fragment is being added or the old one is being
** shown. in both cases the fragment will be in this.fragments, but in the latter case it
** will not have been added to the fragment manager */
@Override
public Fragment getItem(int pos) {
// Tablet view
if (bufferListFragment==null)
return buffers.get(pos);

// Not tablet view
if (pos==0) {
return bufferListFragment;
public Object instantiateItem(ViewGroup container, int i) {
if (DEBUG) logger.info("instantiateItem(..., {})", i);
if (transaction == null)
transaction = manager.beginTransaction();
Fragment f = manager.findFragmentByTag(names.get(i));
if (f != null) {
if (DEBUG) logger.info("instantiateItem(): attach"); // show can be used instead
transaction.attach(f);
} else {
return buffers.get(pos-1);
f = fragments.get(i);
if (DEBUG) logger.info("instantiateItem(): add");
transaction.add(container.getId(), f, names.get(i));
}
return f;
}

/** this can be called either when a fragment has been removed by closeBuffer or when it's
** getting off-screen. in the first case the fragment will still be in this.fragments */
@Override
public CharSequence getPageTitle(int pos) {
// Tablet view
if (bufferListFragment==null) {
String title = buffers.get(pos).getShortBufferName();
if (title==null || title.equals("")){
return buffers.get(pos).getBufferName();
}
return title;
}

// Phone view
if (pos==0) {
return "Buffer List";
public void destroyItem(ViewGroup container, int i, Object object) {
if (DEBUG) logger.info("destroyItem(..., {}, {})", i, object);
if (transaction == null)
transaction = manager.beginTransaction();
if (fragments.size() > i && fragments.get(i) == object) {
if (DEBUG) logger.info("destroyItem(): detach"); // hide can be used instead
transaction.detach((Fragment) object);
} else {
String title = buffers.get(pos-1).getShortBufferName();
if (title==null || title.equals("")){
return buffers.get(pos-1).getBufferName();
}
return title;
if (DEBUG) logger.info("destroyItem(): remove");
transaction.remove((Fragment) object);
}
}

public void closeBuffer(String buffer) {
int i=0;
Iterator<BufferFragment> iter = buffers.iterator();
while(iter.hasNext()) {
BufferFragment bf = iter.next();
if (bf.getBufferName().equals(buffer)) {
// TODO: wrong thread error for pager.setCurrentItem
pager.setCurrentItem(i);
iter.remove();
}
i++;
}
notifyDataSetChanged();

@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
public void openBuffer(String buffer) {
// Find the appropriate buffer in our list
for (int i=0;i<buffers.size(); i++) {
BufferFragment bf = buffers.get(i);
if (bf.getBufferName().equals(buffer)) {

if (bufferListFragment == null) {
pager.setCurrentItem(i);
} else {
pager.setCurrentItem(i+1);
}
// TODO: update title?
return;
}
}

// Create fragment for the buffer and setup the arguments
BufferFragment newFragment = new BufferFragment();
Bundle args = new Bundle();
args.putString("buffer", buffer);
newFragment.setArguments(args);
buffers.add(newFragment);
notifyDataSetChanged();
pager.setCurrentItem(buffers.size());

/** 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 buffer list change */
@Override
public int getItemPosition(Object object) {
int idx = fragments.indexOf(object);
if (DEBUG) logger.info("getItemPosition(...) -> {}", (idx >= 0) ? idx : POSITION_NONE);
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) {}

/** this function, too, is called way too frequently */
@Override
public void finishUpdate(ViewGroup container) {
if (transaction == null)
return;
transaction.commitAllowingStateLoss();
transaction = null;
manager.executePendingTransactions();
}

@Override
public CharSequence getPageTitle(int i) {
return (phone_mode && i == 0) ? "Buffer List" : ((BufferFragment) fragments.get(i)).getShortBufferName();
}

public BufferFragment getCurrentBuffer() {
int pos = pager.getCurrentItem();
if (bufferListFragment == null) {
// Tablet view
if (pos>=0 && pos < buffers.size()) {
return buffers.get(pos);
}
/** switch to already open buffer OR create a new buffer, putting it into BOTH names and fragments,
** run notifyDataSetChanged() which will in turn call instantiateItem(), and set new buffer as the current one */
public void openBuffer(String name) {
if (DEBUG) logger.info("openBuffer({})", name);
int idx = names.indexOf(name);
if (idx >= 0) {
// found buffer by name, switch to it
pager.setCurrentItem(idx);
} else {
// Phone view
if (pos>=1 && pos <= buffers.size()) {
return buffers.get(pos-1);
}
// create a new one
Fragment f = new BufferFragment();
Bundle args = new Bundle();
args.putString("buffer", name);
f.setArguments(args);
fragments.add(f);
names.add(name);
notifyDataSetChanged();
pager.setCurrentItem(names.size());
}
return null;
}


/** close buffer if open, removing it from BOTH names and fragments.
** destroyItem() checks the lists to see if it has to remove the item for good */
public void closeBuffer(String name) {
if (DEBUG) logger.info("closeBuffer({})", name);
int idx = names.indexOf(name);
if (idx >= 0) {
names.remove(idx);
fragments.remove(idx);
notifyDataSetChanged();
}
}

/** returns BufferFragment that is currently focused
** or null if nothing or BufferListFragment is focused */
public BufferFragment getCurrentBuffer() {
int i = pager.getCurrentItem();
if ((phone_mode && i == 0) || fragments.size() == 0)
return null;
else
return (BufferFragment) fragments.get(i);
}

/** the following two methods magically get called on application recreation,
** so put all our save/restore state here */
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
public Parcelable saveState() {
if (DEBUG) logger.info("saveState()");
if (fragments.size() == 0)
return null;
Bundle state = new Bundle();
state.putStringArrayList("\0", names);
for (int i = 0, size = names.size(); i < size; i++)
manager.putFragment(state, names.get(i), fragments.get(i));
return state;
}

@Override
public void restoreState(Parcelable parcel, ClassLoader loader) {
if (DEBUG) logger.info("restoreState()");
if (parcel == null)
return;
Bundle state = (Bundle) parcel;
state.setClassLoader(loader);
names = state.getStringArrayList("\0");
if (names.size() > 0) {
for (String name : names)
fragments.add(manager.getFragment(state, name));
phone_mode = names.get(0).equals("");
notifyDataSetChanged();
}
}

}

0 comments on commit dd5cd98

Please sign in to comment.