Add Contact Details

This commit is contained in:
Paul Schaub 2018-02-13 21:02:46 +01:00
parent 350e665cef
commit 7bc1207581
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
64 changed files with 1144 additions and 221 deletions

View file

@ -27,6 +27,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".mvp.view.MainActivity" /> <activity android:name=".mvp.view.MainActivity" />
<activity android:name=".mvp.view.ContactDetailActivity" />
<meta-data <meta-data
android:name="com.google.android.gms.car.application" android:name="com.google.android.gms.car.application"

View file

@ -1,3 +1,20 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.mvp; package de.vanitasvitae.slam.mvp;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;

View file

@ -17,19 +17,19 @@
*/ */
package de.vanitasvitae.slam.mvp.contracts; package de.vanitasvitae.slam.mvp.contracts;
import org.jivesoftware.smack.packet.Presence;
import java.util.List; import java.util.List;
import de.vanitasvitae.slam.xmpp.Resource;
/** /**
* Model-View-Presenter contract for the {@link de.vanitasvitae.slam.mvp.view.ContactDetailFragment}. * Model-View-Presenter contract for the {@link de.vanitasvitae.slam.mvp.view.ContactDetailActivity}.
*/ */
public interface ContactDetailContract { public interface ContactDetailContract {
interface View { interface View {
void setContactAvatar(); void setContactAvatar();
void setNickname(String nickname); void setNickname(String nickname);
void setPresence(Presence presence); void setResources(List<Resource> presences);
void clearFingerprints(); void clearFingerprints();
void addFingerprints(List<?> fingerprints); void addFingerprints(List<?> fingerprints);
} }

View file

@ -22,6 +22,7 @@ import org.jxmpp.jid.BareJid;
import java.util.List; import java.util.List;
import de.vanitasvitae.slam.mvp.view.ConversationListFragment; import de.vanitasvitae.slam.mvp.view.ConversationListFragment;
import de.vanitasvitae.slam.xmpp.Contact;
/** /**
* Model-View-Presenter contract for the {@link ConversationListFragment}. * Model-View-Presenter contract for the {@link ConversationListFragment}.
@ -31,12 +32,13 @@ import de.vanitasvitae.slam.mvp.view.ConversationListFragment;
public interface ContactListContract { public interface ContactListContract {
interface View { interface View {
void addContactListItems(List<?> contacts); void addContactListItems(List<Contact> contacts);
void clearContactListItems(); void clearContactListItems();
void onUpdateContactPresence(); void onUpdateContactPresence();
void showContactListLoadingIndicator(); void showContactListLoadingIndicator();
void hideContactListLoadingIndicator(); void hideContactListLoadingIndicator();
void navigateToConversation(BareJid contact); void navigateToConversation(BareJid contact);
void navigateToContactDetail(BareJid contact);
} }
interface Presenter { interface Presenter {

View file

@ -32,6 +32,7 @@ public interface ConversationListContract {
interface View { interface View {
void populateConversationList(List<Conversation> conversations); void populateConversationList(List<Conversation> conversations);
void navigateToConversation(BareJid contact); void navigateToConversation(BareJid contact);
void navigateToContactDetail(BareJid contact);
} }
interface Presenter { interface Presenter {

View file

@ -17,9 +17,15 @@
*/ */
package de.vanitasvitae.slam.mvp.presenter.dummy; package de.vanitasvitae.slam.mvp.presenter.dummy;
import android.util.Log;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
import java.util.ArrayList;
import java.util.List;
import de.vanitasvitae.slam.mvp.contracts.ContactDetailContract; import de.vanitasvitae.slam.mvp.contracts.ContactDetailContract;
import de.vanitasvitae.slam.xmpp.Resource;
public class DummyContactDetailPresenter implements ContactDetailContract.Presenter { public class DummyContactDetailPresenter implements ContactDetailContract.Presenter {
@ -28,6 +34,17 @@ public class DummyContactDetailPresenter implements ContactDetailContract.Presen
public DummyContactDetailPresenter(ContactDetailContract.View view) { public DummyContactDetailPresenter(ContactDetailContract.View view) {
this.view = view; this.view = view;
Log.d("SLAM", "Set dummy resources");
view.setResources(dummyResources());
}
private List<Resource> dummyResources() {
List<Resource> r = new ArrayList<>();
r.add(new Resource("Mobile", "Away since 15min", "Conversations 1.23.4", "Android"));
r.add(new Resource("Laptop", "online", "Gajim 16.0.6", "Arch Linux"));
r.add(new Resource("Desktop", "I like coffee!", "Dino", "Debian Stable"));
return r;
} }
@Override @Override

View file

@ -17,6 +17,8 @@
*/ */
package de.vanitasvitae.slam.mvp.presenter.dummy; package de.vanitasvitae.slam.mvp.presenter.dummy;
import de.vanitasvitae.slam.mvp.DummyPresenterFactory;
import de.vanitasvitae.slam.mvp.DummyStore;
import de.vanitasvitae.slam.mvp.contracts.ContactListContract; import de.vanitasvitae.slam.mvp.contracts.ContactListContract;
public class DummyContactListPresenter implements ContactListContract.Presenter { public class DummyContactListPresenter implements ContactListContract.Presenter {
@ -25,6 +27,7 @@ public class DummyContactListPresenter implements ContactListContract.Presenter
public DummyContactListPresenter(ContactListContract.View view) { public DummyContactListPresenter(ContactListContract.View view) {
this.view = view; this.view = view;
view.addContactListItems(DummyPresenterFactory.STORE.contacts);
} }
@Override @Override

View file

@ -0,0 +1,174 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.mvp.view;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.hdodenhof.circleimageview.CircleImageView;
import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.mvp.PresenterFactory;
import de.vanitasvitae.slam.mvp.contracts.ContactDetailContract;
import de.vanitasvitae.slam.mvp.view.abstr.ThemedAppCompatActivity;
import de.vanitasvitae.slam.xmpp.Resource;
/**
* Main activity that hosts some fragments.
*/
public class ContactDetailActivity extends ThemedAppCompatActivity implements ContactDetailContract.View, AppBarLayout.OnOffsetChangedListener {
public static final String TAG = "Slam!";
private final ContactDetailContract.Presenter presenter;
private final ContactDetailResourcesFragment resourcesFragment = new ContactDetailResourcesFragment();
private final ContactDetailInfoFragment infoFragment = new ContactDetailInfoFragment();
private final ContactDetailSecurityFragment securityFragment = new ContactDetailSecurityFragment();
@BindView(R.id.contact_detail_toolbar)
Toolbar toolbar;
@BindView(R.id.contact_detail_appbar_layout)
AppBarLayout appBarLayout;
@BindView(R.id.contact_detail_tab_layout)
TabLayout tabLayout;
@BindView(R.id.contact_detail_viewpager)
ViewPager pager;
@BindView(R.id.contact_detail_profile_circle)
CircleImageView profileCircle;
private int mMaxScrollSize;
private boolean isProfileCircleShown;
private int animateProfileCirclePercent = 20;
public ContactDetailActivity() {
this.presenter = PresenterFactory.getInstance().createContactDetailPresenter(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_detail);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
appBarLayout.addOnOffsetChangedListener(this);
pager.setAdapter(new DetailFragmentPagerAdapter(getSupportFragmentManager()));
tabLayout.setupWithViewPager(pager);
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
if (mMaxScrollSize == 0)
mMaxScrollSize = appBarLayout.getTotalScrollRange();
int percentage = (Math.abs(i)) * 100 / mMaxScrollSize;
if (percentage >= animateProfileCirclePercent && isProfileCircleShown) {
isProfileCircleShown = false;
profileCircle.animate()
.scaleY(0).scaleX(0)
.setDuration(200)
.start();
}
if (percentage <= animateProfileCirclePercent && !isProfileCircleShown) {
isProfileCircleShown = true;
profileCircle.animate()
.scaleY(1).scaleX(1)
.start();
}
}
@Override
public void setContactAvatar() {
}
@Override
public void setNickname(String nickname) {
}
@Override
public void setResources(List<Resource> resources) {
resourcesFragment.setResources(resources);
}
@Override
public void clearFingerprints() {
}
@Override
public void addFingerprints(List<?> fingerprints) {
}
class DetailFragmentPagerAdapter extends FragmentPagerAdapter {
public DetailFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return infoFragment;
case 1: return resourcesFragment;
case 2: return securityFragment;
default: return infoFragment;
}
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
//TODO
switch (position) {
case 0:
return "Info";
case 1:
return "Devices";
case 2:
return "Security";
}
return null;
}
}
}

View file

@ -17,60 +17,25 @@
*/ */
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.jivesoftware.smack.packet.Presence;
import org.jxmpp.jid.BareJid;
import java.util.List;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import de.vanitasvitae.slam.R; import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.mvp.PresenterFactory;
import de.vanitasvitae.slam.mvp.contracts.ContactDetailContract;
public class ContactDetailFragment extends Fragment implements ContactDetailContract.View { /**
* Created by Paul Schaub on 13.02.18.
private final ContactDetailContract.Presenter presenter; */
public class ContactDetailInfoFragment extends Fragment {
public ContactDetailFragment() {
this.presenter = PresenterFactory.getInstance().createContactDetailPresenter(this);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contact_detail, container, false); View view = inflater.inflate(R.layout.fragment_contact_detail__info, container, false);
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
return view; return view;
} }
@Override
public void setContactAvatar() {
}
@Override
public void setNickname(String nickname) {
}
@Override
public void setPresence(Presence presence) {
}
@Override
public void clearFingerprints() {
}
@Override
public void addFingerprints(List<?> fingerprints) {
}
} }

View file

@ -0,0 +1,97 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.mvp.view;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.xmpp.Resource;
/**
* Created by Paul Schaub on 13.02.18.
*/
public class ContactDetailResourcesFragment extends Fragment {
@BindView(R.id.contact_detail_resources_recycler_view)
RecyclerView resourcesRecyclerView;
private final List<Resource> resources = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contact_detail__resources, container, false);
ButterKnife.bind(this, view);
resourcesRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
resourcesRecyclerView.setAdapter(resourcesAdapter);
resourcesAdapter.notifyDataSetChanged();
return view;
}
public void setResources(List<Resource> resources) {
this.resources.clear();
this.resources.addAll(resources);
resourcesAdapter.notifyDataSetChanged();
}
private final RecyclerView.Adapter<ResourceView> resourcesAdapter = new RecyclerView.Adapter<ResourceView>() {
@Override
public ResourceView onCreateViewHolder(ViewGroup parent, int viewType) {
View resourceView = LayoutInflater.from(getActivity()).inflate(R.layout.item_resource, parent, false);
return new ResourceView(resourceView);
}
@Override
public void onBindViewHolder(final ResourceView holder, final int position) {
Resource resource = resources.get(holder.getAdapterPosition());
holder.bind(resource);
}
@Override
public int getItemCount() {
return resources.size();
}
};
static class ResourceView extends RecyclerView.ViewHolder {
public ResourceView(View itemView) {
super(itemView);
}
public void bind(Resource resource) {
((TextView)itemView.findViewById(R.id.resource__resource_value)).setText(resource.getResource());
((TextView)itemView.findViewById(R.id.resource__status_value)).setText(resource.getStatus());
((TextView)itemView.findViewById(R.id.resource__client_value)).setText(resource.getClient());
((TextView)itemView.findViewById(R.id.resource__system_value)).setText(resource.getSystem());
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.mvp.view;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import butterknife.ButterKnife;
import de.vanitasvitae.slam.R;
/**
* Created by Paul Schaub on 13.02.18.
*/
public class ContactDetailSecurityFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contact_detail__security, container, false);
ButterKnife.bind(this, view);
return view;
}
}

