Commit e6b453db authored by Grigory Fedorov's avatar Grigory Fedorov

Merge branch 'feature/chat' into develop

parents bcbb1c98 3871a0d0
......@@ -8,8 +8,8 @@ android {
applicationId "com.xabber.androiddev"
minSdkVersion 14
targetSdkVersion 21
versionCode 107
versionName '0.10.10'
versionCode 113
versionName '0.10.16'
}
buildTypes {
......@@ -27,4 +27,5 @@ android {
dependencies {
compile files('libs/otr4j.jar')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:support-v13:21.0.3'
}
......@@ -110,16 +110,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="com.xabber.android.ui.preferences.AccountList" />
</activity>
<activity
android:label="@string/chat_list"
android:name="com.xabber.android.ui.ChatList"
android:parentActivityName="com.xabber.android.ui.ContactList"
>
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.xabber.android.ui.ContactList" />
</activity>
<activity
android:label="@string/occupant_list"
android:name="com.xabber.android.ui.OccupantList"
......
/**
Copyright (c) 2012 Antony Tran
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.antonyt.infiniteviewpager;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import com.xabber.android.ui.adapter.ChatViewerAdapter;
/**
* A {@link android.support.v4.view.ViewPager} that allows pseudo-infinite paging with a wrap-around effect.
*/
public class InfiniteViewPager extends ViewPager {
public static final int OFFSET = 100;
public static final int TOTAL_COUNT = 200;
public InfiniteViewPager(Context context) {
super(context);
}
public InfiniteViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setAdapter(PagerAdapter adapter) {
super.setAdapter(adapter);
// offset first element so that we can scroll to the left
setCurrentItem(0);
}
@Override
public void setCurrentItem(int item) {
// offset the current item to ensure there is space to scroll
setCurrentItem(item, false);
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {
item = getOffsetAmount() + (item % getAdapter().getCount());
super.setCurrentItem(item, smoothScroll);
}
@Override
public int getCurrentItem() {
int position = super.getCurrentItem();
// Return the actual item position in the data backing InfinitePagerAdapter
return position % ((ChatViewerAdapter)getAdapter()).getRealCount();
}
private int getOffsetAmount() {
// allow for 100 back cycles from the beginning
// should be enough to create an illusion of infinity
// warning: scrolling to very high values (1,000,000+) results in
// strange drawing behaviour
return OFFSET;
}
@Override
public void setOnPageChangeListener(OnPageChangeListener listener) {
super.setOnPageChangeListener(listener);
}
}
......@@ -14,20 +14,6 @@
*/
package com.xabber.android.data.message;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.packet.DelayInformation;
import android.database.Cursor;
import com.xabber.android.data.Application;
......@@ -45,6 +31,20 @@ import com.xabber.android.data.message.chat.ChatManager;
import com.xabber.android.data.notification.NotificationManager;
import com.xabber.xmpp.archive.SaveMode;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.packet.DelayInformation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Chat instance.
*
......@@ -109,6 +109,8 @@ public abstract class AbstractChat extends BaseEntity {
*/
protected final Collection<MessageItem> sendQuery;
protected Date creationTime = new Date();
protected AbstractChat(final String account, final String user) {
super(account, user);
threadId = StringUtils.randomString(12);
......@@ -120,6 +122,7 @@ public abstract class AbstractChat extends BaseEntity {
historyIds = new ArrayList<Long>();
messages = new ArrayList<MessageItem>();
sendQuery = new ArrayList<MessageItem>();
updateCreationTime();
Application.getInstance().runInBackground(new Runnable() {
@Override
......@@ -267,6 +270,10 @@ public abstract class AbstractChat extends BaseEntity {
}
void openChat() {
if (!active) {
updateCreationTime();
}
active = true;
trackStatus = true;
}
......@@ -672,4 +679,11 @@ public abstract class AbstractChat extends BaseEntity {
protected void onDisconnect() {
}
public Date getCreationTime() {
return creationTime;
}
public void updateCreationTime() {
creationTime.setTime(System.currentTimeMillis());
}
}
/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
*/
package com.xabber.android.ui;
import java.util.Collection;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import com.xabber.android.data.Application;
import com.xabber.android.data.account.OnAccountChangedListener;
import com.xabber.android.data.entity.BaseEntity;
import com.xabber.android.data.message.AbstractChat;
import com.xabber.android.data.message.OnChatChangedListener;
import com.xabber.android.data.roster.OnContactChangedListener;
import com.xabber.android.ui.adapter.ChatListAdapter;
import com.xabber.android.ui.helper.ManagedListActivity;
import com.xabber.androiddev.R;
public class ChatList extends ManagedListActivity implements
OnAccountChangedListener, OnContactChangedListener,
OnChatChangedListener, OnItemClickListener {
private ChatListAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing())
return;
setContentView(R.layout.list);
listAdapter = new ChatListAdapter(this);
setListAdapter(listAdapter);
getListView().setOnItemClickListener(this);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
protected void onResume() {
super.onResume();
Application.getInstance().addUIListener(OnAccountChangedListener.class,
this);
Application.getInstance().addUIListener(OnContactChangedListener.class,
this);
Application.getInstance().addUIListener(OnChatChangedListener.class,
this);
listAdapter.onChange();
}
@Override
protected void onPause() {
super.onPause();
Application.getInstance().removeUIListener(
OnAccountChangedListener.class, this);
Application.getInstance().removeUIListener(
OnContactChangedListener.class, this);
Application.getInstance().removeUIListener(OnChatChangedListener.class,
this);
}
@Override
public void onChatChanged(String account, String user, boolean incoming) {
listAdapter.onChange();
}
@Override
public void onContactsChanged(Collection<BaseEntity> addresses) {
listAdapter.onChange();
}
@Override
public void onAccountsChanged(Collection<String> accounts) {
listAdapter.onChange();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
AbstractChat abstractChat = (AbstractChat) parent.getAdapter().getItem(
position);
startActivity(ChatViewer.createIntent(this, abstractChat.getAccount(),
abstractChat.getUser()));
finish();
}
public static Intent createIntent(Context context) {
return new Intent(context, ChatList.class);
}
}
......@@ -14,44 +14,43 @@
*/
package com.xabber.android.ui;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.support.v4.view.ViewPager;
import com.xabber.android.data.ActivityManager;
import com.xabber.android.data.Application;
import com.xabber.android.data.LogManager;
import com.xabber.android.data.account.OnAccountChangedListener;
import com.xabber.android.data.entity.BaseEntity;
import com.xabber.android.data.extension.archive.MessageArchiveManager;
import com.xabber.android.data.extension.attention.AttentionManager;
import com.xabber.android.data.intent.EntityIntentBuilder;
import com.xabber.android.data.message.AbstractChat;
import com.xabber.android.data.message.MessageManager;
import com.xabber.android.data.message.OnChatChangedListener;
import com.xabber.android.data.notification.NotificationManager;
import com.xabber.android.data.roster.AbstractContact;
import com.xabber.android.data.roster.OnContactChangedListener;
import com.xabber.android.data.roster.RosterManager;
import com.xabber.android.ui.adapter.ChatViewerAdapter;
import com.xabber.android.ui.helper.ManagedActivity;
import com.xabber.android.ui.widget.PageSwitcher;
import com.xabber.android.ui.widget.PageSwitcher.OnSelectListener;
import com.xabber.androiddev.R;
import java.util.Collection;
import java.util.HashSet;
/**
* Chat activity.
* <p/>
* Warning: {@link PageSwitcher} is to be removed and related implementation is
* to be fixed.
*
* @author alexander.ivanov
*/
public class ChatViewer extends ManagedActivity implements OnSelectListener,
OnChatChangedListener, OnContactChangedListener,
OnAccountChangedListener {
public class ChatViewer extends ManagedActivity implements OnChatChangedListener,
OnContactChangedListener, OnAccountChangedListener, ViewPager.OnPageChangeListener,
ChatViewerAdapter.FinishUpdateListener, RecentChatFragment.RecentChatFragmentInteractionListener,
ChatViewerFragment.ChatViewerFragmentListener {
/**
* Attention request.
......@@ -62,93 +61,84 @@ public class ChatViewer extends ManagedActivity implements OnSelectListener,
private static final String SAVED_USER = "com.xabber.android.ui.ChatViewer.SAVED_USER";
private static final String SAVED_EXIT_ON_SEND = "com.xabber.android.ui.ChatViewer.EXIT_ON_SEND";
private ChatViewerAdapter chatViewerAdapter;
private PageSwitcher pageSwitcher;
private boolean exitOnSend;
private String actionWithAccount;
private String actionWithUser;
private View actionWithView;
private String extraText = null;
private boolean exitOnSend;
ChatViewerAdapter chatViewerAdapter;
ViewPager viewPager;
Collection<ChatViewerFragment> registeredChats = new HashSet<>();
Collection<RecentChatFragment> recentChatFragments = new HashSet<>();
private String actionWithAccount = null;
private String actionWithUser = null;
private boolean isVisible;
@Override
public void onCreate(Bundle savedInstanceState) {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing())
if (isFinishing()) {
return;
}
Intent intent = getIntent();
String account = getAccount(intent);
String user = getUser(intent);
if (PageSwitcher.LOG)
LogManager.i(this, "Intent: " + account + ":" + user);
if (account == null || user == null) {
Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND);
finish();
return;
}
if (hasAttention(intent))
AttentionManager.getInstance().removeAccountNotifications(account,
user);
actionWithAccount = null;
actionWithUser = null;
actionWithView = null;
setContentView(R.layout.chat_viewer);
chatViewerAdapter = new ChatViewerAdapter(this, account, user);
pageSwitcher = (PageSwitcher) findViewById(R.id.switcher);
pageSwitcher.setAdapter(chatViewerAdapter);
pageSwitcher.setOnSelectListener(this);
if (hasAttention(intent)) {
AttentionManager.getInstance().removeAccountNotifications(account, user);
}
if (savedInstanceState != null) {
actionWithAccount = savedInstanceState.getString(SAVED_ACCOUNT);
actionWithUser = savedInstanceState.getString(SAVED_USER);
if (account == null || user == null) {
account = savedInstanceState.getString(SAVED_ACCOUNT);
user = savedInstanceState.getString(SAVED_USER);
}
exitOnSend = savedInstanceState.getBoolean(SAVED_EXIT_ON_SEND);
}
if (actionWithAccount == null)
actionWithAccount = account;
if (actionWithUser == null)
actionWithUser = user;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
selectChat(actionWithAccount, actionWithUser);
setContentView(R.layout.activity_chat_viewer);
if (account != null && user != null) {
chatViewerAdapter = new ChatViewerAdapter(getFragmentManager(),
account, user, this);
} else {
chatViewerAdapter = new ChatViewerAdapter(getFragmentManager(), this);
}
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(chatViewerAdapter);
viewPager.setOnPageChangeListener(this);
selectPage(account, user, false);
}
@Override
protected void onResume() {
super.onResume();
Application.getInstance().addUIListener(OnChatChangedListener.class,
this);
Application.getInstance().addUIListener(OnContactChangedListener.class,
this);
Application.getInstance().addUIListener(OnAccountChangedListener.class,
this);
chatViewerAdapter.onChange();
if (actionWithView != null)
chatViewerAdapter.onChatChange(actionWithView, false);
Application.getInstance().addUIListener(OnChatChangedListener.class, this);
Application.getInstance().addUIListener(OnContactChangedListener.class, this);
Application.getInstance().addUIListener(OnAccountChangedListener.class, this);
Intent intent = getIntent();
if (Intent.ACTION_SEND.equals(intent.getAction())) {
String additional = intent.getStringExtra(Intent.EXTRA_TEXT);
if (additional != null) {
extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (extraText != null) {
intent.removeExtra(Intent.EXTRA_TEXT);
exitOnSend = true;
if (actionWithView != null)
chatViewerAdapter.insertText(actionWithView, additional);
}
}
isVisible = true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (PageSwitcher.LOG)
LogManager.i(this, "onSave: " + actionWithAccount + ":"
+ actionWithUser);
outState.putString(SAVED_ACCOUNT, actionWithAccount);
outState.putString(SAVED_USER, actionWithUser);
outState.putBoolean(SAVED_EXIT_ON_SEND, exitOnSend);
......@@ -157,15 +147,10 @@ public class ChatViewer extends ManagedActivity implements OnSelectListener,
@Override
protected void onPause() {
super.onPause();
Application.getInstance().removeUIListener(OnChatChangedListener.class,
this);
Application.getInstance().removeUIListener(
OnContactChangedListener.class, this);
Application.getInstance().removeUIListener(
OnAccountChangedListener.class, this);
Application.getInstance().removeUIListener(OnChatChangedListener.class, this);
Application.getInstance().removeUIListener(OnContactChangedListener.class, this);
Application.getInstance().removeUIListener(OnAccountChangedListener.class, this);
MessageManager.getInstance().removeVisibleChat();
pageSwitcher.saveState();
isVisible = false;
}
@Override
......@@ -174,168 +159,50 @@ public class ChatViewer extends ManagedActivity implements OnSelectListener,
if (isFinishing())
return;
chatViewerAdapter.onChange();
String account = getAccount(intent);
String user = getUser(intent);
if (account == null || user == null) {
return;
}
if (hasAttention(intent))
AttentionManager.getInstance().removeAccountNotifications(account,
user);
chatViewerAdapter.onChange();
if (!selectChat(account, user))
Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.clear();
chatViewerAdapter.onPrepareOptionsMenu(actionWithView, menu);
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
close();
return false;
}
return super.onKeyDown(keyCode, event);
}
void close() {
finish();
if (!Intent.ACTION_SEND.equals(getIntent().getAction())) {
ActivityManager.getInstance().clearStack(false);
if (!ActivityManager.getInstance().hasContactList(this))
startActivity(ContactList.createIntent(this));
}
}
void onSent() {
if (exitOnSend)
close();
}
@Override
public void onSelect() {
BaseEntity contactItem = (BaseEntity) pageSwitcher.getSelectedItem();
actionWithAccount = contactItem.getAccount();
actionWithUser = contactItem.getUser();
if (PageSwitcher.LOG)
LogManager.i(this, "onSelect: " + actionWithAccount + ":"
+ actionWithUser);
actionWithView = pageSwitcher.getSelectedView();
if (isVisible)
MessageManager.getInstance().setVisibleChat(actionWithAccount,
actionWithUser);
MessageArchiveManager.getInstance().requestHistory(
actionWithAccount,
actionWithUser,
0,
MessageManager.getInstance()
.getChat(actionWithAccount, actionWithUser)
.getRequiredMessageCount());
NotificationManager.getInstance().removeMessageNotification(
actionWithAccount, actionWithUser);
}
@Override
public void onUnselect() {
actionWithAccount = null;
actionWithUser = null;
actionWithView = null;
if (PageSwitcher.LOG)
LogManager.i(this, "onUnselect");
}
@Override
public void onChatChanged(final String account, final String user,
final boolean incoming) {
BaseEntity baseEntity;
baseEntity = (BaseEntity) pageSwitcher.getSelectedItem();
if (baseEntity != null && baseEntity.equals(account, user)) {
chatViewerAdapter.onChatChange(pageSwitcher.getSelectedView(),
incoming);
return;
}
baseEntity = (BaseEntity) pageSwitcher.getVisibleItem();
if (baseEntity != null && baseEntity.equals(account, user)) {
chatViewerAdapter.onChatChange(pageSwitcher.getVisibleView(),
incoming);
return;
}
// Search for chat in adapter.
final int count = chatViewerAdapter.getCount();
for (int index = 0; index < count; index++)
if (((BaseEntity) chatViewerAdapter.getItem(index)).equals(account,
user))
return;
// New chat.
chatViewerAdapter.onChange();
}
@Override
public void onContactsChanged(Collection<BaseEntity> entities) {
BaseEntity baseEntity;
baseEntity = (BaseEntity) pageSwitcher.getSelectedItem();
if (baseEntity != null && entities.contains(baseEntity)) {
chatViewerAdapter.onChange();
return;
}
baseEntity = (BaseEntity) pageSwitcher.getVisibleItem();
if (baseEntity != null && entities.contains(baseEntity)) {
chatViewerAdapter.onChange();
return;
}
selectPage(account, user, false);
}
@Override
public void onAccountsChanged(Collection<String> accounts) {
BaseEntity baseEntity;
baseEntity = (BaseEntity) pageSwitcher.getSelectedItem();
if (baseEntity != null && accounts.contains(baseEntity.getAccount())) {
chatViewerAdapter.onChange();
return;
}
baseEntity = (BaseEntity) pageSwitcher.getVisibleItem();
if (baseEntity != null && accounts.contains(baseEntity.getAccount())) {
chatViewerAdapter.onChange();
return;
}
private void selectPage(String account, String user, boolean smoothScroll) {
int position = chatViewerAdapter.getPageNumber(account, user);
viewPager.setCurrentItem(position, smoothScroll);
onPageSelected(position);
}
private boolean selectChat(String account, String user) {
for (int position = 0; position < chatViewerAdapter.getCount(); position++)
if (((BaseEntity) chatViewerAdapter.getItem(position)).equals(
account, user)) {
if (PageSwitcher.LOG)
LogManager.i(this, "setSelection: " + position + ", "
+ account + ":" + user);
pageSwitcher.setSelection(position);
return true;
}
if (PageSwitcher.LOG)
LogManager.i(this, "setSelection: not found, " + account + ":"
+ user);
return false;
private static String getAccount(Intent intent) {
String value = EntityIntentBuilder.getAccount(intent);
if (value != null)
return value;
// Backward compatibility.
return intent.getStringExtra("com.xabber.android.data.account");
}
public int getChatCount() {
return chatViewerAdapter.getCount();
private static String getUser(Intent intent) {
String value = EntityIntentBuilder.getUser(intent);
if (value != null)
return value;
// Backward compatibility.
return intent.getStringExtra("com.xabber.android.data.user");
}
public int getChatPosition(String account, String user) {
return chatViewerAdapter.getPosition(account, user);
private static boolean hasAttention(Intent intent) {
return ACTION_ATTENTION.equals(intent.getAction());
}
public static Intent createIntent(Context context, String account,
String user) {
return new EntityIntentBuilder(context, ChatViewer.class)
.setAccount(account).setUser(user).build();
return new EntityIntentBuilder(context, ChatViewer.class).setAccount(account).setUser(user).build();
}
public static Intent createIntent(Context context) {
return new EntityIntentBuilder(context, ChatViewer.class).build();
}
public static Intent createClearTopIntent(Context context, String account,
......@@ -372,24 +239,154 @@ public class ChatViewer extends ManagedActivity implements OnSelectListener,
return intent;
}
private static String getAccount(Intent intent) {
String value = EntityIntentBuilder.getAccount(intent);
if (value != null)
return value;
// Backward compatibility.
return intent.getStringExtra("com.xabber.android.data.account");
@Override
public void onChatChanged(final String account, final String user,
final boolean incoming) {
for (ChatViewerFragment chat : registeredChats) {
if (chat.isEqual(account, user)) {
chat.updateChat(incoming);
}
}
updateRegisteredRecentChatsFragments();
}
private static String getUser(Intent intent) {
String value = EntityIntentBuilder.getUser(intent);
if (value != null)
return value;
// Backward compatibility.
return intent.getStringExtra("com.xabber.android.data.user");
@Override
public void onContactsChanged(Collection<BaseEntity> entities) {
chatViewerAdapter.onChange();
updateRegisteredChats();
updateRegisteredRecentChatsFragments();
}
private static boolean hasAttention(Intent intent) {
return ACTION_ATTENTION.equals(intent.getAction());
@Override
public void onAccountsChanged(Collection<String> accounts) {
chatViewerAdapter.onChange();
updateRegisteredChats();
updateRegisteredRecentChatsFragments();
}
void onSent() {
if (exitOnSend) {
close();
}
}
void close() {
finish();
if (!Intent.ACTION_SEND.equals(getIntent().getAction())) {
ActivityManager.getInstance().clearStack(false);
if (!ActivityManager.getInstance().hasContactList(this)) {
startActivity(ContactList.createIntent(this));
}
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
AbstractChat selectedChat = chatViewerAdapter.getChatByPageNumber(position);
if (selectedChat == null) {
setTitle(getString(R.string.chat_list));
return;
}
String account = selectedChat.getAccount();
String user = selectedChat.getUser();
final AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user);
setTitle(abstractContact.getName());
MessageManager.getInstance().setVisibleChat(account, user);
MessageArchiveManager.getInstance().requestHistory(
account, user, 0,
MessageManager.getInstance().getChat(account, user).getRequiredMessageCount());
NotificationManager.getInstance().removeMessageNotification(account, user);
actionWithAccount = account;
actionWithUser = user;
}
private void updateRegisteredChats() {
for (ChatViewerFragment chat : registeredChats) {
chat.updateChat(false);
}
}
private void updateRegisteredRecentChatsFragments() {
for (RecentChatFragment recentChatFragment : recentChatFragments) {
recentChatFragment.updateChats(chatViewerAdapter.getActiveChats());
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
public void registerChat(ChatViewerFragment chat) {
registeredChats.add(chat);
}
public void unregisterChat(ChatViewerFragment chat) {
registeredChats.remove(chat);
}
public void registerRecentChatsList(RecentChatFragment recentChatFragment) {
recentChatFragments.add(recentChatFragment);
}
public void unregisterRecentChatsList(RecentChatFragment recentChatFragment) {
recentChatFragments.remove(recentChatFragment);
}
@Override
public void onChatViewAdapterFinishUpdate() {
insertExtraText();
Fragment currentFragment = chatViewerAdapter.getCurrentFragment();
if (currentFragment instanceof ChatViewerFragment) {
((ChatViewerFragment)currentFragment).setInputFocus();
}
}
private void insertExtraText() {
if (extraText == null) {
return;
}
boolean isExtraTextInserted = false;
for (ChatViewerFragment chat : registeredChats) {
if (chat.isEqual(actionWithAccount, actionWithUser)) {
chat.setInputText(extraText);
isExtraTextInserted = true;
}
}
if (isExtraTextInserted) {
extraText = null;
}
}
@Override
public void onRecentChatSelected(AbstractChat chat) {
selectPage(chat.getAccount(), chat.getUser(), true);
}
@Override
public void onRecentChatsCalled() {
viewPager.setCurrentItem(0, true);
}
}
package com.xabber.android.ui;
import android.app.Activity;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.text.ClipboardManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.xabber.android.data.Application;
import com.xabber.android.data.LogManager;
import com.xabber.android.data.NetworkException;
import com.xabber.android.data.SettingsManager;
import com.xabber.android.data.SettingsManager.ChatsHideKeyboard;
import com.xabber.android.data.SettingsManager.SecurityOtrMode;
import com.xabber.android.data.extension.archive.MessageArchiveManager;
import com.xabber.android.data.extension.attention.AttentionManager;
import com.xabber.android.data.extension.cs.ChatStateManager;
......@@ -58,25 +50,14 @@ import com.xabber.android.ui.dialog.ChatExportDialogFragment;
import com.xabber.android.ui.helper.AbstractAvatarInflaterHelper;
import com.xabber.android.ui.helper.ContactTitleInflater;
import com.xabber.android.ui.preferences.ChatEditor;
import com.xabber.android.ui.widget.PageSwitcher;
import com.xabber.androiddev.R;
public class ChatViewerFragment implements OnCreateContextMenuListener {
public class ChatViewerFragment extends Fragment {
/**
* Minimum number of new messages to be requested from the server side
* archive.
*/
private static final int MINIMUM_MESSAGES_TO_LOAD = 10;
public static final String ARGUMENT_ACCOUNT = "ARGUMENT_ACCOUNT";
public static final String ARGUMENT_USER = "ARGUMENT_USER";
/**
* Delay before hide pages.
*/
private static final long PAGES_HIDDER_DELAY = 1000;
private AbstractAvatarInflaterHelper avatarInflaterHelper;
private boolean skipOnTextChanges;
private static final int MINIMUM_MESSAGES_TO_LOAD = 10;
private TextView pageView;
private View titleView;
......@@ -84,75 +65,52 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
private ListView listView;
private ChatMessageAdapter chatMessageAdapter;
/**
* Whether pages are shown.
*/
private boolean pagesShown;
/**
* Animation used to hide pages.
*/
private Animation pagesHideAnimation;
private boolean skipOnTextChanges;
/**
* Animation used for incoming message notification.
*/
private Animation shakeAnimation;
private Handler handler;
private AbstractAvatarInflaterHelper avatarInflaterHelper;
/**
* Runnable called to hide pages.
*/
private final Runnable pagesHideRunnable = new Runnable() {
@Override
public void run() {
handler.removeCallbacks(this);
pageView.startAnimation(pagesHideAnimation);
}
};
private String account;
private String user;
private final FragmentActivity activity;
private ChatViewerFragmentListener listener;
private final View view;
public static ChatViewerFragment newInstance(String account, String user) {
ChatViewerFragment fragment = new ChatViewerFragment();
public ChatViewerFragment(FragmentActivity activity) {
this.activity = activity;
onCreate(null);
view = onCreateView(activity.getLayoutInflater(), null, null);
Bundle arguments = new Bundle();
arguments.putString(ARGUMENT_ACCOUNT, account);
arguments.putString(ARGUMENT_USER, user);
fragment.setArguments(arguments);
return fragment;
}
private FragmentActivity getActivity() {
return activity;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
private String getString(int resId, Object... formatArgs) {
return activity.getString(resId, formatArgs);
}
Bundle args = getArguments();
account = args.getString(ARGUMENT_ACCOUNT, null);
user = args.getString(ARGUMENT_USER, null);
private void registerForContextMenu(View view) {
view.setOnCreateContextMenuListener(this);
avatarInflaterHelper = AbstractAvatarInflaterHelper.createAbstractContactInflaterHelper();
}
public View getView() {
return view;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
public void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
avatarInflaterHelper = AbstractAvatarInflaterHelper
.createAbstractContactInflaterHelper();
handler = new Handler();
pagesShown = false;
}
shakeAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.shake);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
shakeAnimation = AnimationUtils.loadAnimation(getActivity(),
R.anim.shake);
pagesHideAnimation = AnimationUtils.loadAnimation(getActivity(),
R.anim.chat_page_out);
pagesHideAnimation.setAnimationListener(new AnimationListener() {
/*
Animation used to hide pages.
*/
Animation pagesHideAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.chat_page_out);
pagesHideAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
......@@ -168,17 +126,21 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
}
});
View view = inflater.inflate(R.layout.chat_viewer_item, container,
false);
View view = inflater.inflate(R.layout.chat_viewer_item, container, false);
chatMessageAdapter = new ChatMessageAdapter(getActivity());
chatMessageAdapter.setChat(account, user);
listView = (ListView) view.findViewById(android.R.id.list);
listView.setAdapter(chatMessageAdapter);
pageView = (TextView) view.findViewById(R.id.chat_page);
titleView = view.findViewById(R.id.title);
inputView = (EditText) view.findViewById(R.id.chat_input);
listView = (ListView) view.findViewById(android.R.id.list);
listView.setAdapter(chatMessageAdapter);
view.findViewById(R.id.chat_send).setOnClickListener(
new OnClickListener() {
new View.OnClickListener() {
@Override
public void onClick(View v) {
......@@ -186,7 +148,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
}
});
titleView.setOnClickListener(new OnClickListener() {
titleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
......@@ -196,7 +158,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
}
});
inputView.setOnKeyListener(new OnKeyListener() {
inputView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
......@@ -210,7 +172,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
}
});
inputView.setOnEditorActionListener(new OnEditorActionListener() {
inputView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView view, int actionId,
......@@ -226,13 +188,11 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
inputView.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
......@@ -245,16 +205,141 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
}
});
registerForContextMenu(listView);
setHasOptionsMenu(true);
updateView();
chatMessageAdapter.onChange();
return view;
}
@Override
public void onResume() {
super.onResume();
((ChatViewer)getActivity()).registerChat(this);
registerForContextMenu(listView);
restoreInputState();
}
private void restoreInputState() {
skipOnTextChanges = true;
inputView.setText(ChatManager.getInstance().getTypedMessage(account, user));
inputView.setSelection(ChatManager.getInstance().getSelectionStart(account, user),
ChatManager.getInstance().getSelectionEnd(account, user));
skipOnTextChanges = false;
}
@Override
public void onPause() {
super.onPause();
saveInputState();
((ChatViewer)getActivity()).unregisterChat(this);
unregisterForContextMenu(listView);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (ChatViewerFragmentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ChatViewerFragmentListener");
}
}
@Override
public void onDetach() {
listener = null;
super.onDetach();
}
public void saveInputState() {
ChatManager.getInstance().setTyped(account, user, inputView.getText().toString(),
inputView.getSelectionStart(), inputView.getSelectionEnd());
}
private void sendMessage() {
String text = inputView.getText().toString();
int start = 0;
int end = text.length();
while (start < end && (text.charAt(start) == ' ' || text.charAt(start) == '\n')) {
start += 1;
}
while (start < end && (text.charAt(end - 1) == ' ' || text.charAt(end - 1) == '\n')) {
end -= 1;
}
text = text.substring(start, end);
if ("".equals(text)) {
return;
}
skipOnTextChanges = true;
inputView.getText().clear();
skipOnTextChanges = false;
sendMessage(text);
((ChatViewer) getActivity()).onSent();
if (SettingsManager.chatsHideKeyboard() == SettingsManager.ChatsHideKeyboard.always
|| (getActivity().getResources().getBoolean(R.bool.landscape)
&& SettingsManager.chatsHideKeyboard() == SettingsManager.ChatsHideKeyboard.landscape)) {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0);
}
}
private void sendMessage(String text) {
MessageManager.getInstance().sendMessage(account, user, text);
updateChat(false);
}
private void updateView() {
chatMessageAdapter.onChange();
final AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user);
ContactTitleInflater.updateTitle(titleView, getActivity(), abstractContact);
avatarInflaterHelper.updateAvatar((ImageView) titleView.findViewById(R.id.avatar), abstractContact);
SecurityLevel securityLevel = OTRManager.getInstance().getSecurityLevel(account, user);
SettingsManager.SecurityOtrMode securityOtrMode = SettingsManager.securityOtrMode();
ImageView securityView = (ImageView) titleView.findViewById(R.id.security);
if (securityLevel == SecurityLevel.plain
&& (securityOtrMode == SettingsManager.SecurityOtrMode.disabled
|| securityOtrMode == SettingsManager.SecurityOtrMode.manual)) {
securityView.setVisibility(View.GONE);
} else {
securityView.setVisibility(View.VISIBLE);
securityView.setImageLevel(securityLevel.getImageLevel());
}
}
public boolean onPrepareOptionsMenu(Menu menu) {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final String account = chatMessageAdapter.getAccount();
final String user = chatMessageAdapter.getUser();
AbstractChat abstractChat = MessageManager.getInstance().getChat(account, user);
getActivity().getMenuInflater().inflate(R.menu.chat, menu);
inflater.inflate(R.menu.chat, menu);
if (abstractChat != null && abstractChat instanceof RoomChat) {
if (((RoomChat) abstractChat).getState() == RoomState.unavailable) {
......@@ -276,7 +361,13 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
menu.findItem(R.id.action_edit_contact).setVisible(true)
.setIntent(ContactEditor.createIntent(getActivity(), account, user));
}
menu.findItem(R.id.action_chat_list).setIntent(ChatList.createIntent(getActivity()));
menu.findItem(R.id.action_chat_list).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
listener.onRecentChatsCalled();
return true;
}
});
menu.findItem(R.id.action_chat_settings)
.setIntent(ChatEditor.createIntent(getActivity(), account, user));
......@@ -288,7 +379,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
MessageManager.getInstance().requestToLoadLocalHistory(account, user);
MessageArchiveManager.getInstance()
.requestHistory(account, user, MINIMUM_MESSAGES_TO_LOAD, 0);
onChatChange(false);
updateChat(false);
return true;
}
});
......@@ -331,7 +422,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
inputView.setText("");
inputView.getText().clear();
return true;
}
});
......@@ -342,7 +433,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
public boolean onMenuItemClick(MenuItem item) {
MessageManager.getInstance()
.clearHistory(account, user);
onChatChange(false);
updateChat(false);
return false;
}
});
......@@ -352,10 +443,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
@Override
public boolean onMenuItemClick(MenuItem item) {
ChatExportDialogFragment
.newInstance(account, user)
.show(getActivity().getSupportFragmentManager(),
"CHAT_EXPORT");
ChatExportDialogFragment.newInstance(account, user).show(getActivity().getFragmentManager(), "CHAT_EXPORT");
return true;
}
......@@ -379,7 +467,7 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
abstractChat.getUser());
if (securityLevel == SecurityLevel.plain) {
menu.findItem(R.id.action_start_encryption).setVisible(true)
.setEnabled(SettingsManager.securityOtrMode() != SecurityOtrMode.disabled)
.setEnabled(SettingsManager.securityOtrMode() != SettingsManager.SecurityOtrMode.disabled)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
......@@ -446,160 +534,59 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
&& ((RoomChat) abstractChat).getState() == RoomState.available)
menu.findItem(R.id.action_list_of_occupants).setVisible(true).setIntent(
OccupantList.createIntent(getActivity(), account, user));
return true;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view,
ContextMenuInfo menuInfo) {
// super.onCreateContextMenu(menu, view, menuInfo);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
final MessageItem message = (MessageItem) listView.getAdapter()
.getItem(info.position);
if (message != null && message.getAction() != null)
return;
if (message.isError()) {
menu.add(R.string.message_repeat).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
final MessageItem message = (MessageItem) listView.getAdapter().getItem(info.position);
@Override
public boolean onMenuItemClick(MenuItem item) {
sendMessage(message.getText());
return true;
}
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.chat_context_menu, menu);
});
if (message.isError()) {
menu.findItem(R.id.action_message_repeat).setVisible(true);
}
menu.add(R.string.message_quote).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
insertText("> " + message.getText() + "\n");
return true;
}
});
menu.add(R.string.message_copy).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
((ClipboardManager) getActivity().getSystemService(
Context.CLIPBOARD_SERVICE)).setText(message
.getSpannable());
return true;
}
});
menu.add(R.string.message_remove).setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
MessageManager.getInstance().removeMessage(message);
onChatChange(false);
return true;
}
});
}
public void setChat(AbstractChat chat) {
final String account = chat.getAccount();
final String user = chat.getUser();
final AbstractContact abstractContact = RosterManager.getInstance()
.getBestContact(account, user);
if (chat.equals(chatMessageAdapter.getAccount(),
chatMessageAdapter.getUser())) {
chatMessageAdapter.updateInfo();
} else {
if (chatMessageAdapter.getAccount() != null
&& chatMessageAdapter.getUser() != null)
saveState();
if (PageSwitcher.LOG)
LogManager.i(this, "Load " + chatMessageAdapter.getUser()
+ " in " + chatMessageAdapter.getAccount());
skipOnTextChanges = true;
inputView.setText(ChatManager.getInstance().getTypedMessage(
account, user));
inputView.setSelection(
ChatManager.getInstance().getSelectionStart(account, user),
ChatManager.getInstance().getSelectionEnd(account, user));
skipOnTextChanges = false;
chatMessageAdapter.setChat(account, user);
listView.setAdapter(listView.getAdapter());
}
pageView.setText(getString(
R.string.chat_page,
((ChatViewer) getActivity()).getChatPosition(account, user) + 1,
((ChatViewer) getActivity()).getChatCount()));
ContactTitleInflater.updateTitle(titleView, getActivity(),
abstractContact);
avatarInflaterHelper.updateAvatar(
(ImageView) titleView.findViewById(R.id.avatar),
abstractContact);
SecurityLevel securityLevel = OTRManager.getInstance()
.getSecurityLevel(chat.getAccount(), chat.getUser());
SecurityOtrMode securityOtrMode = SettingsManager.securityOtrMode();
ImageView securityView = (ImageView) titleView
.findViewById(R.id.security);
if (securityLevel == SecurityLevel.plain
&& (securityOtrMode == SecurityOtrMode.disabled || securityOtrMode == SecurityOtrMode.manual)) {
securityView.setVisibility(View.GONE);
} else {
securityView.setVisibility(View.VISIBLE);
securityView.setImageLevel(securityLevel.getImageLevel());
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final MessageItem message = (MessageItem) listView.getAdapter().getItem(info.position);
switch (item.getItemId()) {
case R.id.action_message_repeat:
sendMessage(message.getText());
return true;
case R.id.action_message_copy:
((ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText(message.getSpannable(), message.getSpannable()));
return true;
case R.id.action_message_quote:
insertText("> " + message.getText() + "\n");
return true;
case R.id.action_message_remove:
MessageManager.getInstance().removeMessage(message);
updateChat(false);
return true;
default:
return super.onContextItemSelected(item);
}
}
public void saveState() {
if (PageSwitcher.LOG)
LogManager.i(this, "Save " + chatMessageAdapter.getUser() + " in "
+ chatMessageAdapter.getAccount());
ChatManager.getInstance().setTyped(chatMessageAdapter.getAccount(),
chatMessageAdapter.getUser(), inputView.getText().toString(),
inputView.getSelectionStart(), inputView.getSelectionEnd());
}
public void onChatChange(boolean incomingMessage) {
if (incomingMessage)
titleView.findViewById(R.id.name_holder).startAnimation(
shakeAnimation);
chatMessageAdapter.onChange();
}
/**
* Show pages.
*/
public void showPages() {
if (pagesShown)
return;
pagesShown = true;
handler.removeCallbacks(pagesHideRunnable);
pageView.clearAnimation();
pageView.setVisibility(View.VISIBLE);
}
/**
* Requests pages to be hiden in future.
*/
public void hidePages() {
if (!pagesShown)
return;
pagesShown = false;
handler.postDelayed(pagesHideRunnable, PAGES_HIDDER_DELAY);
}
/**
* Insert additional text to the input.
*
* @param additional
*/
public void insertText(String additional) {
private void insertText(String additional) {
String source = inputView.getText().toString();
int selection = inputView.getSelectionEnd();
if (selection == -1)
......@@ -614,38 +601,35 @@ public class ChatViewerFragment implements OnCreateContextMenuListener {
inputView.setSelection(selection + additional.length());
}
private void sendMessage() {
String text = inputView.getText().toString();
int start = 0;
int end = text.length();
while (start < end
&& (text.charAt(start) == ' ' || text.charAt(start) == '\n'))
start += 1;
while (start < end
&& (text.charAt(end - 1) == ' ' || text.charAt(end - 1) == '\n'))
end -= 1;
text = text.substring(start, end);
if ("".equals(text))
return;
skipOnTextChanges = true;
inputView.setText("");
skipOnTextChanges = false;
sendMessage(text);
((ChatViewer) getActivity()).onSent();
if (SettingsManager.chatsHideKeyboard() == ChatsHideKeyboard.always
|| (getActivity().getResources().getBoolean(R.bool.landscape) && SettingsManager
.chatsHideKeyboard() == ChatsHideKeyboard.landscape)) {
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0);
public void updateChat(boolean incomingMessage) {
if (incomingMessage) {
titleView.findViewById(R.id.name_holder).startAnimation(shakeAnimation);
}
updateView();
}
private void sendMessage(String text) {
final String account = chatMessageAdapter.getAccount();
final String user = chatMessageAdapter.getUser();
MessageManager.getInstance().sendMessage(account, user, text);
onChatChange(false);
public boolean isEqual(String account, String user) {
return this.account.equals(account) && this.user.equals(user);
}
public void setInputFocus() {
inputView.requestFocus();
}
public void setInputText(String text) {
insertText(text);
}
public String getAccount() {
return account;
}
public String getUser() {
return user;
}
public interface ChatViewerFragmentListener {
public void onRecentChatsCalled();
}
}
......@@ -171,8 +171,8 @@ public class ContactList extends ManagedActivity implements OnChoosedListener, O
text);
return;
}
AccountChooseDialogFragment.newInstance(bareAddress, text).show(
getSupportFragmentManager(), "OPEN_WITH_ACCOUNT");
AccountChooseDialogFragment.newInstance(bareAddress, text)
.show(getFragmentManager(), "OPEN_WITH_ACCOUNT");
}
/**
......@@ -240,15 +240,14 @@ public class ContactList extends ManagedActivity implements OnChoosedListener, O
if (SettingsManager.bootCount() > 2
&& !SettingsManager.connectionStartAtBoot()
&& !SettingsManager.startAtBootSuggested())
StartAtBootDialogFragment.newInstance().show(
getSupportFragmentManager(), "START_AT_BOOT");
StartAtBootDialogFragment.newInstance().show(getFragmentManager(), "START_AT_BOOT");
if (!SettingsManager.contactIntegrationSuggested()
&& Application.getInstance().isContactsSupported()) {
if (AccountManager.getInstance().getAllAccounts().isEmpty())
SettingsManager.setContactIntegrationSuggested();
else
ContactIntegrationDialogFragment.newInstance().show(
getSupportFragmentManager(), "CONTACT_INTEGRATION");
getFragmentManager(), "CONTACT_INTEGRATION");
}
}
}
......@@ -284,7 +283,7 @@ public class ContactList extends ManagedActivity implements OnChoosedListener, O
startActivity(MUCEditor.createIntent(this));
return true;
case R.id.action_chat_list:
startActivity(ChatList.createIntent(this));
startActivity(ChatViewer.createIntent(this));
return true;
case R.id.action_settings:
startActivity(PreferenceEditor.createIntent(this));
......
......@@ -14,10 +14,6 @@
*/
package com.xabber.android.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
......@@ -34,6 +30,10 @@ import com.xabber.android.ui.dialog.GroupAddDialogFragment.OnGroupAddConfirmed;
import com.xabber.android.ui.helper.ManagedListActivity;
import com.xabber.androiddev.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* Manage list of selected groups.
*
......@@ -146,8 +146,7 @@ public abstract class GroupListActivity extends ManagedListActivity implements
}
private void showGroupAddDialog() {
GroupAddDialogFragment.newInstance(getGroups()).show(
getSupportFragmentManager(), "GROUP-ADD");
GroupAddDialogFragment.newInstance(getGroups()).show(getFragmentManager(), "GROUP-ADD");
}
@Override
......
package com.xabber.android.ui;
import android.app.Activity;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.Toast;
import com.xabber.android.data.message.AbstractChat;
import com.xabber.android.ui.adapter.ChatListAdapter;
import com.xabber.androiddev.R;
import java.util.List;
public class RecentChatFragment extends ListFragment {
private RecentChatFragmentInteractionListener listener;
private List<AbstractChat> initialChats = null;
public static RecentChatFragment newInstance() {
return new RecentChatFragment();
}
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public RecentChatFragment() {
}
public void setInitialChats(List<AbstractChat> initialChats) {
this.initialChats = initialChats;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ChatListAdapter(getActivity()));
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (RecentChatFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement RecentChatFragmentInteractionListener");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (initialChats != null) {
((ChatListAdapter) getListAdapter()).updateChats(initialChats);
initialChats = null;
}
if (getListAdapter().isEmpty()) {
Activity activity = getActivity();
Toast.makeText(activity, R.string.chat_list_is_empty, Toast.LENGTH_LONG).show();
activity.finish();
}
return inflater.inflate(R.layout.list, container, false);
}
@Override
public void onResume() {
super.onResume();
((ChatViewer)getActivity()).registerRecentChatsList(this);
}
@Override
public void onPause() {
super.onPause();
((ChatViewer)getActivity()).unregisterRecentChatsList(this);
}
@Override
public void onDetach() {
listener = null;
super.onDetach();
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if (null != listener) {
listener.onRecentChatSelected((AbstractChat) getListAdapter().getItem(position));
}
}
public interface RecentChatFragmentInteractionListener {
public void onRecentChatSelected(AbstractChat chat);
}
public void updateChats(List<AbstractChat> chats) {
((ChatListAdapter) getListAdapter()).updateChats(chats);
}
}
/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
*/
package com.xabber.android.ui.adapter;
import java.util.ArrayList;
import java.util.Collections;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.xabber.android.data.message.AbstractChat;
import com.xabber.android.data.message.MessageManager;
import com.xabber.android.data.notification.NotificationManager;
import com.xabber.android.data.roster.AbstractContact;
import com.xabber.android.data.roster.RosterManager;
import com.xabber.android.ui.ChatList;
import com.xabber.android.ui.helper.AbstractAvatarInflaterHelper;
import com.xabber.androiddev.R;
/**
* Adapter for {@link ChatList}.
*
* @author alexander.ivanov
*/
public class ChatListAdapter extends BaseAdapter implements UpdatableAdapter {
import java.util.ArrayList;
import java.util.List;
private final Activity activity;
public class ChatListAdapter extends BaseAdapter {
private final ArrayList<AbstractChat> abstractChats;
private List<AbstractChat> chats;
private final AbstractAvatarInflaterHelper avatarInflaterHelper;
public ChatListAdapter(Activity activity) {
this.activity = activity;
abstractChats = new ArrayList<AbstractChat>();
avatarInflaterHelper = AbstractAvatarInflaterHelper
.createAbstractContactInflaterHelper();
private final Context context;
public ChatListAdapter(Context context) {
this.context = context;
chats = new ArrayList<>();
avatarInflaterHelper = AbstractAvatarInflaterHelper.createAbstractContactInflaterHelper();
}
@Override
public void onChange() {
abstractChats.clear();
abstractChats.addAll(MessageManager.getInstance().getActiveChats());
Collections.sort(abstractChats, ChatComparator.CHAT_COMPARATOR);
if (abstractChats.size() == 0) {
Toast.makeText(activity, R.string.chat_list_is_empty,
Toast.LENGTH_LONG).show();
activity.finish();
return;
}
public void updateChats(List<AbstractChat> chats) {
this.chats.clear();
this.chats.addAll(chats);
notifyDataSetChanged();
}
@Override
public int getCount() {
return abstractChats.size();
return chats.size();
}
@Override
public Object getItem(int position) {
return abstractChats.get(position);
return chats.get(position);
}
@Override
......@@ -87,33 +59,32 @@ public class ChatListAdapter extends BaseAdapter implements UpdatableAdapter {
public View getView(int position, View convertView, ViewGroup parent) {
final View view;
if (convertView == null) {
view = activity.getLayoutInflater().inflate(
R.layout.chat_list_item, parent, false);
view = LayoutInflater.from(context).inflate(R.layout.chat_list_item, parent, false);
} else {
view = convertView;
}
final AbstractChat abstractChat = (AbstractChat) getItem(position);
final AbstractContact abstractContact = RosterManager.getInstance()
.getBestContact(abstractChat.getAccount(),
abstractChat.getUser());
final ImageView colorView = (ImageView) view.findViewById(R.id.color);
.getBestContact(abstractChat.getAccount(), abstractChat.getUser());
((TextView) view.findViewById(R.id.name)).setText(abstractContact.getName());
((ImageView) view.findViewById(R.id.color)).setImageLevel(abstractContact.getColorLevel());
final ImageView avatarView = (ImageView) view.findViewById(R.id.avatar);
final TextView nameView = (TextView) view.findViewById(R.id.name);
final TextView textView = (TextView) view.findViewById(R.id.text);
colorView.setImageLevel(abstractContact.getColorLevel());
avatarView.setImageDrawable(abstractContact.getAvatar());
avatarInflaterHelper.updateAvatar(avatarView, abstractContact);
nameView.setText(abstractContact.getName());
final TextView textView = (TextView) view.findViewById(R.id.text);
String statusText = MessageManager.getInstance().getLastText(
abstractContact.getAccount(), abstractContact.getUser());
textView.setText(statusText);
boolean newMessages = NotificationManager.getInstance()
.getNotificationMessageCount(abstractChat.getAccount(),
abstractChat.getUser()) > 0;
textView.setTextAppearance(activity,
newMessages ? R.style.ChatList_Notification
.getNotificationMessageCount(abstractChat.getAccount(), abstractChat.getUser()) > 0;
textView.setTextAppearance(context, newMessages ? R.style.ChatList_Notification
: R.style.ChatList_Normal);
return view;
}
}
......@@ -14,11 +14,6 @@
*/
package com.xabber.android.ui.adapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import android.app.Activity;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
......@@ -49,6 +44,11 @@ import com.xabber.android.utils.Emoticons;
import com.xabber.android.utils.StringUtils;
import com.xabber.androiddev.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Adapter for the list of messages in the chat.
*
......@@ -309,8 +309,7 @@ public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter
@Override
public void onChange() {
messages = new ArrayList<MessageItem>(MessageManager.getInstance()
.getMessages(account, user));
messages = new ArrayList<>(MessageManager.getInstance().getMessages(account, user));
hint = getHint();
notifyDataSetChanged();
}
......
/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
*/
package com.xabber.android.ui.adapter;
import java.util.ArrayList;
import java.util.Collection;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;
import android.view.View;
import android.app.Fragment;
import android.app.FragmentManager;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.xabber.android.data.entity.BaseEntity;
import com.antonyt.infiniteviewpager.InfiniteViewPager;
import com.xabber.android.data.message.AbstractChat;
import com.xabber.android.data.message.MessageManager;
import com.xabber.android.ui.ChatViewerFragment;
import com.xabber.android.ui.RecentChatFragment;
import com.xabber.xmpp.address.Jid;
/**
* Adapter for the list of chat pages.
*
* @author alexander.ivanov
*/
public class ChatViewerAdapter extends BaseAdapter implements SaveStateAdapter,
UpdatableAdapter {
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
private final FragmentActivity activity;
public class ChatViewerAdapter extends FragmentStatePagerAdapter implements UpdatableAdapter {
/**
* Intent sent while opening chat activity.
*/
private final AbstractChat intent;
/**
* Position to insert intent.
*/
private final int intentPosition;
private ArrayList<AbstractChat> activeChats;
public ChatViewerAdapter(FragmentActivity activity, String account,
String user) {
this.activity = activity;
activeChats = new ArrayList<AbstractChat>();
intent = MessageManager.getInstance().getOrCreateChat(account,
Jid.getBareAddress(user));
Collection<? extends BaseEntity> activeChats = MessageManager
.getInstance().getActiveChats();
if (activeChats.contains(intent))
intentPosition = -1;
else
intentPosition = activeChats.size();
private FinishUpdateListener finishUpdateListener;
private Fragment currentFragment;
public ChatViewerAdapter(FragmentManager fragmentManager, String account, String user, FinishUpdateListener finishUpdateListener) {
super(fragmentManager);
this.finishUpdateListener = finishUpdateListener;
activeChats = new ArrayList<>(MessageManager.getInstance().getActiveChats());
intent = MessageManager.getInstance().getOrCreateChat(account, Jid.getBareAddress(user));
if (!activeChats.contains(intent)) {
intent.updateCreationTime();
}
onChange();
}
@Override
public int getCount() {
return activeChats.size();
public ChatViewerAdapter(FragmentManager fragmentManager, FinishUpdateListener finishUpdateListener) {
super(fragmentManager);
this.finishUpdateListener = finishUpdateListener;
activeChats = new ArrayList<>(MessageManager.getInstance().getActiveChats());
intent = null;
onChange();
}
@Override
public Object getItem(int position) {
return activeChats.get(position);
public int getCount() {
// warning: scrolling to very high values (1,000,000+) results in
// strange drawing behaviour
return InfiniteViewPager.TOTAL_COUNT;
}
@Override
public long getItemId(int position) {
return position;
public int getRealCount() {
return activeChats.size() + 1;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
final AbstractChat chat = (AbstractChat) getItem(position);
ChatViewerFragment fragment;
if (convertView == null) {
fragment = new ChatViewerFragment(activity);
view = fragment.getView();
view.setTag(fragment);
public Fragment getItem(int virtualPagePosition) {
int chatIndex;
int realPosition = virtualPagePosition % getRealCount();
if (realPosition == 0) {
RecentChatFragment activeChatFragment = RecentChatFragment.newInstance();
activeChatFragment.setInitialChats(activeChats);
return activeChatFragment;
} else {
view = convertView;
fragment = (ChatViewerFragment) view.getTag();
chatIndex = realPosition -1;
}
fragment.setChat(chat);
return view;
AbstractChat chat = activeChats.get(chatIndex);
return ChatViewerFragment.newInstance(chat.getAccount(), chat.getUser());
}
@Override
public void saveState(View view) {
((ChatViewerFragment) view.getTag()).saveState();
public void onChange() {
activeChats = new ArrayList<>(MessageManager.getInstance().getActiveChats());
if (intent != null && !activeChats.contains(intent)) {
activeChats.add(intent);
}
Collections.sort(activeChats, new Comparator<AbstractChat>() {
@Override
public int compare(AbstractChat lhs, AbstractChat rhs) {
return lhs.getCreationTime().compareTo(rhs.getCreationTime());
}
});
notifyDataSetChanged();
}
@Override
public void hidePages(View view) {
((ChatViewerFragment) view.getTag()).hidePages();
public int getPageNumber(String account, String user) {
for (int position = 0; position < activeChats.size(); position++) {
if (activeChats.get(position).equals(account, user)) {
return position + 1;
}
}
return 0;
}
@Override
public void showPages(View view) {
((ChatViewerFragment) view.getTag()).showPages();
public AbstractChat getChatByPageNumber(int virtualPosition) {
int realPosition = virtualPosition % getRealCount();
int chatIndex = realPosition - 1;
if (chatIndex < 0) {
return null;
}
return activeChats.get(chatIndex);
}
/**
* Must be called on changes in chat (message sent, received, etc.).
*/
public void onChatChange(View view, boolean incomingMessage) {
((ChatViewerFragment) view.getTag()).onChatChange(incomingMessage);
@Override
public void startUpdate(ViewGroup container) {
if (currentFragment instanceof ChatViewerFragment) {
((ChatViewerFragment)currentFragment).saveInputState();
}
super.startUpdate(container);
}
/**
* Must be called on changes in chat (message sent, received, etc.).
*/
public void onPrepareOptionsMenu(View view, Menu menu) {
((ChatViewerFragment) view.getTag()).onPrepareOptionsMenu(menu);
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
finishUpdateListener.onChatViewAdapterFinishUpdate();
}
public void insertText(View view, String text) {
((ChatViewerFragment) view.getTag()).insertText(text);
public interface FinishUpdateListener {
public void onChatViewAdapterFinishUpdate();
}
@Override
public void onChange() {
activeChats = new ArrayList<AbstractChat>(MessageManager.getInstance()
.getActiveChats());
if (intentPosition != -1) {
int index = activeChats.indexOf(intent);
AbstractChat chat;
if (index == -1)
chat = intent;
else
chat = activeChats.remove(index);
activeChats.add(Math.min(intentPosition, activeChats.size()), chat);
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
currentFragment = ((Fragment) object);
}
notifyDataSetChanged();
super.setPrimaryItem(container, position, object);
}
public int getPosition(String account, String user) {
for (int position = 0; position < activeChats.size(); position++)
if (activeChats.get(position).equals(account, user))
return position;
return -1;
public Fragment getCurrentFragment() {
return currentFragment;
}
}
public ArrayList<AbstractChat> getActiveChats() {
return activeChats;
}
}
\ No newline at end of file
......@@ -14,13 +14,13 @@
*/
package com.xabber.android.ui.dialog;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import java.util.ArrayList;
/**
* Base dialog fragment.
......
package com.xabber.android.ui.dialog;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.view.ViewGroup;
......@@ -14,6 +12,8 @@ import com.xabber.android.data.roster.RosterContact;
import com.xabber.android.data.roster.RosterManager;
import com.xabber.android.ui.adapter.AccountChooseAdapter;
import java.util.ArrayList;
public class AccountChooseDialogFragment extends AbstractDialogFragment {
private static final String USER = "USER";
......
......@@ -14,14 +14,12 @@
*/
package com.xabber.android.ui.dialog;
import java.io.File;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.DialogFragment;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
......@@ -34,6 +32,8 @@ import com.xabber.android.data.message.MessageManager;
import com.xabber.android.data.roster.RosterManager;
import com.xabber.androiddev.R;
import java.io.File;
public class ChatExportDialogFragment extends ConfirmDialogFragment {
private static final String ACCOUNT = "ACCOUNT";
......@@ -45,8 +45,7 @@ public class ChatExportDialogFragment extends ConfirmDialogFragment {
* @return
*/
public static DialogFragment newInstance(String account, String user) {
return new ChatExportDialogFragment().putAgrument(ACCOUNT, account)
.putAgrument(USER, user);
return new ChatExportDialogFragment().putAgrument(ACCOUNT, account).putAgrument(USER, user);
}
private String account;
......
package com.xabber.android.ui.dialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import com.xabber.android.data.Application;
import com.xabber.android.data.NetworkException;
......
package com.xabber.android.ui.dialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import com.xabber.android.data.SettingsManager;
import com.xabber.android.data.account.AccountManager;
......
......@@ -14,17 +14,17 @@
*/
package com.xabber.android.ui.dialog;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.xabber.androiddev.R;
import java.util.ArrayList;
public class GroupAddDialogFragment extends ConfirmDialogFragment {
private static final String GROUPS = "GROUPS";
......
package com.xabber.android.ui.dialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import com.xabber.android.data.Application;
import com.xabber.android.data.NetworkException;
......
......@@ -16,7 +16,7 @@ package com.xabber.android.ui.dialog;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
......
package com.xabber.android.ui.dialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import com.xabber.android.data.account.AccountManager;
import com.xabber.android.data.extension.muc.MUCManager;
......
package com.xabber.android.ui.dialog;
import android.app.AlertDialog.Builder;
import android.support.v4.app.DialogFragment;
import android.app.DialogFragment;
import com.xabber.android.data.SettingsManager;
import com.xabber.androiddev.R;
......
......@@ -14,8 +14,6 @@
*/
package com.xabber.android.ui.helper;
import org.jivesoftware.smackx.ChatState;
import android.app.Activity;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
......@@ -24,7 +22,6 @@ import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
......@@ -34,6 +31,8 @@ import com.xabber.android.data.roster.AbstractContact;
import com.xabber.android.utils.Emoticons;
import com.xabber.androiddev.R;
import org.jivesoftware.smackx.ChatState;
/**
* Helper class to update <code>contact_title.xml</code>.
*
......@@ -51,47 +50,53 @@ public class ContactTitleInflater {
*/
public static void updateTitle(View titleView, final Activity activity,
AbstractContact abstractContact) {
final TypedArray typedArray = activity
.obtainStyledAttributes(R.styleable.ContactList);
final TypedArray typedArray = activity.obtainStyledAttributes(R.styleable.ContactList);
final Drawable titleAccountBackground = typedArray
.getDrawable(R.styleable.ContactList_titleAccountBackground);
typedArray.recycle();
final TextView nameView = (TextView) titleView.findViewById(R.id.name);
final ImageView avatarView = (ImageView) titleView
.findViewById(R.id.avatar);
final ImageView statusModeView = (ImageView) titleView
.findViewById(R.id.status_mode);
final TextView statusTextView = (TextView) titleView
.findViewById(R.id.status_text);
final ImageView avatarView = (ImageView) titleView.findViewById(R.id.avatar);
final ImageView statusModeView = (ImageView) titleView.findViewById(R.id.status_mode);
final TextView statusTextView = (TextView) titleView.findViewById(R.id.status_text);
final View shadowView = titleView.findViewById(R.id.shadow);
titleView.setBackgroundDrawable(titleAccountBackground);
nameView.setText(abstractContact.getName());
statusModeView.setImageLevel(abstractContact.getStatusMode()
.getStatusLevel());
titleView.getBackground().setLevel(
AccountManager.getInstance().getColorLevel(
statusModeView.setImageLevel(abstractContact.getStatusMode().getStatusLevel());
titleView.getBackground().setLevel(AccountManager.getInstance().getColorLevel(
abstractContact.getAccount()));
avatarView.setImageDrawable(abstractContact.getAvatar());
setStatusText(activity, abstractContact, statusTextView);
final Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.shadow);
final BitmapDrawable shadowDrawable = new BitmapDrawable(bitmap);
shadowDrawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
shadowView.setBackgroundDrawable(shadowDrawable);
if (abstractContact.isConnected()) {
shadowView.setVisibility(View.GONE);
} else {
shadowView.setVisibility(View.VISIBLE);
}
}
private static void setStatusText(Activity activity, AbstractContact abstractContact,
TextView statusTextView) {
ChatState chatState = ChatStateManager.getInstance().getChatState(
abstractContact.getAccount(), abstractContact.getUser());
final CharSequence statusText;
if (chatState == ChatState.composing)
if (chatState == ChatState.composing) {
statusText = activity.getString(R.string.chat_state_composing);
else if (chatState == ChatState.paused)
} else if (chatState == ChatState.paused) {
statusText = activity.getString(R.string.chat_state_paused);
else
statusText = Emoticons.getSmiledText(activity,
abstractContact.getStatusText());
} else {
statusText = Emoticons.getSmiledText(activity, abstractContact.getStatusText());
}
statusTextView.setText(statusText);
final Bitmap bitmap = BitmapFactory.decodeResource(
activity.getResources(), R.drawable.shadow);
final BitmapDrawable shadowDrawable = new BitmapDrawable(bitmap);
shadowDrawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
shadowView.setBackgroundDrawable(shadowDrawable);
if (abstractContact.isConnected())
shadowView.setVisibility(View.GONE);
else
shadowView.setVisibility(View.VISIBLE);
}
}
......@@ -33,11 +33,9 @@ import com.xabber.android.data.roster.AbstractContact;
import com.xabber.android.data.roster.GroupManager;
import com.xabber.android.data.roster.PresenceManager;
import com.xabber.android.data.roster.ShowOfflineMode;
import com.xabber.android.ui.preferences.AccountEditor;
import com.xabber.android.ui.ChatViewer;
import com.xabber.android.ui.ContactAdd;
import com.xabber.android.ui.ContactEditor;
import com.xabber.android.ui.preferences.ContactViewer;
import com.xabber.android.ui.MUCEditor;
import com.xabber.android.ui.StatusEditor;
import com.xabber.android.ui.adapter.UpdatableAdapter;
......@@ -45,6 +43,8 @@ import com.xabber.android.ui.dialog.ContactDeleteDialogFragment;
import com.xabber.android.ui.dialog.GroupDeleteDialogFragment;
import com.xabber.android.ui.dialog.GroupRenameDialogFragment;
import com.xabber.android.ui.dialog.MUCDeleteDialogFragment;
import com.xabber.android.ui.preferences.AccountEditor;
import com.xabber.android.ui.preferences.ContactViewer;
import com.xabber.androiddev.R;
/**
......@@ -84,7 +84,7 @@ public class ContextMenuHelper {
@Override
public boolean onMenuItemClick(MenuItem item) {
MUCDeleteDialogFragment.newInstance(account, user)
.show(activity.getSupportFragmentManager(),
.show(activity.getFragmentManager(),
"MUC_DELETE");
return true;
}
......@@ -131,8 +131,7 @@ public class ContextMenuHelper {
@Override
public boolean onMenuItemClick(MenuItem item) {
ContactDeleteDialogFragment.newInstance(account,
user).show(
activity.getSupportFragmentManager(),
user).show(activity.getFragmentManager(),
"CONTACT_DELETE");
return true;
}
......@@ -225,8 +224,7 @@ public class ContextMenuHelper {
account == GroupManager.NO_ACCOUNT ? null
: account,
group == GroupManager.NO_GROUP ? null
: group).show(
activity.getSupportFragmentManager(),
: group).show(activity.getFragmentManager(),
"GROUP_RENAME");
return true;
}
......@@ -241,9 +239,7 @@ public class ContextMenuHelper {
.newInstance(
account == GroupManager.NO_ACCOUNT ? null
: account, group)
.show(activity
.getSupportFragmentManager(),
"GROUP_DELETE");
.show(activity.getFragmentManager(), "GROUP_DELETE");
return true;
}
});
......
/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
*/
package com.xabber.android.ui.widget;
import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.Scroller;
import com.xabber.android.data.LogManager;
import com.xabber.android.ui.adapter.SaveStateAdapter;
import com.xabber.androiddev.R;
/**
* Widget to switch between chat pages.
* <p/>
* Warning: This class is to be replaced.
*
* @author alexander.ivanov
*/
public class PageSwitcher extends ViewGroup {
public static final boolean LOG = false;
/**
* Distance a touch can wander before we think the user is scrolling.
*/
private final int touchSlop;
private final DataSetObserver dataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
dataChanged = true;
requestLayout();
}
@Override
public void onInvalidated() {
dataChanged = true;
requestLayout();
}
};
private boolean dataChanged;
private SaveStateAdapter adapter;
private int widthMeasureSpec;
private int heightMeasureSpec;
/**
* User is currently dragging this view.
*/
private boolean isBeingDragged;
/**
* Drag was interrupted.
*/
private boolean dragWasCanceled;
/**
* Position of down touch.
*/
private float touchX;
/**
* Initial scroll position.
*/
private int initialScrollX;
/**
* {@link Scroller#isFinished()} is <code>false</code> when being flung.
*/
private final Scroller scroller;
/**
* The listener that receives notifications when an item is selected,
* unselected or removed.
*/
private OnSelectListener onSelectListener;
/**
* Selected position in adapter. Can be incorrect while {@link #dataChanged}
* is <code>true</code> .
*/
private int selectedPosition;
private View selectedView;
/**
* Previous selected object (before data changes).
*/
private Object previousSelectedObject;
/**
* Visible but not selected position.
*/
private int visiblePosition;
/**
* Visible but not selected view.
*/
private View visibleView;
/**
* Previous visible object (before data changes).
*/
private Object previousVisibleObject;
public PageSwitcher(Context context, AttributeSet attrs) {
super(context, attrs);
touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
dataChanged = true;
adapter = null;
widthMeasureSpec = 0;
heightMeasureSpec = 0;
isBeingDragged = false;
dragWasCanceled = false;
touchX = 0;
initialScrollX = 0;
scroller = new Scroller(getContext());
onSelectListener = null;
selectedPosition = 0;
selectedView = null;
previousSelectedObject = null;
visiblePosition = 0;
visibleView = null;
previousVisibleObject = null;
}
/**
* Sets adapter. Request update and set initial selected position.
*
* @param adapter
*/
public void setAdapter(SaveStateAdapter adapter) {
if (adapter == null)
throw new IllegalStateException();
if (this.adapter != null)
this.adapter.unregisterDataSetObserver(dataSetObserver);
this.adapter = adapter;
this.adapter.registerDataSetObserver(dataSetObserver);
// dataChanged will be set in setSelection().
setSelection(0);
}
/**
* @return Assigned adapter.
*/
public Adapter getAdapter() {
return adapter;
}
/**
* Register a callback to be invoked when an item in this View has been
* selected, unselected or removed.
*
* @param listener The callback that will run
*/
public void setOnSelectListener(OnSelectListener listener) {
onSelectListener = listener;
}
public OnSelectListener getOnSelectListener() {
return onSelectListener;
}
/**
* @return Selected position in adapter.
*/
public int getSelectedItemPosition() {
if (adapter == null)
throw new IllegalStateException();
return selectedPosition;
}
/**
* @return Selected item in adapter or <code>null</code> if there is no
* elements.
*/
public Object getSelectedItem() {
if (adapter == null)
throw new IllegalStateException();
if (selectedPosition < 0 || selectedPosition >= adapter.getCount())
return null;
else
return adapter.getItem(selectedPosition);
}
/**
* @return Visible item from adapter or <code>null</code> if there is no
* visible elements (selected element still can exists).
*/
public Object getVisibleItem() {
if (adapter == null)
throw new IllegalStateException();
if (visiblePosition < 0 || visiblePosition >= adapter.getCount())
return null;
else
return adapter.getItem(visiblePosition);
}
/**
* @return Selected view.
*/
public View getSelectedView() {
if (adapter == null)
throw new IllegalStateException();
return selectedView;
}
/**
* @return Visible view.
*/
public View getVisibleView() {
if (adapter == null)
throw new IllegalStateException();
return visibleView;
}
/**
* @return number of items or zero if adapter is <code>null</code> .
*/
private int getCount() {
return adapter == null ? 0 : adapter.getCount();
}
/**
* Returns correct position.
*
* @param position
* @return value between <code>0</code> and <code>count - 1</code>.
*/
private int correntPosition(int position) {
final int count = getCount();
if (position >= count)
return 0;
if (position < 0)
return count - 1;
return position;
}
/**
* Gets view.
*
* @param position in adapter.
* @param x position in layout.
* @param convertView previous view.
* @param update whether we need force update underlying view.
* @param layout whether we need to update layout (remeasure).
* @return
*/
private View getView(int position, int x, View convertView, boolean update,
boolean layout) {
final View view;
if (convertView == null) {
if (LOG)
LogManager.i(this, "new view");
view = adapter.getView(position, null, this);
} else if (update) {
if (LOG)
LogManager.i(this, "update view");
view = adapter.getView(position, convertView, this);
} else {
view = convertView;
}
if (view != convertView) {
if (LOG)
LogManager.i(this, "init view");
LayoutParams layoutParams = view.getLayoutParams();
if (layoutParams == null)
layoutParams = new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
addViewInLayout(view, 0, layoutParams, true);
}
if (update || layout || view.getLeft() != x) {
if (LOG)
LogManager.i(this, "layout view");
// We must measure ListView after update to show items.
measureChild(view, widthMeasureSpec, heightMeasureSpec);
view.layout(x, 0, x + view.getMeasuredWidth(),
view.getMeasuredHeight());
}
return view;
}
/**
* Updates scrolling, creates views if necessary .
*
* @param layout whether we need to update layout (remeasure).
*/
private void update(boolean layout) {
// Process data change.
final int count = getCount();
if (dataChanged) {
if (previousSelectedObject != null)
for (int position = 0; position < count; position++)
if (adapter.getItem(position)
.equals(previousSelectedObject)) {
selectedPosition = position;
if (LOG)
LogManager.i(this, "Found selected position: "
+ selectedPosition);
break;
}
selectedPosition = correntPosition(selectedPosition);
}
// Process scrolling.
final int width = getWidth();
int scrollX = getScrollX();
if (width != 0) {
while (scrollX >= width) {
scrollX -= width;
initialScrollX -= width;
selectedPosition = correntPosition(selectedPosition + 1);
if (LOG)
LogManager.i(this, "scrollX >= width: " + selectedPosition);
}
while (scrollX <= -width) {
scrollX += width;
initialScrollX += width;
selectedPosition = correntPosition(selectedPosition - 1);
if (LOG)
LogManager
.i(this, "scrollX <= -width: " + selectedPosition);
}
}
// Process low count.
if (count < 2) {
if (LOG)
LogManager.i(this, "count < 2");
dragWasCanceled = true;
isBeingDragged = false;
if (!scroller.isFinished())
scroller.abortAnimation();
if (scrollX != 0)
scrollX = 0;
}
// Store focus.
final View focus;
if (selectedView != null)
focus = selectedView.findFocus();
else
focus = null;
// Process selected view.
if (count == 0) {
if (LOG)
LogManager.i(this, "count == 0");
selectedPosition = -1;
if (selectedView != null) {
if (onSelectListener != null)
onSelectListener.onUnselect();
adapter.saveState(selectedView);
removeViewInLayout(selectedView);
selectedView = null;
// We must invalidate to update view.
invalidate();
}
} else {
if (LOG)
LogManager.i(this, "count > 0");
// Exchange visible and selected views and previous objects.
final Object selectedObject = adapter.getItem(selectedPosition);
final boolean exchange = previousSelectedObject != null
&& previousVisibleObject != null
&& !previousSelectedObject.equals(selectedObject)
&& previousVisibleObject.equals(selectedObject);
if (exchange) {
Object tempObject = previousSelectedObject;
previousSelectedObject = previousVisibleObject;
previousVisibleObject = tempObject;
View view = selectedView;
selectedView = visibleView;
visibleView = view;
}
// Update view.
final boolean update = dataChanged
|| previousSelectedObject == null
|| !previousSelectedObject.equals(selectedObject);
selectedView = getView(selectedPosition, 0, selectedView, update,
layout);
previousSelectedObject = selectedObject;
if (update || exchange)
if (onSelectListener != null)
onSelectListener.onSelect();
// Enable focusable.
if (selectedView instanceof ViewGroup)
((ViewGroup) selectedView)
.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
else
selectedView.setFocusable(true);
}
// Process visible (not selected) view.
if (count < 2) {
if (LOG)
LogManager.i(this, "count < 2 || scrollX == 0");
visiblePosition = -1;
if (visibleView != null) {
adapter.saveState(visibleView);
removeViewInLayout(visibleView);
visibleView = null;
}
} else {
// Calculate position.
final int visibleX;
if (scrollX > 0) {
if (LOG)
LogManager.i(this, "scrollX > 0");
visiblePosition = correntPosition(selectedPosition + 1);
visibleX = width;
} else {
if (LOG)
LogManager.i(this, "scrollX < 0");
visiblePosition = correntPosition(selectedPosition - 1);
visibleX = -width;
}
// Update view.
final Object visibleObject = adapter.getItem(visiblePosition);
final boolean update = dataChanged || previousVisibleObject == null
|| !previousVisibleObject.equals(visibleObject);
visibleView = getView(visiblePosition, visibleX, visibleView,
update, layout);
previousVisibleObject = visibleObject;
// Disable focusable.
if (visibleView instanceof ViewGroup)
((ViewGroup) visibleView)
.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
else
visibleView.setFocusable(false);
}
// Restore focus by ID.
if (selectedView != null) {
View target;
if (focus == null || focus.getId() == View.NO_ID)
target = null;
else
target = selectedView.findViewById(focus.getId());
if (target == null)
target = selectedView.findViewById(R.id.chat_input);
target.requestFocus();
}
if (scrollX == 0) {
if (LOG)
LogManager.i(this, "Scroll X == 0");
if (visibleView != null)
adapter.hidePages(visibleView);
if (selectedView != null)
adapter.hidePages(selectedView);
} else {
if (LOG)
LogManager.i(this, "Scroll X != 0");
if (visibleView != null)
adapter.showPages(visibleView);
if (selectedView != null)
adapter.showPages(selectedView);
}
super.scrollTo(scrollX, 0);
dataChanged = false;
}
/**
* Save state of views. Must be called on activity pause.
*/
public void saveState() {
if (visibleView != null)
adapter.saveState(visibleView);
if (selectedView != null)
adapter.saveState(selectedView);
}
/**
* Selects an item. Immediately scroll to this position.
*
* @param position
*/
public void setSelection(int position) {
if (adapter == null)
throw new IllegalStateException();
dataChanged = true;
isBeingDragged = false;
dragWasCanceled = true;
if (!scroller.isFinished())
scroller.abortAnimation();
if (getScrollX() != 0 || getScrollY() != 0)
super.scrollTo(0, 0);
previousSelectedObject = null;
selectedPosition = position;
if (LOG)
LogManager.i(this, "setSelection: " + selectedPosition);
update(false);
if (selectedView == null)
return;
ListView listView = (ListView) selectedView
.findViewById(android.R.id.list);
listView.setAdapter(listView.getAdapter());
}
/**
* Stop any movements.
*/
public void stopMovement() {
isBeingDragged = false;
dragWasCanceled = true;
if (!scroller.isFinished())
scroller.abortAnimation();
if (getScrollX() != 0 || getScrollY() != 0)
super.scrollTo(0, 0);
update(false);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
this.widthMeasureSpec = widthMeasureSpec;
this.heightMeasureSpec = heightMeasureSpec;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
if (LOG)
LogManager.i(this, "onLayout");
update(true);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (getCount() <= 1) {
isBeingDragged = false;
dragWasCanceled = true;
return false;
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN)
dragWasCanceled = false;
if (dragWasCanceled)
return false;
final float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
touchX = x;
initialScrollX = getScrollX();
if (!scroller.isFinished()) {
scroller.abortAnimation();
isBeingDragged = true;
} else {
isBeingDragged = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(x - touchX) > touchSlop)
isBeingDragged = true;
// requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
break;
}
return isBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (getCount() <= 1)
dragWasCanceled = true;
if (dragWasCanceled)
return false;
final int action = event.getAction();
final float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(x - touchX) > touchSlop)
isBeingDragged = true;
if (isBeingDragged) {
if (LOG)
LogManager.i(this, "onTouchEvent - MOVE");
scrollTo((int) (touchX - x) + initialScrollX, 0);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (isBeingDragged) {
final int scroll = getScrollX();
final int width = getWidth();
final int center = width / 2;
final int target;
if (scroll > center) {
target = width;
} else if (scroll < -center) {
target = -width;
} else {
target = 0;
}
scroller.startScroll(scroll, 0, target - scroll, 0);
invalidate();
}
isBeingDragged = false;
break;
}
return true;
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
if (scroller.getCurrX() == scroller.getFinalX()
&& scroller.getCurrY() == scroller.getFinalY())
scroller.abortAnimation();
if (LOG)
LogManager.i(this, "computeScroll");
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
@Override
public void scrollTo(int x, int y) {
if (LOG)
LogManager.i(this, "scrollTo: " + x + "," + y);
super.scrollTo(x, y);
update(false);
}
public interface OnSelectListener {
/**
* Callback method to be invoked when an item has been selected.
*/
void onSelect();
/**
* Callback method to be invoked when an item has been unselected.
*/
void onUnselect();
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.antonyt.infiniteviewpager.InfiniteViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2013, Redsolution LTD. All rights reserved.
This file is part of Xabber project; you can redistribute it and/or
modify it under the terms of the GNU General Public License, Version 3.
Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
-->
<com.xabber.android.ui.widget.PageSwitcher
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/switcher"
/>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2013, Redsolution LTD. All rights reserved.
This file is part of Xabber project; you can redistribute it and/or
modify it under the terms of the GNU General Public License, Version 3.
Xabber 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 this program. If not, see http://www.gnu.org/licenses/.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_message_repeat"
android:visible="false"
android:title="@string/message_repeat" />
<item android:id="@+id/action_message_quote"
android:title="@string/message_quote" />
<item android:id="@+id/action_message_copy"
android:title="@string/message_copy" />
<item android:id="@+id/action_message_remove"
android:title="@string/message_remove" />
</menu>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment