Some Material Design changes

This commit is contained in:
Paul Schaub 2019-09-23 23:20:43 +02:00
parent eb6a406f03
commit 75483efd70
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
38 changed files with 397 additions and 337 deletions

View File

@ -35,8 +35,6 @@
<activity
android:name=".ui.login.LoginActivity"
android:label="@string/title_activity_login" />
<activity android:name=".ui.login.AccountsActivity" />
<service android:name=".service.XmppConnectionService" />
</application>

View File

@ -1,7 +1,5 @@
package org.mercury_im.messenger.di.component;
import dagger.Component;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.core.stores.PlainMessageStore;
import org.mercury_im.messenger.di.module.AppModule;
@ -13,14 +11,15 @@ import org.mercury_im.messenger.ui.chat.ChatInputFragment;
import org.mercury_im.messenger.ui.chat.ChatInputViewModel;
import org.mercury_im.messenger.ui.chat.ChatViewModel;
import org.mercury_im.messenger.ui.chatlist.ChatListViewModel;
import org.mercury_im.messenger.ui.login.AccountsActivity;
import org.mercury_im.messenger.ui.login.AccountsViewModel;
import org.mercury_im.messenger.ui.login.LoginActivity;
import org.mercury_im.messenger.ui.login.LoginViewModel;
import org.mercury_im.messenger.ui.roster.RosterViewModel;
import org.mercury_im.messenger.ui.roster.contacts.ContactListViewModel;
import javax.inject.Singleton;
import dagger.Component;
/**
* Main Application Component that binds together all the modules needed for the Android
* application.
@ -45,8 +44,6 @@ public interface AppComponent {
void inject(ChatActivity chatActivity);
void inject(AccountsActivity accountsActivity);
void inject(ChatInputFragment chatInputFragment);
void inject(ChatListViewModel chatListViewModel);
@ -54,7 +51,7 @@ public interface AppComponent {
// ViewModels
void inject(RosterViewModel rosterViewModel);
void inject(ContactListViewModel contactListViewModel);
void inject(ChatViewModel chatViewModel);

View File

@ -10,10 +10,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentTransaction;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.navigation.NavigationView;
@ -22,15 +18,16 @@ import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.repository.ChatRepository;
import org.mercury_im.messenger.ui.chatlist.ChatListFragment;
import org.mercury_im.messenger.ui.login.AccountsActivity;
import org.mercury_im.messenger.ui.login.AccountsFragment;
import org.mercury_im.messenger.ui.login.LoginActivity;
import org.mercury_im.messenger.ui.roster.RosterFragment;
import org.mercury_im.messenger.ui.settings.SettingsActivity;
import org.mercury_im.messenger.util.ColorUtil;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener,
AccountsFragment.OnAccountListItemClickListener {
@ -56,6 +53,7 @@ public class MainActivity extends AppCompatActivity
setSupportActionBar(toolbar);
bottomNavigationView.setOnNavigationItemSelectedListener(this::onNavigationItemSelected);
bottomNavigationView.setSelectedItemId(R.id.entry_chats);
MercuryImApplication.getApplication().getAppComponent().inject(this);
}
@ -78,10 +76,6 @@ public class MainActivity extends AppCompatActivity
case R.id.action_login:
startActivity(new Intent(getApplicationContext(), LoginActivity.class));
return true;
case R.id.action_accounts:
startActivity(new Intent(getApplicationContext(), AccountsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);

View File

@ -53,7 +53,7 @@ public class ChatActivity extends AppCompatActivity
@Inject
ChatRepository chatRepository;
private final ChatRecyclerViewAdapter recyclerViewAdapter = new ChatRecyclerViewAdapter();
private final MessagesRecyclerViewAdapter recyclerViewAdapter = new MessagesRecyclerViewAdapter();
private ChatViewModel chatViewModel;

View File

@ -11,16 +11,17 @@ import androidx.recyclerview.widget.RecyclerView;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.MessageModel;
import org.mercury_im.messenger.ui.util.MessageBackgroundDrawable;
import java.util.ArrayList;
import java.util.List;
public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerViewAdapter.ChatItemViewHolder> {
public class MessagesRecyclerViewAdapter extends RecyclerView.Adapter<MessagesRecyclerViewAdapter.MessageViewHolder> {
private List<MessageModel> messages = new ArrayList<>();
private SparseArray<Boolean> checkedItems = new SparseArray<>();
public ChatRecyclerViewAdapter() {
public MessagesRecyclerViewAdapter() {
}
@ -40,18 +41,18 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerVi
@NonNull
@Override
public ChatItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ChatItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false));
public MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MessageViewHolder(LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ChatItemViewHolder holder, int position) {
public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) {
MessageModel message = messages.get(position);
MessageBackgroundDrawable background = getBackgroundDrawable(position);
if (message.isIncoming()) {
holder.body.setBackgroundResource(background.getIn());
holder.body.setBackgroundResource(background.getIncomingDrawable());
} else {
holder.body.setBackgroundResource(background.getOut());
holder.body.setBackgroundResource(background.getOutgoingDrawable());
}
holder.body.setText(message.getBody());
holder.body.requestLayout();
@ -71,55 +72,36 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerVi
return messages.size();
}
public class ChatItemViewHolder extends RecyclerView.ViewHolder {
public class MessageViewHolder extends RecyclerView.ViewHolder {
private TextView body;
public ChatItemViewHolder(@NonNull View itemView) {
public MessageViewHolder(@NonNull View itemView) {
super(itemView);
body = itemView.findViewById(R.id.msg_body);
}
}
enum MessageBackgroundDrawable {
FIRST(R.drawable.bg_msg_in_first, R.drawable.bg_msg_out_first),
MID(R.drawable.bg_msg_in_mid, R.drawable.bg_msg_out_mid),
LAST(R.drawable.bg_msg_in_last, R.drawable.bg_msg_out_last),
SINGLE(R.drawable.bg_msg_single, R.drawable.bg_msg_single),
;
private final int in, out;
MessageBackgroundDrawable(int in, int out) {
this.in = in;
this.out = out;
}
public int getIn() {
return in;
}
public int getOut() {
return out;
}
}
public MessageBackgroundDrawable getBackgroundDrawable(int pos) {
MessageModel m = messages.get(pos);
MessageModel b = pos != 0 ? messages.get(pos - 1) : null;
MessageModel a = pos != messages.size() - 1 ? messages.get(pos + 1) : null;
MessageModel subject = messages.get(pos);
MessageModel predecessor = pos != 0 ? messages.get(pos - 1) : null;
MessageModel successor = pos != messages.size() - 1 ? messages.get(pos + 1) : null;
if (b == null || b.isIncoming() != m.isIncoming()) {
if (a == null || a.isIncoming() != m.isIncoming()) {
if (predecessor == null || predecessor.isIncoming() != subject.isIncoming()) {
if (successor == null || successor.isIncoming() != subject.isIncoming()) {
// This is a single message
return MessageBackgroundDrawable.SINGLE;
} else {
// This is the first message of a group
return MessageBackgroundDrawable.FIRST;
}
} else {
if (a == null || a.isIncoming() != m.isIncoming()) {
if (successor == null || successor.isIncoming() != subject.isIncoming()) {
// This is the last message of a group
return MessageBackgroundDrawable.LAST;
} else {
// This message is in the middle of a group
return MessageBackgroundDrawable.MID;
}
}

View File

@ -1,5 +1,7 @@
package org.mercury_im.messenger.ui.chatlist;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -13,7 +15,9 @@ import butterknife.ButterKnife;
import de.hdodenhof.circleimageview.CircleImageView;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.core.util.ContactNameUtil;
import org.mercury_im.messenger.persistence.pojo.Chat;
import org.mercury_im.messenger.ui.chat.ChatActivity;
import org.mercury_im.messenger.ui.util.AbstractRecyclerViewAdapter;
public class ChatListRecyclerViewAdapter
@ -27,14 +31,24 @@ public class ChatListRecyclerViewAdapter
@Override
public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item_chat_list, parent, false);
return new ChatHolder(view);
.inflate(R.layout.list_item_chat, parent, false);
return new ChatHolder(parent.getContext(), view);
}
@Override
public void onBindViewHolder(@NonNull ChatHolder holder, int position) {
Chat model = getModelAt(position);
holder.nameView.setText(model.jid.toString());
holder.nameView.setText(model.peerName != null ?
model.peerName : model.jid.toString());
holder.itemView.setOnClickListener(view -> {
Intent intent = new Intent(holder.context, ChatActivity.class);
intent.putExtra("JID", model.jid.toString());
intent.putExtra("ACCOUNT", model.accountId);
holder.context.startActivity(intent);
});
// TODO: Better bindable model pls
}
@ -52,8 +66,11 @@ public class ChatListRecyclerViewAdapter
@BindView(R.id.chat_avatar)
CircleImageView avatarView;
public ChatHolder(@NonNull View itemView) {
Context context;
public ChatHolder(Context context, @NonNull View itemView) {
super(itemView);
this.context = context;
ButterKnife.bind(this, itemView);
}
}

View File

@ -1,58 +0,0 @@
package org.mercury_im.messenger.ui.login;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import io.reactivex.schedulers.Schedulers;
import org.mercury_im.messenger.MercuryImApplication;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.persistence.model.AccountModel;
import org.mercury_im.messenger.persistence.repository.AccountRepository;
import javax.inject.Inject;
public class AccountsActivity extends AppCompatActivity
implements AccountsFragment.OnAccountListItemClickListener {
@Inject
AccountRepository accountRepository;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_accounts);
MercuryImApplication.getApplication().getAppComponent().inject(this);
AccountsFragment accountsFragment = (AccountsFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_accounts);
}
@Override
public void onAccountListItemClick(AccountModel item) {
View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_account_details, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setView(dialogView)
.setTitle(item.getJid())
.setPositiveButton("Save", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
builder.create().show();
}
@Override
public void onAccountListItemLongClick(AccountModel item) {
accountRepository.deleteAccount(item)
.subscribeOn(Schedulers.io())
.subscribe();
}
}

View File

@ -34,7 +34,7 @@ public class AccountsRecyclerViewAdapter extends RecyclerView.Adapter<AccountsRe
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item_account, parent, false);
.inflate(R.layout.list_item_account, parent, false);
return new ViewHolder(view);
}

View File

@ -1,64 +1,80 @@
package org.mercury_im.messenger.ui.roster;
import static androidx.constraintlayout.widget.Constraints.TAG;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import org.mercury_im.messenger.R;
import org.mercury_im.messenger.ui.roster.bookmarks.BookmarkListFragment;
import org.mercury_im.messenger.ui.roster.contacts.ContactListFragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import org.mercury_im.messenger.R;
/**
* A placeholder fragment containing a simple view.
*/
public class RosterFragment extends Fragment {
private RosterViewModel rosterViewModel;
@BindView(R.id.tab_layout)
TabLayout tabLayout;
@BindView(R.id.roster_entry_list__recycler_view)
RecyclerView recyclerView;
private final RosterRecyclerViewAdapter recyclerViewAdapter = new RosterRecyclerViewAdapter();
public RosterFragment() {
}
@BindView(R.id.viewpager)
ViewPager viewPager;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_roster_entry_list, container, false);
View view = inflater.inflate(R.layout.fragment_roster_tabswitcher, container, false);
ButterKnife.bind(this, view);
recyclerView.setAdapter(recyclerViewAdapter);
viewPager.setAdapter(new RosterFragmentPagerAdapter(
getChildFragmentManager(),
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT));
tabLayout.setupWithViewPager(viewPager);
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
rosterViewModel = ViewModelProviders.of(getActivity()).get(RosterViewModel.class);
observeViewModel(rosterViewModel);
}
private class RosterFragmentPagerAdapter extends FragmentPagerAdapter {
private void observeViewModel(RosterViewModel viewModel) {
viewModel.getRosterEntryList().observe(this, rosterEntries -> {
if (rosterEntries == null) {
Log.d(TAG, "Displaying null roster entries");
return;
}
recyclerViewAdapter.setModels(rosterEntries);
Log.d(TAG, "Displaying " + rosterEntries.size() + " roster entries");
});
}
final int PAGE_COUNT = 2;
final String[] PAGE_TITLES = new String[] {
getString(R.string.tab_contacts),
getString(R.string.tab_bookmarks)
};
final Fragment[] PAGES = new Fragment[] {
new ContactListFragment(),
new BookmarkListFragment()
};
public RosterFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
@NonNull
@Override
public Fragment getItem(int position) {
return PAGES[position];
}
@Override
public int getCount() {
return PAGE_COUNT;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return PAGE_TITLES[position];
}
};
}

View File

@ -0,0 +1,26 @@
package org.mercury_im.messenger.ui.roster.bookmarks;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.mercury_im.messenger.R;
import butterknife.ButterKnife;
public class BookmarkListFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bookmarks_list, container, false);
ButterKnife.bind(this, view);
return view;
}
}

View File

@ -0,0 +1,65 @@
package org.mercury_im.messenger.ui.roster.contacts;
import static androidx.constraintlayout.widget.Constraints.TAG;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import org.mercury_im.messenger.R;
/**
* A placeholder fragment containing a simple view.
*/
public class ContactListFragment extends Fragment {
private ContactListViewModel contactListViewModel;
@BindView(R.id.roster_entry_list__recycler_view)
RecyclerView recyclerView;
private final ContactListRecyclerViewAdapter recyclerViewAdapter = new ContactListRecyclerViewAdapter();
public ContactListFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contact_list, container, false);
ButterKnife.bind(this, view);
recyclerView.setAdapter(recyclerViewAdapter);
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
contactListViewModel = new ViewModelProvider(this).get(ContactListViewModel.class);
observeViewModel(contactListViewModel);
}
private void observeViewModel(ContactListViewModel viewModel) {
viewModel.getRosterEntryList().observe(this, rosterEntries -> {
if (rosterEntries == null) {
Log.d(TAG, "Displaying null roster entries");
return;
}
recyclerViewAdapter.setModels(rosterEntries);
Log.d(TAG, "Displaying " + rosterEntries.size() + " roster entries");
});
}
}

View File

@ -1,4 +1,4 @@
package org.mercury_im.messenger.ui.roster;
package org.mercury_im.messenger.ui.roster.contacts;
import android.app.Application;
@ -11,7 +11,7 @@ import org.mercury_im.messenger.persistence.room.repository.IRosterRepository;
import javax.inject.Inject;
public class RosterItemViewModel extends AndroidViewModel {
public class ContactListItemViewModel extends AndroidViewModel {
@Inject
IRosterRepository contactRepository;
@ -19,7 +19,7 @@ public class RosterItemViewModel extends AndroidViewModel {
private LiveData<RoomContactModel> contact;
@Inject
public RosterItemViewModel(@NonNull Application application) {
public ContactListItemViewModel(@NonNull Application application) {
super(application);
}

View File

@ -1,4 +1,4 @@
package org.mercury_im.messenger.ui.roster;
package org.mercury_im.messenger.ui.roster.contacts;
import android.content.Context;
import android.content.Intent;
@ -24,10 +24,10 @@ import org.mercury_im.messenger.util.ColorUtil;
import java.util.Objects;
public class RosterRecyclerViewAdapter
extends AbstractRecyclerViewAdapter<RoomContactModel, RosterRecyclerViewAdapter.RosterItemViewHolder> {
public class ContactListRecyclerViewAdapter
extends AbstractRecyclerViewAdapter<RoomContactModel, ContactListRecyclerViewAdapter.RosterItemViewHolder> {
public RosterRecyclerViewAdapter() {
public ContactListRecyclerViewAdapter() {
super(new ContactDiffCallback());
}
@ -35,7 +35,7 @@ public class RosterRecyclerViewAdapter
@Override
public RosterItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new RosterItemViewHolder(parent.getContext(), LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view_item_roster_entry, parent, false));
.inflate(R.layout.list_item_contact, parent, false));
}
@Override

View File

@ -1,4 +1,4 @@
package org.mercury_im.messenger.ui.roster;
package org.mercury_im.messenger.ui.roster.contacts;
import android.util.Log;
@ -21,7 +21,7 @@ import java.util.List;
import javax.inject.Inject;
public class RosterViewModel extends ViewModel {
public class ContactListViewModel extends ViewModel {
@Inject
RosterRepository rosterRepository;
@ -29,16 +29,16 @@ public class RosterViewModel extends ViewModel {
private final MutableLiveData<List<RoomContactModel>> rosterEntryList = new MutableLiveData<>();
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
public RosterViewModel() {
public ContactListViewModel() {
super();
MercuryImApplication.getApplication().getAppComponent().inject(this);
Log.d("RosterViewModel", "Start observing database");
Log.d("ContactListViewModel", "Start observing database");
// Subscribe to changes to the contacts table and update the LiveData object for the UI.
compositeDisposable.add(rosterRepository.getAllContacts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Consumer<List<? extends ContactModel>>) o -> {
Log.d("RosterViewModel", "Room changed contacts: " + o.size());
Log.d("ContactListViewModel", "Room changed contacts: " + o.size());
rosterEntryList.setValue((List<RoomContactModel>) o);
}));
}

View File

@ -0,0 +1,39 @@
package org.mercury_im.messenger.ui.util;
import org.mercury_im.messenger.R;
/**
* Enum that describes which position in a message grouping a message has.
*/
public enum MessageBackgroundDrawable {
FIRST(R.drawable.bg_msg_in_first, R.drawable.bg_msg_out_first),
MID(R.drawable.bg_msg_in_mid, R.drawable.bg_msg_out_mid),
LAST(R.drawable.bg_msg_in_last, R.drawable.bg_msg_out_last),
SINGLE(R.drawable.bg_msg_single, R.drawable.bg_msg_single),
;
private final int in, out;
MessageBackgroundDrawable(int in, int out) {
this.in = in;
this.out = out;
}
/**
* Return the resource id for the drawable that is used to render the messages background
* in case of an incoming message.
* @return res id of incoming message background
*/
public int getIncomingDrawable() {
return in;
}
/**
* Return the resource id for the drawable that is used to render the messages background
* in case of an outgoing message.
* @return res id of outgoing message background
*/
public int getOutgoingDrawable() {
return out;
}
}

View File

@ -22,15 +22,18 @@
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar_layout">
<include layout="@layout/content_chat" />
</FrameLayout>
app:layout_constraintTop_toBottomOf="@id/appbar_layout"
android:paddingBottom="56dp"
android:clipToPadding="false"
android:clipChildren="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="@layout/view_message_text_in" />
<fragment
android:id="@+id/fragment_compose"

View File

@ -1,73 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".ui.login.LoginActivity">
<ScrollView
android:id="@+id/login_form"
<LinearLayout
android:id="@+id/jid_login_form"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/jid_login_form"
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/jid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_jid"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/jid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
app:passwordToggleContentDescription="@string/password_show_password">
android:hint="@string/prompt_jid"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_password"
android:imeActionId="6"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionUnspecified"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
app:passwordToggleContentDescription="@string/password_show_password"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<Button
android:id="@+id/sign_in_button"
style="?android:textAppearanceSmall"
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/action_sign_in"
android:textStyle="bold" />
android:hint="@string/prompt_password"
android:imeActionId="6"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionUnspecified"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/sign_in_button"
style="@style/Widget.MaterialComponents.Button"
android:backgroundTint="@color/mercuryGreenLight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/action_sign_in"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.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"
@ -7,27 +7,41 @@
tools:context=".ui.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
android:background="?attr/colorPrimary" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
<FrameLayout
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar_layout"
app:layout_constraintBottom_toTopOf="@id/bottom_navigation">
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:itemIconTint="?attr/tabsColorSelector"
app:itemTextColor="?attr/tabsColorSelector"
app:itemBackground="?attr/colorPrimary"
app:menu="@menu/bottom_menu_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,9 +0,0 @@
<fragment 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:id="@+id/fragment_accounts"
android:name="org.mercury_im.messenger.ui.login.AccountsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_account_list" />

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/chat"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.chat.ChatInputFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="56dp"
android:clipToPadding="false"
android:clipChildren="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/view_message_text_in">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,9 +0,0 @@
<?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"
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</FrameLayout>

View File

@ -1,9 +0,0 @@
<fragment 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:id="@+id/fragment"
android:name="org.mercury_im.messenger.ui.roster.RosterFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_roster_entry_list" />

View File

@ -10,4 +10,4 @@
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context="org.mercury_im.messenger.ui.login.AccountsFragment"
tools:listitem="@layout/recycler_view_item_account" />
tools:listitem="@layout/list_item_account" />

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
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:id="@+id/roster_entry_list__recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context=".ui.roster.contacts.ContactListFragment"
tools:listitem="@layout/list_item_chat" />

View File

@ -1,18 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.recyclerview.widget.RecyclerView
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:id="@+id/chat_list__recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.chatlist.ChatListFragment"
tools:showIn="@layout/activity_main">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/chat_list__recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/recycler_view_item_chat_list"/>
</androidx.constraintlayout.widget.ConstraintLayout>
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/list_item_chat"
tools:context=".ui.chatlist.ChatListFragment"/>

View File

@ -3,6 +3,4 @@
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
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:id="@+id/roster_entry_list__recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/list_item_contact"
tools:context=".ui.roster.contacts.ContactListFragment"/>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
tools:context=".ui.roster.RosterFragment"
tools:showIn="@layout/content_roster">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/roster_entry_list__recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/recycler_view_item_chat_list" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIconTint="?attr/tabsColorSelector"
app:tabTextColor="?attr/tabsColorSelector"
app:tabIndicatorColor="@color/mercuryGreen">
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.viewpager.widget.ViewPager>
</LinearLayout>

View File

@ -16,7 +16,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/aldrin"
tools:src="@drawable/aldrin"
tools:srcCompat="@drawable/aldrin" />
@ -35,22 +35,13 @@
<TextView
android:id="@+id/roster_entry__jid"
android:layout_width="285dp"
android:layout_height="16dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="@+id/roster_entry__avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.050"
app:layout_constraintStart_toEndOf="@+id/roster_entry__avatar"
tools:text="@tools:sample/lorem[4:10]" />
<TextView
android:id="@+id/roster_entry__date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/roster_entry__avatar"
tools:text="@tools:sample/date/hhmm" />
tools:text="romeo@montague.lit" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -3,7 +3,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
tools:showIn="@layout/activity_chat">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"

View File

@ -22,10 +22,4 @@
android:orderInCategory="110"
android:title="@string/title_activity_login"
app:showAsAction="never" />
<item
android:id="@+id/action_accounts"
android:orderInCategory="130"
android:title="Accounts"
app:showAsAction="never" />
</menu>

View File

@ -122,5 +122,7 @@
<string name="entry_chats">Chats</string>
<string name="entry_contacts">Contacts</string>
<string name="entry_accounts">Accounts</string>
<string name="tab_contacts">Contacts</string>
<string name="tab_bookmarks">Bookmarks</string>
</resources>

View File

@ -1,5 +1,10 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="true"
android:key="dark_theme"
android:summary="Come to the dark side! We have cookies!"
android:title="Dark Theme" />
<SwitchPreference
android:defaultValue="true"

View File

@ -44,7 +44,13 @@ public interface ChatDao extends BaseDao<RoomChatModel> {
@Query("SELECT chats.pk_chat_id as chatId, " +
"chats.fk_entity_id as entityId, " +
"contacts.rostername as peerName, " +
"entities.fk_account_id as accountId, " +
"jid, active " +
"from chats LEFT JOIN entities ON chats.fk_entity_id = entities.pk_entity_id")
"from chats " +
"LEFT JOIN entities " +
"ON chats.fk_entity_id = entities.pk_entity_id " +
"LEFT JOIN contacts " +
"ON entities.pk_entity_id = contacts.fk_entity_id")
Observable<List<Chat>> getChatPojos();
}

View File

@ -5,6 +5,8 @@ import org.jxmpp.jid.EntityBareJid;
public class Chat {
public long chatId;
public long entityId;
public String peerName;
public long accountId;
public EntityBareJid jid;
public boolean active;
}