View file

@ -1,36 +1,86 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.vanitasvitae.slam.R; import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.mvp.PresenterFactory; import de.vanitasvitae.slam.mvp.PresenterFactory;
import de.vanitasvitae.slam.mvp.contracts.ContactListContract; import de.vanitasvitae.slam.mvp.contracts.ContactListContract;
import de.vanitasvitae.slam.ui.ContactListEntry;
import de.vanitasvitae.slam.xmpp.Contact;
/** /**
* Created by Paul Schaub on 11.02.18. * Created by Paul Schaub on 11.02.18.
*/ */
public class ContactListFragment extends Fragment implements ContactListContract.View { public class ContactListFragment extends Fragment implements ContactListContract.View {
@BindView(R.id.recycler_list)
RecyclerView recyclerView;
private final ContactListContract.Presenter presenter; private final ContactListContract.Presenter presenter;
private final List<Contact> contacts = new ArrayList<>();
public ContactListFragment() { public ContactListFragment() {
super(); super();
this.presenter = PresenterFactory.getInstance().createContactListPresenter(this); this.presenter = PresenterFactory.getInstance().createContactListPresenter(this);
} }
@Override @Override
public void addContactListItems(List<?> contacts) { public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_conversation_contact_list, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(contactsAdapter);
contactsAdapter.notifyDataSetChanged();
}
@Override
public void addContactListItems(List<Contact> contacts) {
this.contacts.addAll(contacts);
contactsAdapter.notifyDataSetChanged();
} }
@Override @Override
public void clearContactListItems() { public void clearContactListItems() {
this.contacts.clear();
contactsAdapter.notifyDataSetChanged();
} }
@Override @Override
@ -48,6 +98,31 @@ public class ContactListFragment extends Fragment implements ContactListContract
} }
private final RecyclerView.Adapter<ContactListEntry> contactsAdapter = new RecyclerView.Adapter<ContactListEntry>() {
@Override
public ContactListEntry onCreateViewHolder(ViewGroup parent, int viewType) {
View contactView = LayoutInflater.from(getActivity()).inflate(R.layout.item_contact, parent, false);
return new ContactListEntry(contactView);
}
@Override
public void onBindViewHolder(ContactListEntry holder, int position) {
final Contact contact = contacts.get(holder.getAdapterPosition());
holder.bind(contact);
holder.setOnAvatarClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navigateToContactDetail(contact.getJid());
}
});
}
@Override
public int getItemCount() {
return contacts.size();
}
};
@Override @Override
public void navigateToConversation(BareJid contact) { public void navigateToConversation(BareJid contact) {
ConversationFragment fragment = new ConversationFragment(); ConversationFragment fragment = new ConversationFragment();
@ -60,4 +135,11 @@ public class ContactListFragment extends Fragment implements ContactListContract
.addToBackStack("conversation") .addToBackStack("conversation")
.commit(); .commit();
} }
@Override
public void navigateToContactDetail(BareJid contact) {
startActivity(new Intent(getContext(), ContactDetailActivity.class));
}
} }

