Replace gallery view for conversations by ViewPager. Fixes #38.

This commit is contained in:
Sebastian Kaspari 2012-01-21 08:56:40 +01:00
parent 659d6c0b89
commit e04499070a
8 changed files with 226 additions and 490 deletions

View File

@ -38,7 +38,7 @@ along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
android:src="@drawable/disconnected"/> android:src="@drawable/disconnected"/>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="3dp" android:paddingLeft="3dp"
android:textSize="12sp" android:textSize="12sp"
@ -46,20 +46,15 @@ along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
android:maxLines="1" android:maxLines="1"
android:layout_weight="1" /> android:layout_weight="1" />
</LinearLayout> </LinearLayout>
<ViewSwitcher <android.support.v4.view.ViewPager
android:id="@+id/switcher" android:id="@+id/pager"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"> android:layout_weight="1"
<org.yaaic.view.ConversationGallery android:layout_margin="0px"
android:id="@+id/deck" android:unselectedAlpha="100"
android:layout_width="fill_parent" android:spacing="5dp"
android:layout_height="fill_parent" android:padding="0dp" />
android:layout_margin="0px"
android:unselectedAlpha="100"
android:spacing="5dp"
android:padding="0dp" />
</ViewSwitcher>
<org.yaaic.view.ConversationSwitcher <org.yaaic.view.ConversationSwitcher
android:id="@+id/dots" android:id="@+id/dots"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -70,7 +65,7 @@ along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
android:orientation="horizontal"> android:orientation="horizontal">
<EditText <EditText
android:id="@+id/input" android:id="@+id/input"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:inputType="textImeMultiLine" android:inputType="textImeMultiLine"

View File

@ -26,14 +26,13 @@ import java.util.List;
import org.yaaic.R; import org.yaaic.R;
import org.yaaic.Yaaic; import org.yaaic.Yaaic;
import org.yaaic.adapter.DeckAdapter; import org.yaaic.adapter.ConversationPagerAdapter;
import org.yaaic.adapter.MessageListAdapter; import org.yaaic.adapter.MessageListAdapter;
import org.yaaic.command.CommandParser; import org.yaaic.command.CommandParser;
import org.yaaic.irc.IRCBinder; import org.yaaic.irc.IRCBinder;
import org.yaaic.irc.IRCConnection; import org.yaaic.irc.IRCConnection;
import org.yaaic.irc.IRCService; import org.yaaic.irc.IRCService;
import org.yaaic.layout.NonScalingBackgroundDrawable; import org.yaaic.layout.NonScalingBackgroundDrawable;
import org.yaaic.listener.ConversationClickListener;
import org.yaaic.listener.ConversationListener; import org.yaaic.listener.ConversationListener;
import org.yaaic.listener.ConversationSelectedListener; import org.yaaic.listener.ConversationSelectedListener;
import org.yaaic.listener.ServerListener; import org.yaaic.listener.ServerListener;
@ -53,7 +52,6 @@ import org.yaaic.model.User;
import org.yaaic.receiver.ConversationReceiver; import org.yaaic.receiver.ConversationReceiver;
import org.yaaic.receiver.ServerReceiver; import org.yaaic.receiver.ServerReceiver;
import org.yaaic.view.ConversationSwitcher; import org.yaaic.view.ConversationSwitcher;
import org.yaaic.view.MessageListView;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -69,28 +67,27 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.speech.RecognizerIntent; import android.speech.RecognizerIntent;
import android.support.v4.view.ViewPager;
import android.text.InputType; import android.text.InputType;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnKeyListener;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.View.OnKeyListener;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Gallery;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.ViewSwitcher;
/** /**
* The server view with a scrollable list of all channels * The server view with a scrollable list of all channels
* *
* @author Sebastian Kaspari <sebastian@yaaic.org> * @author Sebastian Kaspari <sebastian@yaaic.org>
*/ */
public class ConversationActivity extends Activity implements ServiceConnection, ServerListener, ConversationListener public class ConversationActivity extends Activity implements ServiceConnection, ServerListener, ConversationListener
@ -108,9 +105,9 @@ public class ConversationActivity extends Activity implements ServiceConnection,
private ConversationReceiver channelReceiver; private ConversationReceiver channelReceiver;
private ServerReceiver serverReceiver; private ServerReceiver serverReceiver;
private ViewSwitcher switcher; private ViewPager pager;
private Gallery deck; private ConversationPagerAdapter pagerAdapter;
private DeckAdapter deckAdapter;
private Scrollback scrollback; private Scrollback scrollback;
private ConversationSwitcher dots; private ConversationSwitcher dots;
@ -202,23 +199,32 @@ public class ConversationActivity extends Activity implements ServiceConnection,
EditText input = (EditText) findViewById(R.id.input); EditText input = (EditText) findViewById(R.id.input);
input.setOnKeyListener(inputKeyListener); input.setOnKeyListener(inputKeyListener);
switcher = (ViewSwitcher) findViewById(R.id.switcher); pager = (ViewPager) findViewById(R.id.pager);
dots = (ConversationSwitcher) findViewById(R.id.dots); dots = (ConversationSwitcher) findViewById(R.id.dots);
dots.setServer(server); dots.setServer(server);
deckAdapter = new DeckAdapter(); pagerAdapter = new ConversationPagerAdapter();
deck = (Gallery) findViewById(R.id.deck); pager.setAdapter(pagerAdapter);
deck.setOnItemSelectedListener(new ConversationSelectedListener(this, server, (TextView) findViewById(R.id.title), dots)); pager.setPageMargin(5);
deck.setAdapter(deckAdapter);
deck.setOnItemClickListener(new ConversationClickListener(deckAdapter, switcher)); pager.setOnPageChangeListener(
deck.setBackgroundDrawable(new NonScalingBackgroundDrawable(this, deck, R.drawable.background)); new ConversationSelectedListener(
this,
server,
(TextView) findViewById(R.id.title),
pagerAdapter,
dots
)
);
pager.setBackgroundDrawable(new NonScalingBackgroundDrawable(this, pager, R.drawable.background));
historySize = settings.getHistorySize(); historySize = settings.getHistorySize();
if (server.getStatus() == Status.PRE_CONNECTING) { if (server.getStatus() == Status.PRE_CONNECTING) {
server.clearConversations(); server.clearConversations();
deckAdapter.clearConversations(); pagerAdapter.clearConversations();
server.getConversation(ServerInfo.DEFAULT_NAME).setHistorySize(historySize); server.getConversation(ServerInfo.DEFAULT_NAME).setHistorySize(historySize);
} }
@ -313,14 +319,14 @@ public class ConversationActivity extends Activity implements ServiceConnection,
// Fill view with messages that have been buffered while paused // Fill view with messages that have been buffered while paused
for (Conversation conversation : mConversations) { for (Conversation conversation : mConversations) {
String name = conversation.getName(); String name = conversation.getName();
mAdapter = deckAdapter.getItemAdapter(name); mAdapter = pagerAdapter.getItemAdapter(name);
if (mAdapter != null) { if (mAdapter != null) {
mAdapter.addBulkMessages(conversation.getBuffer()); mAdapter.addBulkMessages(conversation.getBuffer());
conversation.clearBuffer(); conversation.clearBuffer();
} else { } else {
// Was conversation created while we were paused? // Was conversation created while we were paused?
if (deckAdapter.getPositionByName(name) == -1) { if (pagerAdapter.getPositionByName(name) == -1) {
onNewConversation(name); onNewConversation(name);
} }
} }
@ -336,11 +342,11 @@ public class ConversationActivity extends Activity implements ServiceConnection,
} }
// Remove views for conversations that ended while we were paused // Remove views for conversations that ended while we were paused
int numViews = deckAdapter.getCount(); int numViews = pagerAdapter.getCount();
if (numViews > mConversations.size()) { if (numViews > mConversations.size()) {
for (int i = 0; i < numViews; ++i) { for (int i = 0; i < numViews; ++i) {
if (!mConversations.contains(deckAdapter.getItem(i))) { if (!mConversations.contains(pagerAdapter.getItem(i))) {
deckAdapter.removeItem(i--); pagerAdapter.removeConversation(i--);
--numViews; --numViews;
} }
} }
@ -379,33 +385,6 @@ public class ConversationActivity extends Activity implements ServiceConnection,
unregisterReceiver(serverReceiver); unregisterReceiver(serverReceiver);
} }
/**
* On save instance state (e.g. before a configuration change)
*/
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if (deckAdapter.isSwitched()) {
outState.putBoolean("isSwitched", deckAdapter.isSwitched());
outState.putString("switchedName", deckAdapter.getSwitchedName());
}
}
/**
* On restore instance state (e.g. after a configuration change)
*/
@Override
protected void onRestoreInstanceState(Bundle inState)
{
super.onRestoreInstanceState(inState);
if (inState.getBoolean("isSwitched")) {
deckAdapter.setSwitched(inState.getString("switchedName"), null);
}
}
/** /**
* On service connected * On service connected
*/ */
@ -477,7 +456,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
break; break;
case R.id.close: case R.id.close:
Conversation conversationToClose = deckAdapter.getItem(deck.getSelectedItemPosition()); Conversation conversationToClose = pagerAdapter.getItem(pager.getCurrentItem());
// Make sure we part a channel when closing the channel conversation // Make sure we part a channel when closing the channel conversation
if (conversationToClose.getType() == Conversation.TYPE_CHANNEL) { if (conversationToClose.getType() == Conversation.TYPE_CHANNEL) {
binder.getService().getConnection(serverId).partChannel(conversationToClose.getName()); binder.getService().getConnection(serverId).partChannel(conversationToClose.getName());
@ -495,15 +474,15 @@ public class ConversationActivity extends Activity implements ServiceConnection,
break; break;
case R.id.users: case R.id.users:
Conversation conversationForUserList = deckAdapter.getItem(deck.getSelectedItemPosition()); Conversation conversationForUserList = pagerAdapter.getItem(pager.getCurrentItem());
if (conversationForUserList.getType() == Conversation.TYPE_CHANNEL) { if (conversationForUserList.getType() == Conversation.TYPE_CHANNEL) {
Intent intent = new Intent(this, UsersActivity.class); Intent intent = new Intent(this, UsersActivity.class);
intent.putExtra( intent.putExtra(
Extra.USERS, Extra.USERS,
binder.getService().getConnection(server.getId()).getUsersAsStringArray( binder.getService().getConnection(server.getId()).getUsersAsStringArray(
conversationForUserList.getName() conversationForUserList.getName()
) )
); );
startActivityForResult(intent, REQUEST_CODE_USERS); startActivityForResult(intent, REQUEST_CODE_USERS);
} else { } else {
Toast.makeText(this, getResources().getString(R.string.only_usable_from_channel), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getResources().getString(R.string.only_usable_from_channel), Toast.LENGTH_SHORT).show();
@ -516,7 +495,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
/** /**
* Get server object assigned to this activity * Get server object assigned to this activity
* *
* @return the server object * @return the server object
*/ */
public Server getServer() public Server getServer()
@ -538,7 +517,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
return; return;
} }
MessageListAdapter adapter = deckAdapter.getItemAdapter(target); MessageListAdapter adapter = pagerAdapter.getItemAdapter(target);
while(conversation.hasBufferedMessages()) { while(conversation.hasBufferedMessages()) {
Message message = conversation.pollBufferedMessage(); Message message = conversation.pollBufferedMessage();
@ -574,14 +553,18 @@ public class ConversationActivity extends Activity implements ServiceConnection,
{ {
createNewConversation(target); createNewConversation(target);
if (!deckAdapter.isSwitched()) { pager.setCurrentItem(pagerAdapter.getCount() - 1);
// Scroll to new conversation
deck.setSelection(deckAdapter.getCount() - 1);
}
} }
/**
* Create a new conversation in the pager adapter for the
* given target conversation.
*
* @param target
*/
public void createNewConversation(String target) public void createNewConversation(String target)
{ {
deckAdapter.addItem(server.getConversation(target)); pagerAdapter.addConversation(server.getConversation(target));
} }
/** /**
@ -590,18 +573,17 @@ public class ConversationActivity extends Activity implements ServiceConnection,
@Override @Override
public void onRemoveConversation(String target) public void onRemoveConversation(String target)
{ {
deckAdapter.removeItem(target); int position = pagerAdapter.getPositionByName(target);
if (deckAdapter.isSwitched()) { if (position != -1) {
switcher.showNext(); pagerAdapter.removeConversation(position);
switcher.removeView(deckAdapter.getSwitchedView());
deckAdapter.setSwitched(null, null);
} }
} }
/** /**
* On topic change * On topic change
*/ */
@Override
public void onTopicChanged(String target) public void onTopicChanged(String target)
{ {
String selected = server.getSelectedConversation(); String selected = server.getSelectedConversation();
@ -654,8 +636,8 @@ public class ConversationActivity extends Activity implements ServiceConnection,
return; return;
} }
binder.getService().getConnection(server.getId()).setAutojoinChannels( binder.getService().getConnection(server.getId()).setAutojoinChannels(
server.getCurrentChannelNames() server.getCurrentChannelNames()
); );
server.setStatus(Status.CONNECTING); server.setStatus(Status.CONNECTING);
binder.connect(server); binder.connect(server);
reconnectDialogActive = false; reconnectDialogActive = false;
@ -675,25 +657,6 @@ public class ConversationActivity extends Activity implements ServiceConnection,
} }
} }
/**
* On key down
*
* XXX: As we only track the back key: Android >= 2.0 will call a method called onBackPressed()
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
if (deckAdapter.isSwitched()) {
MessageListView canvas = (MessageListView) deckAdapter.getView(deckAdapter.getPositionByName(deckAdapter.getSwitchedName()), null, switcher);
canvas.setSwitched(false);
deckAdapter.setSwitched(null, null);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
/** /**
* On activity result * On activity result
*/ */
@ -746,11 +709,11 @@ public class ConversationActivity extends Activity implements ServiceConnection,
String nicknameWithoutPrefix = nickname; String nicknameWithoutPrefix = nickname;
while ( while (
nicknameWithoutPrefix.startsWith("@") || nicknameWithoutPrefix.startsWith("@") ||
nicknameWithoutPrefix.startsWith("+") || nicknameWithoutPrefix.startsWith("+") ||
nicknameWithoutPrefix.startsWith(".") || nicknameWithoutPrefix.startsWith(".") ||
nicknameWithoutPrefix.startsWith("%") nicknameWithoutPrefix.startsWith("%")
) { ) {
// Strip prefix(es) now // Strip prefix(es) now
nicknameWithoutPrefix = nicknameWithoutPrefix.substring(1); nicknameWithoutPrefix = nicknameWithoutPrefix.substring(1);
} }
@ -776,10 +739,10 @@ public class ConversationActivity extends Activity implements ServiceConnection,
server.addConversation(query); server.addConversation(query);
Intent intent = Broadcast.createConversationIntent( Intent intent = Broadcast.createConversationIntent(
Broadcast.CONVERSATION_NEW, Broadcast.CONVERSATION_NEW,
server.getId(), server.getId(),
nicknameWithoutPrefix nicknameWithoutPrefix
); );
binder.getService().sendBroadcast(intent); binder.getService().sendBroadcast(intent);
} }
break; break;
@ -830,7 +793,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
scrollback.addMessage(text); scrollback.addMessage(text);
Conversation conversation = deckAdapter.getItem(deck.getSelectedItemPosition()); Conversation conversation = pagerAdapter.getItem(pager.getCurrentItem());
if (conversation != null) { if (conversation != null) {
if (!text.trim().startsWith("/")) { if (!text.trim().startsWith("/")) {
@ -907,14 +870,14 @@ public class ConversationActivity extends Activity implements ServiceConnection,
} }
// Log.d("Yaaic", "Trying to complete nick: " + word); // Log.d("Yaaic", "Trying to complete nick: " + word);
Conversation conversationForUserList = deckAdapter.getItem(deck.getSelectedItemPosition()); Conversation conversationForUserList = pagerAdapter.getItem(pager.getCurrentItem());
String[] users = null; String[] users = null;
if (conversationForUserList.getType() == Conversation.TYPE_CHANNEL) { if (conversationForUserList.getType() == Conversation.TYPE_CHANNEL) {
users = binder.getService().getConnection(server.getId()).getUsersAsStringArray( users = binder.getService().getConnection(server.getId()).getUsersAsStringArray(
conversationForUserList.getName() conversationForUserList.getName()
); );
} }
// go through users and add matches // go through users and add matches
@ -987,7 +950,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
/** /**
* Remove the status char off the front of a nick if one is present * Remove the status char off the front of a nick if one is present
* *
* @param nick * @param nick
* @return nick without statuschar * @return nick without statuschar
*/ */
@ -995,7 +958,7 @@ public class ConversationActivity extends Activity implements ServiceConnection,
{ {
/* Discard status characters */ /* Discard status characters */
if (nick.startsWith("@") || nick.startsWith("+") if (nick.startsWith("@") || nick.startsWith("+")
|| nick.startsWith("%")) { || nick.startsWith("%")) {
nick = nick.substring(1); nick = nick.substring(1);
} }
return nick; return nick;

View File

@ -17,31 +17,33 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Yaaic. If not, see <http://www.gnu.org/licenses/>. along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.yaaic.adapter; package org.yaaic.adapter;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import org.yaaic.listener.MessageClickListener; import org.yaaic.listener.MessageClickListener;
import org.yaaic.model.Conversation; import org.yaaic.model.Conversation;
import org.yaaic.view.MessageListView; import org.yaaic.view.MessageListView;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
/** /**
* The adapter for the "DeckView" * Adapter for displaying a pager of conversations.
* *
* @author Sebastian Kaspari <sebastian@yaaic.org> * @author Sebastian Kaspari <sebastian@yaaic.org>
*/ */
public class DeckAdapter extends BaseAdapter public class ConversationPagerAdapter extends PagerAdapter
{ {
private LinkedList<ConversationInfo> conversations; private LinkedList<ConversationInfo> conversations;
private MessageListView currentView; private final HashMap<Integer, View> views;
private String currentChannel;
/**
* Container class to remember conversation and view association.
*/
public class ConversationInfo { public class ConversationInfo {
public Conversation conv; public Conversation conv;
public MessageListAdapter adapter; public MessageListAdapter adapter;
@ -55,44 +57,51 @@ public class DeckAdapter extends BaseAdapter
} }
/** /**
* Create a new DeckAdapter instance * Create a new {@link ConversationPagerAdapter} instance.
*/ */
public DeckAdapter() public ConversationPagerAdapter() {
{
conversations = new LinkedList<ConversationInfo>(); conversations = new LinkedList<ConversationInfo>();
views = new HashMap<Integer, View>();
} }
/** /**
* Clear conversations * Add a conversation to the adapter.
*
* @param conversation
*/ */
public void clearConversations() public void addConversation(Conversation conversation) {
{ conversations.add(new ConversationInfo(conversation));
conversations = new LinkedList<ConversationInfo>();
notifyDataSetChanged();
} }
/** /**
* Get number of item * Remove the conversation at the given position from the adapter.
*
* @param position
*/
public void removeConversation(int position) {
conversations.remove(position);
notifyDataSetChanged();
}
/**
* Get position of given item.
*/ */
@Override @Override
public int getCount() public int getItemPosition(Object object)
{ {
return conversations.size(); if (views.containsKey(object)) {
} return POSITION_UNCHANGED;
/**
* Get ConversationInfo on item at position
*/
private ConversationInfo getItemInfo(int position) {
if (position >= 0 && position < conversations.size()) {
return conversations.get(position);
} }
return null;
return POSITION_NONE;
} }
/** /**
* Get item at position * Get item at position
*/ */
@Override
public Conversation getItem(int position) public Conversation getItem(int position)
{ {
ConversationInfo convInfo = getItemInfo(position); ConversationInfo convInfo = getItemInfo(position);
@ -104,11 +113,13 @@ public class DeckAdapter extends BaseAdapter
} }
/** /**
* Get MessageListAdapter belonging to a conversation * Get the adapter of the {@link MessageListView} at the given position.
* *
* @param position Position of the conversation in the deck * @param position
* @return
*/ */
public MessageListAdapter getItemAdapter(int position) { public MessageListAdapter getItemAdapter(int position)
{
ConversationInfo convInfo = getItemInfo(position); ConversationInfo convInfo = getItemInfo(position);
if (convInfo != null) { if (convInfo != null) {
return convInfo.adapter; return convInfo.adapter;
@ -118,39 +129,32 @@ public class DeckAdapter extends BaseAdapter
} }
/** /**
* Get MessageListAdapter belonging to a conversation * Get the adapter of the {@link MessageListView} for the conversation
* with the given name.
* *
* @param name Name of the conversation * @param name
* @return
*/ */
public MessageListAdapter getItemAdapter(String name) { public MessageListAdapter getItemAdapter(String name)
{
return getItemAdapter(getPositionByName(name)); return getItemAdapter(getPositionByName(name));
} }
/** /**
* Get id of item at position * Get ConversationInfo on item at position
*
* @param position
*/ */
@Override private ConversationInfo getItemInfo(int position) {
public long getItemId(int position) if (position >= 0 && position < conversations.size()) {
{ return conversations.get(position);
return position; }
} return null;
/**
* Add an item
*
* @param channel Name of the channel
* @param view The view object
*/
public void addItem(Conversation conversation)
{
conversations.add(new ConversationInfo(conversation));
notifyDataSetChanged();
} }
/** /**
* Get an item by the channel's name * Get an item by the channel's name
* *
* @param channel * @param channel
* @return The item * @return The item
*/ */
@ -170,102 +174,62 @@ public class DeckAdapter extends BaseAdapter
} }
/** /**
* Remove an item * Remove all conversations.
*
* @param position
*/ */
public void removeItem(int position) public void clearConversations()
{ {
if (position >= 0 && position < conversations.size()) { conversations = new LinkedList<ConversationInfo>();
conversations.remove(position);
notifyDataSetChanged();
}
} }
/** /**
* Remove an item * Get number of conversations from this adapter.
*
* @param target
*/
public void removeItem(String target)
{
removeItem(getPositionByName(target));
}
/**
* Set single channel view
*
* @param switched
*/
public void setSwitched(String channel, MessageListView current)
{
currentChannel = channel;
currentView = current;
}
/**
* Get single channel view
*
* @return
*/
public MessageListView getSwitchedView()
{
return currentView;
}
/**
* Get name of channel (single channel view)
*
* @return
*/
public String getSwitchedName()
{
return currentChannel;
}
/**
* Has the view been switched to single channel view?
*
* @return view true if view is in single channel view, false otherwise
*/
public boolean isSwitched()
{
return currentChannel != null;
}
/**
* Get view at given position
*/ */
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) public int getCount()
{ {
ConversationInfo convInfo = getItemInfo(position); return conversations.size();
// Market stack traces prove that sometimes we get a null converstion
// because the collection changed while a view is requested for an
// item that does not exist anymore... so we just need to reply with
// some kind of view here.
if (convInfo == null || convInfo.conv == null) {
return new TextView(parent.getContext());
}
if (convInfo.view != null) {
return convInfo.view;
} else {
return renderConversation(convInfo, parent);
}
} }
/** /**
* Render a conversation view (MessageListView) * Determines whether a page View is associated with a specific key object.
*
* @param channel The conversation of the view
* @param parent The parent view (context)
* @return The rendered MessageListView
*/ */
private MessageListView renderConversation(ConversationInfo convInfo, ViewGroup parent) @Override
public boolean isViewFromObject(View view, Object object)
{ {
MessageListView list = new MessageListView(parent.getContext(), parent); return view == object;
}
/**
* Create a view object for the conversation at the given position.
*/
@Override
public Object instantiateItem(View collection, int position) {
// ConversationInfo convInfo = getItemInfo(position);
ConversationInfo convInfo = conversations.get(position);
View view;
if (convInfo.view != null) {
view = convInfo.view;
} else {
view = renderConversation(convInfo, collection);
}
views.put(position, view);
((ViewPager) collection).addView(view);
return view;
}
/**
* Render the given conversation and return the new view.
*
* @param convInfo
* @param parent
* @return
*/
private MessageListView renderConversation(ConversationInfo convInfo, View parent)
{
MessageListView list = new MessageListView(parent.getContext());
convInfo.view = list; convInfo.view = list;
list.setOnItemClickListener(MessageClickListener.getInstance()); list.setOnItemClickListener(MessageClickListener.getInstance());
@ -279,11 +243,15 @@ public class DeckAdapter extends BaseAdapter
list.setAdapter(adapter); list.setAdapter(adapter);
list.setSelection(adapter.getCount() - 1); // scroll to bottom list.setSelection(adapter.getCount() - 1); // scroll to bottom
if (convInfo.conv.getName().equals(currentChannel)) {
list.setSwitched(true);
currentView = list;
}
return list; return list;
} }
/**
* Remove the given view from the adapter and collection.
*/
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
views.remove(position);
}
} }

View File

@ -1,66 +0,0 @@
/*
Yaaic - Yet Another Android IRC Client
Copyright 2009-2011 Sebastian Kaspari
This file is part of Yaaic.
Yaaic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Yaaic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.yaaic.listener;
import org.yaaic.adapter.DeckAdapter;
import org.yaaic.model.Conversation;
import org.yaaic.view.MessageListView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ViewSwitcher;
/**
* Listener for clicks on conversations
*
* @author Sebastian Kaspari <sebastian@yaaic.org>
*/
public class ConversationClickListener implements OnItemClickListener
{
private final DeckAdapter adapter;
private final ViewSwitcher switcher;
/**
* Create a new ConversationClickListener
*
* @param adapter
* @param switcher
*/
public ConversationClickListener(DeckAdapter adapter, ViewSwitcher switcher)
{
this.adapter = adapter;
this.switcher = switcher;
}
/**
* On conversation item clicked
*/
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id)
{
Conversation conversation = adapter.getItem(position);
MessageListView canvas = (MessageListView) adapter.getView(position, null, switcher);
canvas.setSwitched(true);
adapter.setSwitched(conversation.getName(), canvas);
}
}

View File

@ -20,61 +20,63 @@ along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.yaaic.listener; package org.yaaic.listener;
import org.yaaic.adapter.ConversationPagerAdapter;
import org.yaaic.irc.IRCService;
import org.yaaic.model.Channel; import org.yaaic.model.Channel;
import org.yaaic.model.Conversation; import org.yaaic.model.Conversation;
import org.yaaic.model.Server; import org.yaaic.model.Server;
import org.yaaic.view.ConversationSwitcher; import org.yaaic.view.ConversationSwitcher;
import org.yaaic.irc.IRCService;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.widget.TextView;
/** /**
* Listener for conversation selections * Listener for conversation selections
* *
* @author Sebastian Kaspari <sebastian@yaaic.org> * @author Sebastian Kaspari <sebastian@yaaic.org>
*/ */
public class ConversationSelectedListener implements OnItemSelectedListener public class ConversationSelectedListener implements OnPageChangeListener
{ {
private final Context ctx; private final Context ctx;
private final Server server; private final Server server;
private final TextView titleView; private final TextView titleView;
private final ConversationSwitcher switcher; private final ConversationSwitcher switcher;
private final ConversationPagerAdapter adapter;
/** /**
* Create a new ConversationSelectedListener * Create a new ConversationSelectedListener
* *
* @param server * @param server
* @param titleView * @param titleView
*/ */
public ConversationSelectedListener(Context ctx, Server server, TextView titleView, ConversationSwitcher switcher) public ConversationSelectedListener(Context ctx, Server server, TextView titleView, ConversationPagerAdapter adapter, ConversationSwitcher switcher)
{ {
this.ctx = ctx; this.ctx = ctx;
this.server = server; this.server = server;
this.titleView = titleView; this.titleView = titleView;
this.switcher = switcher; this.switcher = switcher;
this.adapter = adapter;
} }
/** /**
* On conversation selected/focused * On page has been selected.
*/ */
@Override @Override
public void onItemSelected(AdapterView<?> deck, View view, int position, long id) public void onPageSelected(int position)
{ {
Conversation conversation = (Conversation) deck.getItemAtPosition(position); Conversation conversation = adapter.getItem(position);
if (conversation != null && conversation.getType() != Conversation.TYPE_SERVER) { if (conversation != null && conversation.getType() != Conversation.TYPE_SERVER) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(server.getTitle() + " - " + conversation.getName()); sb.append(server.getTitle() + " - " + conversation.getName());
if (conversation.getType() == Conversation.TYPE_CHANNEL && !((Channel)conversation).getTopic().equals("")) if (conversation.getType() == Conversation.TYPE_CHANNEL && !((Channel)conversation).getTopic().equals("")) {
sb.append(" - " + ((Channel)conversation).getTopic()); sb.append(" - " + ((Channel)conversation).getTopic());
}
titleView.setText(sb.toString()); titleView.setText(sb.toString());
} else { } else {
onNothingSelected(deck); titleView.setText(server.getTitle());
} }
// Remember selection // Remember selection
@ -101,11 +103,18 @@ public class ConversationSelectedListener implements OnItemSelectedListener
} }
/** /**
* On no conversation selected/focused * On scroll state of pager has been chanaged.
*/ */
@Override @Override
public void onNothingSelected(AdapterView<?> deck) public void onPageScrollStateChanged(int state)
{
}
/**
* On page has been scrolled.
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{ {
titleView.setText(server.getTitle());
} }
} }

View File

@ -1,60 +0,0 @@
/*
Yaaic - Yet Another Android IRC Client
Copyright 2009-2011 Sebastian Kaspari
This file is part of Yaaic.
Yaaic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Yaaic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Yaaic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.yaaic.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Gallery;
/**
* Conversation gallery for horizontal scrolling
*
* @author Thomas Martitz
*/
public class ConversationGallery extends Gallery
{
/**
* Create a new conversation gallery
*
* @param context
* @param attrs
*/
public ConversationGallery(Context context, AttributeSet attrs)
{
super(context, attrs);
}
/**
* On Fling: Reduce sensitivity of the channel gallery view
*/
@Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
/* Reduce sensitivity based on f(x) = x * 0.925^y with y = (|x/500| - 1)
* The goal is to reduce higher velocities stronger than low ones
* 500 is the base, i.e. it will not reduced. */
final float fexp = Math.abs(velocityX / 500.0f) - 1;
velocityX *= Math.pow(0.925, fexp);
return super.onFling(e1, e2, velocityX, velocityY);
}
}

View File

@ -33,7 +33,7 @@ import android.view.View;
/** /**
* The ConversationSwitcher - The small funny dots at the bottom ;) * The ConversationSwitcher - The small funny dots at the bottom ;)
* *
* @author Sebastian Kaspari <sebastian@yaaic.org> * @author Sebastian Kaspari <sebastian@yaaic.org>
*/ */
public class ConversationSwitcher extends View public class ConversationSwitcher extends View
@ -43,7 +43,7 @@ public class ConversationSwitcher extends View
/** /**
* Create a new ConversationSwitcher * Create a new ConversationSwitcher
* *
* @param context The Context the view is running in, through which it can access the current theme, resources, etc. * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
*/ */
public ConversationSwitcher(Context context, AttributeSet attributes) public ConversationSwitcher(Context context, AttributeSet attributes)
@ -56,7 +56,7 @@ public class ConversationSwitcher extends View
/** /**
* Set the server whos conversations should be displayed * Set the server whos conversations should be displayed
* *
* @param server * @param server
*/ */
public void setServer(Server server) public void setServer(Server server)
@ -66,7 +66,7 @@ public class ConversationSwitcher extends View
/** /**
* Measure the size of the view * Measure the size of the view
* *
* @param widthMeasureSpec * @param widthMeasureSpec
* @param heightMeasureSpec * @param heightMeasureSpec
*/ */

View File

@ -25,46 +25,27 @@ import org.yaaic.adapter.MessageListAdapter;
import org.yaaic.listener.MessageClickListener; import org.yaaic.listener.MessageClickListener;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Gallery;
import android.widget.ListView; import android.widget.ListView;
/** /**
* A customized ListView for Messages * A customized ListView for Messages
* *
* @author Sebastian Kaspari <sebastian@yaaic.org> * @author Sebastian Kaspari <sebastian@yaaic.org>
*/ */
public class MessageListView extends ListView public class MessageListView extends ListView
{ {
private boolean switched = false;
private final View parent;
private int parentWidth;
private int parentHeight;
private final int padding;
private final int paddingWide;
/** /**
* Create a new MessageListView * Create a new MessageListView
* *
* @param context * @param context
*/ */
public MessageListView(Context context, View parent) public MessageListView(Context context)
{ {
super(context); super(context);
this.parent = parent;
setOnItemClickListener(MessageClickListener.getInstance()); setOnItemClickListener(MessageClickListener.getInstance());
parentWidth = parent.getWidth();
parentHeight = parent.getHeight();
setDivider(null); setDivider(null);
setLayoutParams(new Gallery.LayoutParams(
parentWidth*85/100,
parentHeight
));
setBackgroundResource(R.layout.rounded); setBackgroundResource(R.layout.rounded);
setCacheColorHint(0xee000000); setCacheColorHint(0xee000000);
@ -74,52 +55,17 @@ public class MessageListView extends ListView
// Scale padding by screen density // Scale padding by screen density
float density = context.getResources().getDisplayMetrics().density; float density = context.getResources().getDisplayMetrics().density;
padding = (int)(5 * density); int padding = (int) (5 * density);
paddingWide = (int)(12 * density);
setPadding(padding, padding, padding, 0); setPadding(padding, padding, padding, 0);
}
/** // XXX: This should be dynamically
* Handle touch screen motion events setTranscriptMode(TRANSCRIPT_MODE_ALWAYS_SCROLL);
*/
@Override
public boolean onTouchEvent(MotionEvent event)
{
if (!switched) {
// We delegate the touch events to the underlying view
return false;
} else {
return super.onTouchEvent(event);
}
}
/**
* On draw
*/
@Override
protected void onDraw(Canvas canvas)
{
if (parent.getWidth() != parentWidth || parent.getHeight() != parentHeight) {
// parent size changed, resizing this child too
parentWidth = parent.getWidth();
parentHeight = parent.getHeight();
if (!switched) {
setLayoutParams(new Gallery.LayoutParams(
parentWidth*85/100,
parentHeight
));
}
}
super.onDraw(canvas);
} }
/** /**
* Get the adapter of this MessageListView * Get the adapter of this MessageListView
* (Helper to avoid casting) * (Helper to avoid casting)
* *
* @return The MessageListAdapter * @return The MessageListAdapter
*/ */
@Override @Override
@ -127,23 +73,4 @@ public class MessageListView extends ListView
{ {
return (MessageListAdapter) super.getAdapter(); return (MessageListAdapter) super.getAdapter();
} }
/**
* Set whether this conversation is switched (taking up all of deck's space
* and handling touch events itself)
*/
public void setSwitched(boolean switched)
{
this.switched = switched;
if (switched) {
setLayoutParams(new Gallery.LayoutParams(Gallery.LayoutParams.FILL_PARENT, Gallery.LayoutParams.FILL_PARENT));
setTranscriptMode(TRANSCRIPT_MODE_NORMAL);
setPadding(paddingWide, padding, paddingWide, 0);
} else {
setLayoutParams(new Gallery.LayoutParams(parentWidth*85/100, parentHeight));
setTranscriptMode(TRANSCRIPT_MODE_ALWAYS_SCROLL);
setPadding(padding, padding, padding, 0);
}
}
} }