View file

@ -17,8 +17,9 @@
*/ */
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -61,15 +62,7 @@ public class ConversationFragment extends Fragment implements ConversationContra
private final RecyclerView.Adapter<ChatMessageEntry> chatMessageAdapter = new RecyclerView.Adapter<ChatMessageEntry>() { private final RecyclerView.Adapter<ChatMessageEntry> chatMessageAdapter = new RecyclerView.Adapter<ChatMessageEntry>() {
@Override @Override
public ChatMessageEntry onCreateViewHolder(ViewGroup parent, int viewType) { public ChatMessageEntry onCreateViewHolder(ViewGroup parent, int viewType) {
View messageView = LayoutInflater.from(getActivity()).inflate(R.layout.item_chatmessage, parent, false); View messageView = LayoutInflater.from(getActivity()).inflate(R.layout.item_conversation_message, parent, false);
messageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new ContactDetailFragment())
.commit();
}
});
return new ChatMessageEntry(messageView); return new ChatMessageEntry(messageView);
} }
@ -83,6 +76,13 @@ public class ConversationFragment extends Fragment implements ConversationContra
((TextView)content).setText(message.getBody()); ((TextView)content).setText(message.getBody());
holder.bind(sender, role, content, "now"); holder.bind(sender, role, content, "now");
holder.setOnAvatarClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navigateToContactProfile();
}
});
} }
@Override @Override
@ -99,7 +99,7 @@ public class ConversationFragment extends Fragment implements ConversationContra
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat, container, false); View view = inflater.inflate(R.layout.fragment_conversation, container, false);
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
Bundle arguments = getArguments(); Bundle arguments = getArguments();
@ -145,10 +145,6 @@ public class ConversationFragment extends Fragment implements ConversationContra
@Override @Override
public void navigateToContactProfile() { public void navigateToContactProfile() {
getFragmentManager().beginTransaction() startActivity(new Intent(getContext(), ContactDetailActivity.class));
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.add(R.id.fragment_container, new ContactDetailFragment())
.addToBackStack("detail")
.commit();
} }
} }

View file

@ -17,8 +17,9 @@
*/ */
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -45,45 +46,12 @@ import de.vanitasvitae.slam.xmpp.Conversation;
*/ */
public class ConversationListFragment extends Fragment implements ConversationListContract.View { public class ConversationListFragment extends Fragment implements ConversationListContract.View {
@BindView(R.id.recycler_chatlist) @BindView(R.id.recycler_list)
RecyclerView recyclerView; RecyclerView recyclerView;
private final ConversationListContract.Presenter presenter; private final ConversationListContract.Presenter presenter;
private final List<Conversation> conversations = new ArrayList<>(); private final List<Conversation> conversations = new ArrayList<>();
private final RecyclerView.Adapter<ConversationEntry> conversationEntryAdapter = new RecyclerView.Adapter<ConversationEntry>() {
@Override
public ConversationEntry onCreateViewHolder(ViewGroup parent, int viewType) {
View conversationView = LayoutInflater.from(getActivity()).inflate(R.layout.chatlist_singlechat, parent, false);
return new ConversationEntry(conversationView);
}
@Override
public void onBindViewHolder(final ConversationEntry holder, final int position) {
Conversation conversation = conversations.get(holder.getAdapterPosition());
String name = conversation.getContact().getNickname();
holder.bind(
name != null ? name : conversation.getContact().getJid().toString(),
conversation.getLastMessage(),
conversation.getDate(),
true);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getAdapterPosition();
navigateToConversation(conversations.get(pos).getContact().getJid());
}
});
}
@Override
public int getItemCount() {
return conversations.size();
}
};
public ConversationListFragment() { public ConversationListFragment() {
super(); super();
this.presenter = PresenterFactory.getInstance().createConversationListPresenter(this); this.presenter = PresenterFactory.getInstance().createConversationListPresenter(this);
@ -92,7 +60,7 @@ public class ConversationListFragment extends Fragment implements ConversationLi
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chatlist, container, false); View view = inflater.inflate(R.layout.fragment_conversation_contact_list, container, false);
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
return view; return view;
} }
@ -120,8 +88,52 @@ public class ConversationListFragment extends Fragment implements ConversationLi
fragment.setArguments(bundle); fragment.setArguments(bundle);
getFragmentManager().beginTransaction() getFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment) .replace(R.id.fragment_container, fragment)
.addToBackStack("conversation") .addToBackStack("conversation")
.commit(); .commit();
} }
@Override
public void navigateToContactDetail(BareJid contact) {
startActivity(new Intent(getContext(), ContactDetailActivity.class));
}
private final RecyclerView.Adapter<ConversationEntry> conversationEntryAdapter = new RecyclerView.Adapter<ConversationEntry>() {
@Override
public ConversationEntry onCreateViewHolder(ViewGroup parent, int viewType) {
View conversationView = LayoutInflater.from(getActivity()).inflate(R.layout.item_conversation_list, parent, false);
return new ConversationEntry(conversationView);
}
@Override
public void onBindViewHolder(final ConversationEntry holder, final int position) {
final Conversation conversation = conversations.get(holder.getAdapterPosition());
String name = conversation.getContact().getNickname();
holder.bind(
name != null ? name : conversation.getContact().getJid().toString(),
conversation.getLastMessage(),
conversation.getDate(),
true);
holder.setOnEntryClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navigateToConversation(conversation.getContact().getJid());
}
});
holder.setOnAvatarClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navigateToContactDetail(conversation.getContact().getJid());
}
});
}
@Override
public int getItemCount() {
return conversations.size();
}
};
} }

View file

@ -37,10 +37,8 @@ import butterknife.OnClick;
import de.vanitasvitae.slam.AbstractTextWatcher; import de.vanitasvitae.slam.AbstractTextWatcher;
import de.vanitasvitae.slam.EditorActionDoneListener; import de.vanitasvitae.slam.EditorActionDoneListener;
import de.vanitasvitae.slam.R; import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.mvp.DummyPresenterFactory;
import de.vanitasvitae.slam.mvp.PresenterFactory; import de.vanitasvitae.slam.mvp.PresenterFactory;
import de.vanitasvitae.slam.mvp.view.abstr.ThemedAppCompatActivity; import de.vanitasvitae.slam.mvp.view.abstr.ThemedAppCompatActivity;
import de.vanitasvitae.slam.mvp.presenter.dummy.DummyLoginPresenter;
import de.vanitasvitae.slam.mvp.contracts.LoginContract; import de.vanitasvitae.slam.mvp.contracts.LoginContract;
public class LoginActivity extends ThemedAppCompatActivity implements LoginContract.View { public class LoginActivity extends ThemedAppCompatActivity implements LoginContract.View {
@ -118,17 +116,17 @@ public class LoginActivity extends ThemedAppCompatActivity implements LoginContr
@Override @Override
public void showInvalidJidError() { public void showInvalidJidError() {
inputUsernameLayout.setError(getResources().getText(R.string.error_invalid_jid)); inputUsernameLayout.setError(getResources().getText(R.string.login__error_invalid_jid));
} }
@Override @Override
public void showInvalidPasswordError() { public void showInvalidPasswordError() {
inputPasswordLayout.setError(getResources().getText(R.string.error_invalid_password)); inputPasswordLayout.setError(getResources().getText(R.string.login__error_invalid_password));
} }
@Override @Override
public void showIncorrectPasswordError() { public void showIncorrectPasswordError() {
inputPasswordLayout.setError(getResources().getText(R.string.error_incorrect_password)); inputPasswordLayout.setError(getResources().getText(R.string.login__error_incorrect_password));
} }
@Override @Override
@ -143,7 +141,7 @@ public class LoginActivity extends ThemedAppCompatActivity implements LoginContr
@Override @Override
public void showServerNotFoundError() { public void showServerNotFoundError() {
Toast.makeText(this, R.string.error_server_not_found, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.login__error_server_not_found, Toast.LENGTH_LONG).show();
} }
@Override @Override

View file

@ -17,15 +17,17 @@
*/ */
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView; import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.Toast;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@ -35,7 +37,7 @@ import de.vanitasvitae.slam.mvp.view.abstr.ThemedAppCompatActivity;
/** /**
* Main activity that hosts some fragments. * Main activity that hosts some fragments.
*/ */
public class MainActivity extends ThemedAppCompatActivity { public class MainActivity extends ThemedAppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public static final String TAG = "Slam!"; public static final String TAG = "Slam!";
@ -62,11 +64,12 @@ public class MainActivity extends ThemedAppCompatActivity {
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
drawerToggle = new ActionBarDrawerToggle( drawerToggle = new ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.error_incorrect_password, R.string.error_invalid_jid); this, drawerLayout, toolbar, R.string.login__error_incorrect_password, R.string.login__error_invalid_jid);
drawerLayout.addDrawerListener(drawerToggle); drawerLayout.addDrawerListener(drawerToggle);
navigationView.setNavigationItemSelectedListener(this);
Fragment chatListFragment = new ConversationListFragment(); Fragment chatListFragment = new ConversationListFragment();
getFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, chatListFragment) .add(R.id.fragment_container, chatListFragment)
.addToBackStack("conversation_list") .addToBackStack("conversation_list")
.commit(); .commit();
@ -83,4 +86,28 @@ public class MainActivity extends ThemedAppCompatActivity {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
drawerToggle.syncState(); drawerToggle.syncState();
} }
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
drawerLayout.closeDrawers();
switch (item.getItemId()) {
case R.id.navdrawer__item_conversations:
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new ConversationListFragment())
.commit();
return true;
case R.id.navdrawer__item_contacts:
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new ContactListFragment())
.commit();
return true;
case R.id.navdrawer__item_bookmarks:
case R.id.navdrawer__item_blogging:
case R.id.navdrawer__item_settings:
Toast.makeText(this, R.string.feature_not_implemented, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
} }

View file

@ -17,7 +17,7 @@
*/ */
package de.vanitasvitae.slam.mvp.view; package de.vanitasvitae.slam.mvp.view;
import android.app.Fragment; import android.support.v4.app.Fragment;
import java.util.List; import java.util.List;

View file

@ -29,17 +29,18 @@ import de.vanitasvitae.slam.R;
*/ */
public class ChatMessageEntry extends RecyclerView.ViewHolder { public class ChatMessageEntry extends RecyclerView.ViewHolder {
private View view;
public ChatMessageEntry(View itemView) { public ChatMessageEntry(View itemView) {
super(itemView); super(itemView);
this.view = itemView;
} }
public void bind(String sender, String role, View content, String date) { public void bind(String sender, String role, View content, String date) {
((TextView)view.findViewById(R.id.message_sender)).setText(sender); ((TextView)itemView.findViewById(R.id.message_sender)).setText(sender);
((TextView)view.findViewById(R.id.message_sender_role)).setText(role); ((TextView)itemView.findViewById(R.id.message_sender_role)).setText(role);
((RelativeLayout)view.findViewById(R.id.message_content)).addView(content); ((RelativeLayout)itemView.findViewById(R.id.message_content)).addView(content);
((TextView)view.findViewById(R.id.message_date)).setText(date); ((TextView)itemView.findViewById(R.id.message_date)).setText(date);
}
public void setOnAvatarClickListener(View.OnClickListener listener) {
itemView.findViewById(R.id.contact_image).setOnClickListener(listener);
} }
} }

View file

@ -0,0 +1,48 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.ui;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import de.vanitasvitae.slam.R;
import de.vanitasvitae.slam.xmpp.Contact;
/**
* Created by Paul Schaub on 13.02.18.
*/
public class ContactListEntry extends RecyclerView.ViewHolder {
public ContactListEntry(View itemView) {
super(itemView);
}
public void bind(Contact contact) {
((TextView)itemView.findViewById(R.id.contact_name)).setText(contact.getNickname());
((TextView)itemView.findViewById(R.id.contact_jid)).setText(contact.getJid());
}
public void setOnAvatarClickListener(View.OnClickListener listener) {
itemView.findViewById(R.id.contact_image).setOnClickListener(listener);
}
public void setOnEntryClickListener(View.OnClickListener listener) {
itemView.setOnClickListener(listener);
}
}

View file

@ -28,18 +28,22 @@ import de.vanitasvitae.slam.R;
*/ */
public class ConversationEntry extends RecyclerView.ViewHolder { public class ConversationEntry extends RecyclerView.ViewHolder {
private View view;
public ConversationEntry(View itemView) { public ConversationEntry(View itemView) {
super(itemView); super(itemView);
this.view = itemView;
} }
public void bind(String username, String message, String date, boolean read) { public void bind(String username, String message, String date, boolean read) {
View v = view; ((TextView)itemView.findViewById(R.id.contact_name)).setText(username);
((TextView)v.findViewById(R.id.contact_name)).setText(username); ((TextView)itemView.findViewById(R.id.contact_jid)).setText(message);
((TextView)v.findViewById(R.id.contact_textcontent)).setText(message); ((TextView)itemView.findViewById(R.id.contact_date)).setText(date);
((TextView)v.findViewById(R.id.contact_date)).setText(date); itemView.findViewById(R.id.send_indicator).setVisibility(read ? View.VISIBLE : View.GONE);
v.findViewById(R.id.send_indicator).setVisibility(read ? View.VISIBLE : View.GONE); }
public void setOnAvatarClickListener(View.OnClickListener listener) {
itemView.findViewById(R.id.contact_image).setOnClickListener(listener);
}
public void setOnEntryClickListener(View.OnClickListener listener) {
itemView.setOnClickListener(listener);
} }
} }

View file

@ -1,3 +1,20 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.xmpp; package de.vanitasvitae.slam.xmpp;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;

View file

@ -1,3 +1,20 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.xmpp; package de.vanitasvitae.slam.xmpp;
/** /**

View file

@ -0,0 +1,49 @@
/*
* Copyright 2018 Paul Schaub
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package de.vanitasvitae.slam.xmpp;
/**
* Created by Paul Schaub on 13.02.18.
*/
public class Resource {
private final String resource, status, client, system;
public Resource(String resource, String status, String client, String system) {
this.resource = resource;
this.status = status;
this.client = client;
this.system = system;
}
public String getClient() {
return client;
}
public String getResource() {
return resource;
}
public String getStatus() {
return status;
}
public String getSystem() {
return system;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/contact_detail.appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/contact_detail.collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="200dp"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:layout_scrollFlags="scroll">
<ImageView
android:id="@+id/contact_detail.profile_backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/background_image"
app:layout_collapseMode="parallax"
/>
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/contact_detail.toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
/>
<LinearLayout
android:id="@+id/contact_detail.identity_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="8dp"
android:gravity="center"
app:layout_scrollFlags="scroll|enterAlways|snap"
>
<TextView
android:id="@+id/contact_detail.nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:text="Max Mustermann" />
<TextView
android:id="@+id/contact_detail.jid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle"
android:text="max@mustermann.de"
android:textColor="@android:color/white" />
</LinearLayout>
<android.support.design.widget.TabLayout
android:id="@+id/contact_detail.tab_layout"
android:layout_width="fill_parent"
android:layout_height="?attr/actionBarSize"
app:tabSelectedTextColor="?android:attr/textColorPrimary"
app:tabIndicatorColor="?android:attr/textColorPrimary"
app:tabIndicatorHeight="4dp"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/contact_detail.viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.FloatingActionButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:elevation="8dp"
android:layout_gravity="bottom|right|end"
android:src="@drawable/ic_chat_white_48dp"
android:layout_margin="@dimen/activity_horizontal_margin"
android:clickable="true"
android:focusable="true" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/contact_detail.profile_circle"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/face"
app:layout_anchor="@id/contact_detail.identity_layout"
android:elevation="8dp"
app:layout_scrollFlags="scroll"
app:layout_anchorGravity="top|center_horizontal"
app:civ_border_width="4dp"
app:civ_border_color="@android:color/white"
/>
</android.support.design.widget.CoordinatorLayout>

View file

@ -61,7 +61,7 @@
android:id="@+id/login_username" android:id="@+id/login_username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/hint_jid" android:hint="@string/login__hint_jid"
android:inputType="textEmailAddress" android:inputType="textEmailAddress"
android:nextFocusForward="@+id/login_password" /> android:nextFocusForward="@+id/login_password" />
@ -79,7 +79,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textPassword" android:inputType="textPassword"
android:hint="@string/hint_password" /> android:hint="@string/login__hint_password" />
</android.support.design.widget.TextInputLayout> </android.support.design.widget.TextInputLayout>
@ -94,7 +94,7 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textColor="@color/secondaryColor" android:textColor="@color/secondaryColor"
android:text="@string/button_login_account" /> android:text="@string/login__button_login" />
</RelativeLayout> </RelativeLayout>

View file

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="300dp">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="@color/primaryDarkColor"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="?attr/themedContactDrawable"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="JKANSkjhkjasdhkjashdkslaudhfliuasaulkdhfliusadfhlasdkjfnlaskufskjnlaukldsjfnlkasjfnldsfjadsiljfoadsijflcmladsimnciadslmnfiadslmcvloasimcloasdicm"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
android:src="@drawable/ic_share_white_48dp"
app:layout_anchor="@id/app_bar_layout"
app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginTop="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="20sp"
android:text="Person"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Max Mustermann"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/colorBackground">
<android.support.v7.widget.RecyclerView
android:id="@+id/contact_detail.resources.recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Security Stuff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_chatlist" android:id="@+id/recycler_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="10dp"> android:padding="10dp">

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/chatlist_vert_size">
<ImageView
android:id="@+id/contact_image"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_margin="5dp"
android:layout_alignParentStart="true"
android:src="@drawable/ic_launcher_background"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/contact_image">
<TextView
android:id="@+id/contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:textStyle="bold"
android:text="Max Mustermann"
android:ellipsize="end"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"/>
<TextView
android:id="@+id/contact_jid"
android:layout_below="@id/contact_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="max@mustermann.de"/>
</RelativeLayout>
</RelativeLayout>

View file

@ -64,7 +64,7 @@
android:layout_toStartOf="@+id/lower_meta"> android:layout_toStartOf="@+id/lower_meta">
<TextView <TextView
android:id="@+id/contact_textcontent" android:id="@+id/contact_jid"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="Ich war letzte Woche noch da und habe mir einen eigenen Eindruck von der Lage verschafft."/> android:text="Ich war letzte Woche noch da und habe mir einen eigenen Eindruck von der Lage verschafft."/>

View file

@ -16,6 +16,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<de.hdodenhof.circleimageview.CircleImageView <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/contact_image"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_person_black_48dp" android:src="@drawable/ic_person_black_48dp"

View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="8dp">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/activity_vertical_margin">
<android.support.constraint.ConstraintLayout
android:id="@+id/resource__layout_resource"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/resource__resource_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="XMPP-Resource"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/resource__resource_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:text="phone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__resource_title"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/resource__layout_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__layout_resource">
<TextView
android:id="@+id/resource__status_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/resource__status_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:text="Away"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__status_title"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/resource__layout_client"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__layout_status">
<TextView
android:id="@+id/resource__client_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Client"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/resource__client_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:text="Conversations 1.23.4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__client_title"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/resource__layout_system"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__layout_client">
<TextView
android:id="@+id/resource__system_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="System"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/resource__system_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:text="Android"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/resource__system_title"/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

View file

@ -1,11 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:minHeight="?attr/actionBarSize" android:minHeight="?attr/actionBarSize"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:titleTextColor="@android:color/white"
android:background="?attr/colorPrimary"> android:background="?attr/colorPrimary">
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>

View file

@ -1,15 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/item_navdrawer__contacts" <item android:id="@+id/navdrawer__item_conversations"
android:title="@string/item_navdrawer__contacts" android:title="@string/navdrawer__conversations"
android:icon="?attr/themedConversationsDrawable"/>
<item android:id="@+id/navdrawer__item_contacts"
android:title="@string/navdrawer__contacts"
android:icon="?attr/themedContactDrawable"/> android:icon="?attr/themedContactDrawable"/>
<item android:id="@+id/item_navdrawer__bookmarks" <item android:id="@+id/navdrawer__item_bookmarks"
android:title="@string/item_navdrawer__bookmarks" android:title="@string/navdrawer__groupchats"
android:icon="?attr/themedBookmarkDrawable"/> android:icon="?attr/themedBookmarkDrawable"/>
<item android:id="@+id/item_navdrawer__settings" <item android:id="@+id/navdrawer__item_blogging"
android:title="@string/item_navdrawer__settings" android:title="@string/navdrawer__blogging"
android:icon="?attr/themedBloggingDrawable"/>
<item android:id="@+id/navdrawer__item_settings"
android:title="@string/navdrawer__settings"
android:icon="?attr/themedSettingsDrawable"/> android:icon="?attr/themedSettingsDrawable"/>
</menu> </menu>

View file

@ -3,6 +3,6 @@
<item <item
android:id="@+id/item_menu_login__advanced_options" android:id="@+id/item_menu_login__advanced_options"
android:title="@string/item_menu_login__advanced_options" /> android:title="@string/login__advanced_options" />
</menu> </menu>

View file

@ -5,8 +5,8 @@
<item <item
android:id="@+id/app_bar_search" android:id="@+id/app_bar_search"
android:icon="?attr/themedSearchDrawable" android:icon="?attr/themedSearchDrawable"
android:title="@string/search" android:title="@string/toolbar__search"
app:actionViewClass="android.widget.SearchView" app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/> app:showAsAction="always"/>
<item <item

View file

@ -22,4 +22,6 @@
<attr name="themedAddBookmarkDrawable" format="reference" /> <attr name="themedAddBookmarkDrawable" format="reference" />
<attr name="themedSettingsDrawable" format="reference" /> <attr name="themedSettingsDrawable" format="reference" />
<attr name="themedSearchDrawable" format="reference" /> <attr name="themedSearchDrawable" format="reference" />
<attr name="themedConversationsDrawable" format="reference" />
<attr name="themedBloggingDrawable" format="reference" />
</resources> </resources>

View file

@ -8,25 +8,40 @@
<string name="app_name" translatable="false">&appname;</string> <string name="app_name" translatable="false">&appname;</string>
<!-- Strings related to login --> <!-- Strings related to login -->
<string name="item_menu_login__advanced_options">Advanced Options</string> <string name="login__advanced_options">Advanced Options</string>
<string name="hint_jid">Jabber ID</string> <string name="login__hint_jid">Jabber ID</string>
<string name="hint_password">Password</string> <string name="login__hint_password">Password</string>
<string name="button_login_account">Login</string> <string name="login__button_login">Login</string>
<string name="button_register_account">Register</string> <string name="login__button_register">Register</string>
<string name="error_invalid_jid">Invalid Jabber ID</string> <string name="login__error_invalid_jid">Invalid Jabber ID</string>
<string name="error_invalid_password">Password is too short</string> <string name="login__error_invalid_password">Password is too short</string>
<string name="error_incorrect_password">Password is incorrect</string> <string name="login__error_incorrect_password">Password is incorrect</string>
<string name="error_field_required">This field is required</string> <string name="login__error_field_required">This field is required</string>
<string name="login__error_server_not_found">Server not found</string>
<!-- Toolbar --> <!-- Main Toolbar -->
<string name="search">Search</string> <string name="toolbar__search">Search</string>
<string name="settings">Settings</string> <string name="toolbar__settings">Settings</string>
<!-- Navigation Drawer --> <!-- Navigation Drawer -->
<string name="item_navdrawer__contacts">Contacts</string> <string name="navdrawer__conversations">Conversations</string>
<string name="item_navdrawer__bookmarks">Bookmarks</string> <string name="navdrawer__contacts">Contacts</string>
<string name="item_navdrawer__settings">Settings</string> <string name="navdrawer__groupchats">Groups</string>
<string name="error_server_not_found">Server not found</string> <string name="navdrawer__blogging">Blogging</string>
<string name="navdrawer__settings">Settings</string>
<!-- Contact Detail -->
<string name="contact_detail__tab_info">Info</string>
<string name="contact_detail__tab_security">Security</string>
<string name="contact_detail__devices">Devices</string>
<string name="contact_detail__fingerprints_omemo">OMEMO Fingerprints</string>
<!-- Encryption Modes -->
<string name="desc_omemo">Confabulate</string>
<string name="desc_ox">Keep a History</string>
<string name="feature_not_implemented">Feature not implemented.</string>
</resources> </resources>

View file

@ -15,11 +15,13 @@
<item name="themedAddBookmarkDrawable">@drawable/ic_group_add_black_48dp</item> <item name="themedAddBookmarkDrawable">@drawable/ic_group_add_black_48dp</item>
<item name="themedSettingsDrawable">@drawable/ic_settings_black_48dp</item> <item name="themedSettingsDrawable">@drawable/ic_settings_black_48dp</item>
<item name="themedSearchDrawable">@drawable/ic_search_black_48dp</item> <item name="themedSearchDrawable">@drawable/ic_search_black_48dp</item>
<item name="themedConversationsDrawable">@drawable/ic_chat_black_48dp</item>
<item name="themedBloggingDrawable">@drawable/ic_public_black_48dp</item>
</style> </style>
<style name="Slam.Dark" parent="Theme.AppCompat.NoActionBar"> <style name="Slam.Dark" parent="Theme.AppCompat.NoActionBar">
<item name="colorBackground">@color/backgroundDark</item> <item name="colorBackground">@color/backgroundDark</item>
<item name="android:titleTextStyle">@style/Theme.AppCompat</item>
<item name="textSizeMessageMetadata">10sp</item> <item name="textSizeMessageMetadata">10sp</item>
<item name="textSizeMessageContent">18sp</item> <item name="textSizeMessageContent">18sp</item>
@ -29,6 +31,8 @@
<item name="themedAddBookmarkDrawable">@drawable/ic_group_add_white_48dp</item> <item name="themedAddBookmarkDrawable">@drawable/ic_group_add_white_48dp</item>
<item name="themedSettingsDrawable">@drawable/ic_settings_white_48dp</item> <item name="themedSettingsDrawable">@drawable/ic_settings_white_48dp</item>
<item name="themedSearchDrawable">@drawable/ic_search_white_48dp</item> <item name="themedSearchDrawable">@drawable/ic_search_white_48dp</item>
<item name="themedConversationsDrawable">@drawable/ic_chat_white_48dp</item>
<item name="themedBloggingDrawable">@drawable/ic_public_white_48dp</item>
</style> </style>
<style name="Divider"> <style name="Divider">