Added the sharing option
This commit is contained in:
@@ -113,6 +113,7 @@ dependencies {
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
|
||||
provided 'javax.annotation:jsr250-api:1.0'
|
||||
implementation files('libs/sanselan-0_97-android-1.0.0.jar')
|
||||
}
|
||||
|
||||
task adp2upload(type: Exec, dependsOn: "build") {
|
||||
|
||||
Binary file not shown.
@@ -122,6 +122,17 @@
|
||||
android:theme="@style/ChatStyle"
|
||||
/>
|
||||
|
||||
<activity android:name=".page.sharing.SharingActivity"
|
||||
android:label="ownChat"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/ChatStyle">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -15,6 +15,8 @@ import de.beyondsoft.ownchat.page.main.DaggerMainComponent;
|
||||
import de.beyondsoft.ownchat.page.main.MainComponent;
|
||||
import de.beyondsoft.ownchat.page.profile.DaggerProfileComponent;
|
||||
import de.beyondsoft.ownchat.page.profile.ProfileComponent;
|
||||
import de.beyondsoft.ownchat.page.sharing.DaggerSharingComponent;
|
||||
import de.beyondsoft.ownchat.page.sharing.SharingComponent;
|
||||
import de.beyondsoft.ownchat.page.splash.DaggerSplashComponent;
|
||||
import de.beyondsoft.ownchat.page.splash.SplashComponent;
|
||||
|
||||
@@ -33,11 +35,22 @@ public class InjectionHelper {
|
||||
private static ChatComponent sChatComponent;
|
||||
private static ProfileComponent sProfileComponent;
|
||||
private static FileConfirmationComponent sFileConfirmationComponent;
|
||||
private static SharingComponent sSharingComponent;
|
||||
|
||||
public static ApplicationComponent getApplicationComponent(Context context) {
|
||||
return ((BewoplanerApplication) context.getApplicationContext()).getApplicationComponent();
|
||||
}
|
||||
|
||||
public static SharingComponent getSharingComponent(Context context) {
|
||||
if(sSharingComponent == null) {
|
||||
sSharingComponent = DaggerSharingComponent.builder()
|
||||
.applicationComponent(getApplicationComponent(context))
|
||||
.build();
|
||||
}
|
||||
|
||||
return sSharingComponent;
|
||||
}
|
||||
|
||||
public static SplashComponent getSplashComponent(Context context) {
|
||||
if (sSplashComponent == null) {
|
||||
sSplashComponent = DaggerSplashComponent.builder()
|
||||
|
||||
@@ -280,7 +280,6 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha
|
||||
}
|
||||
|
||||
private void selectFile(int requestCode) {
|
||||
// TODO: show attachment type dialog first
|
||||
final String[] zipFileTypes = {".zip", ".rar", ".7z"};
|
||||
final String[] pdfFileTypes = {".pdf"};
|
||||
final String[] docFileTypes = {".txt", ".doc", ".xls", ".docx", ".xlsx", ".odt"};
|
||||
@@ -506,6 +505,7 @@ abstract class BaseChatActivity extends RuntimePermissionActivity implements Cha
|
||||
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(mKeyboardListener);
|
||||
EventBus.getDefault().unregister(this);
|
||||
InjectionHelper.destroyChatComponent();
|
||||
|
||||
mRecyclerView.setAdapter(null);
|
||||
mEmptyLayout.setOnTryAgainClickListener(null);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package de.beyondsoft.ownchat.page.contacts;
|
||||
import de.beyondsoft.ownchat.R;
|
||||
import de.beyondsoft.ownchat.data.AppPrefsConstants;
|
||||
import de.beyondsoft.ownchat.data.api.common.ErrorHandlingResponseConverter;
|
||||
import de.beyondsoft.ownchat.data.api.responses.GroupResponse;
|
||||
import de.beyondsoft.ownchat.data.api.services.ChatService;
|
||||
import de.beyondsoft.ownchat.data.prefs.LongPreference;
|
||||
import de.beyondsoft.ownchat.data.repository.GroupRealmRepository;
|
||||
@@ -30,14 +29,12 @@ import rx.Observable;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.functions.Func1;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by imarneanu on 2/20/17.
|
||||
*/
|
||||
class ContactsPresenter extends MVPAbstractPresenter<ContactsView>
|
||||
implements ContactsView.Callback {
|
||||
class ContactsPresenter extends MVPAbstractPresenter<ContactsView> implements ContactsView.Callback {
|
||||
|
||||
@Inject
|
||||
ChatService mChatService;
|
||||
@@ -80,21 +77,21 @@ class ContactsPresenter extends MVPAbstractPresenter<ContactsView>
|
||||
public void loadGroups() {
|
||||
Subscription subscription = mChatService.getGroups()
|
||||
.flatMap(new ErrorHandlingResponseConverter<>())
|
||||
.flatMap(new Func1<GroupResponse, Observable<ArrayList<Group>>>() {
|
||||
@Override
|
||||
public Observable<ArrayList<Group>> call(GroupResponse groupResponse) {
|
||||
if (groupResponse.response.groups != null) {
|
||||
for (Group group : groupResponse.response.groups) {
|
||||
group.userId = mUserIdReference.get();
|
||||
if (group.lastMessage != null) {
|
||||
group.lastMessage.status = Message.Status.DELIVERED;
|
||||
group.lastMessage.userId = mUserIdReference.get();
|
||||
}
|
||||
mUserRealmRepository.update(group.users, new UserByIdSpecification()).subscribe();
|
||||
.flatMap(groupResponse -> {
|
||||
if (groupResponse.response.groups != null) {
|
||||
for (Group group : groupResponse.response.groups) {
|
||||
group.userId = mUserIdReference.get();
|
||||
|
||||
if (group.lastMessage != null) {
|
||||
group.lastMessage.status = Message.Status.DELIVERED;
|
||||
group.lastMessage.userId = mUserIdReference.get();
|
||||
}
|
||||
|
||||
mUserRealmRepository.update(group.users, new UserByIdSpecification()).subscribe();
|
||||
}
|
||||
return Observable.just(groupResponse.response.groups);
|
||||
}
|
||||
|
||||
return Observable.just(groupResponse.response.groups);
|
||||
})
|
||||
.compose(RxUtils.provideDefaultTransformer())
|
||||
.subscribe(this::groupsSuccess, this::groupsError);
|
||||
@@ -109,6 +106,7 @@ class ContactsPresenter extends MVPAbstractPresenter<ContactsView>
|
||||
getView().showError(R.string.error_no_internet_connection);
|
||||
return;
|
||||
}
|
||||
|
||||
getView().showError(R.string.error_something_went_wrong);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.CoordinatorLayout;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import de.beyondsoft.ownchat.R;
|
||||
import de.beyondsoft.ownchat.data.AppPrefsConstants;
|
||||
import de.beyondsoft.ownchat.data.prefs.LongPreference;
|
||||
import de.beyondsoft.ownchat.di.InjectionHelper;
|
||||
import de.beyondsoft.ownchat.model.Group;
|
||||
import de.beyondsoft.ownchat.page.chat.BaseChatActivity;
|
||||
import de.beyondsoft.ownchat.page.chat.GroupChatActivity;
|
||||
import de.beyondsoft.ownchat.page.chat.SingleChatActivity;
|
||||
import de.beyondsoft.ownchat.page.fileconfirmation.FileConfirmationActivity;
|
||||
import de.beyondsoft.ownchat.utils.Constants;
|
||||
import de.beyondsoft.ownchat.utils.FileUtils;
|
||||
import de.beyondsoft.ownchat.utils.ui.EmptyLayout;
|
||||
import de.beyondsoft.ownchat.utils.ui.HorizontalDividerItemDecoration;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
public class SharingActivity extends AppCompatActivity implements SharingView, SharingContactsAdapter.OnOpenClickListener {
|
||||
private final static int CONFIRM_FILE = 666;
|
||||
|
||||
@Inject
|
||||
SharingPresenter mSharingPresenter;
|
||||
|
||||
@Inject
|
||||
@Named(AppPrefsConstants.USER_ID)
|
||||
LongPreference mUserIdPreference;
|
||||
|
||||
@BindView(R.id.sharing_contacts_recycler_view)
|
||||
RecyclerView mContactsRecyclerView;
|
||||
|
||||
@BindView(R.id.sharing_contacts_coordinator_layout)
|
||||
CoordinatorLayout mCoordinatorLayout;
|
||||
|
||||
@BindView(R.id.sharing_contacts_empty_layout)
|
||||
EmptyLayout mEmptyLayout;
|
||||
|
||||
@BindView(R.id.chat_toolbar_single_title)
|
||||
TextView mSingleChatTitleTextView;
|
||||
|
||||
@BindView(R.id.chat_toolbar_image_view)
|
||||
ImageView mChatImageView;
|
||||
|
||||
@BindView(R.id.chat_toolbar_extra_image)
|
||||
ImageView mExtraImageView;
|
||||
|
||||
private SharingView.Callback mCallback;
|
||||
private SharingContactsAdapter mContactsAdapter;
|
||||
private RecyclerView.ItemDecoration mHorizontalDividerItemDecoration;
|
||||
private Snackbar mSnackbar;
|
||||
|
||||
private Uri selectedFileUri = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_sharing);
|
||||
|
||||
InjectionHelper.getSharingComponent(this).inject(this);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
mSharingPresenter.attachView(this);
|
||||
|
||||
mHorizontalDividerItemDecoration = new HorizontalDividerItemDecoration.Builder(this).colorResId(R.color.colorFill).build();
|
||||
|
||||
mContactsRecyclerView.addItemDecoration(mHorizontalDividerItemDecoration);
|
||||
mContactsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
mContactsAdapter = new SharingContactsAdapter(this, mUserIdPreference.get());
|
||||
mContactsRecyclerView.setAdapter(mContactsAdapter);
|
||||
|
||||
setToolBar();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
|
||||
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
||||
handleSendFile(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void setToolBar() {
|
||||
mSingleChatTitleTextView.setVisibility(View.VISIBLE);
|
||||
mSingleChatTitleTextView.setText(R.string.sharing_title);
|
||||
}
|
||||
|
||||
void handleSendFile(Intent intent) {
|
||||
Uri fileUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
|
||||
if (fileUri != null) {
|
||||
selectedFileUri = fileUri;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mCallback.loadGroups();
|
||||
mCallback.loadGroupsFromDB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(@StringRes int message) {
|
||||
if (mSnackbar == null) {
|
||||
mSnackbar = Snackbar.make(mCoordinatorLayout, message, Snackbar.LENGTH_LONG);
|
||||
mSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.colorDefault));
|
||||
}
|
||||
|
||||
mSnackbar.setText(message);
|
||||
mSnackbar.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfirmSharingFile(Group contact) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.FILENAME, FileUtils.getPath(this, selectedFileUri));
|
||||
|
||||
Intent intent = new Intent(this, FileConfirmationActivity.class);
|
||||
intent.putExtras(bundle);
|
||||
|
||||
intent.putExtra(Constants.EXTRA_GROUP, contact);
|
||||
|
||||
startActivityForResult(intent, 666);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null == data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(requestCode == CONFIRM_FILE) {
|
||||
String filePath = data.getStringExtra(Constants.FILE_PATH);
|
||||
String fileMessageText = data.getStringExtra(Constants.FILE_MESSAGE_TEXT);
|
||||
|
||||
if (!mSharingPresenter.isAttached()) {
|
||||
mSharingPresenter.attachView(this);
|
||||
}
|
||||
|
||||
mCallback.sendFile(Uri.fromFile(new File(filePath)), fileMessageText);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(Callback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showProgress() {
|
||||
mEmptyLayout.showLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideProgress() {
|
||||
mEmptyLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroups(ArrayList<Group> groups) {
|
||||
mContactsAdapter.setGroups(groups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile(Uri fileUri) {
|
||||
return FileUtils.getFile(this, fileUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showToastMessage(@StringRes int message) {
|
||||
Toast.makeText(BaseChatActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import dagger.Component;
|
||||
import de.beyondsoft.ownchat.di.ApplicationComponent;
|
||||
import de.beyondsoft.ownchat.di.scopes.ActivityScope;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
@ActivityScope
|
||||
@Component(dependencies = ApplicationComponent.class, modules = SharingModule.class)
|
||||
public interface SharingComponent {
|
||||
void inject(SharingActivity sharingActivity);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import de.beyondsoft.ownchat.R;
|
||||
import de.beyondsoft.ownchat.model.Group;
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
public class SharingContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
private static final int GROUP_TYPE = 0;
|
||||
private static final int CONTACT_TYPE = 1;
|
||||
|
||||
private ArrayList<Group> mGroups;
|
||||
private SharingContactsAdapter.OnOpenClickListener mItemClickListener;
|
||||
private long mUserId;
|
||||
|
||||
interface OnOpenClickListener {
|
||||
void onConfirmSharingFile(Group contact);
|
||||
}
|
||||
|
||||
SharingContactsAdapter(OnOpenClickListener openClickListener, long userId) {
|
||||
mItemClickListener = openClickListener;
|
||||
mGroups = new ArrayList<>();
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
void setGroups(ArrayList<Group> groups) {
|
||||
mGroups.clear();
|
||||
|
||||
if (groups != null) {
|
||||
mGroups.addAll(groups);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getItemViewType(int position) {
|
||||
if (mGroups.get(position).users.size() > 2) {
|
||||
return GROUP_TYPE;
|
||||
}
|
||||
|
||||
return CONTACT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sharing_contact, parent, false);
|
||||
return new SharingContactsAdapter.SharingContactViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
Group group = mGroups.get(position);
|
||||
SharingContactsAdapter.SharingContactViewHolder viewHolder = (SharingContactsAdapter.SharingContactViewHolder) holder;
|
||||
|
||||
int type = getItemViewType(position);
|
||||
|
||||
if (type == GROUP_TYPE) {
|
||||
viewHolder.name.setText(group.name);
|
||||
viewHolder.name.setTextColor(ContextCompat.getColor(viewHolder.name.getContext(), group.isHelpGroup() ? R.color.colorHelpGroup : R.color.colorTeamGroup));
|
||||
viewHolder.avatar.setImageResource(group.isHelpGroup() ? R.drawable.avatar_help_group : R.drawable.avatar_team_group);
|
||||
|
||||
return;
|
||||
} else if(!group.onlyEmployees) {
|
||||
viewHolder.avatar.setImageResource(R.drawable.avatar_help);
|
||||
}
|
||||
|
||||
if(type == CONTACT_TYPE) {
|
||||
viewHolder.name.setTextColor(ContextCompat.getColor(viewHolder.name.getContext(), group.isEmployee(mGroups.get(position).users) ? R.color.colorBlack : R.color.colorHelpGroup));
|
||||
}
|
||||
|
||||
viewHolder.name.setText(group.name);
|
||||
|
||||
Glide.with(viewHolder.avatar.getContext()).load(group.avatar)
|
||||
.placeholder(group.onlyEmployees ? R.drawable.avatar_list : R.drawable.avatar_help)
|
||||
.dontAnimate()
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.into(viewHolder.avatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mGroups.size();
|
||||
}
|
||||
|
||||
class SharingContactViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.sharing_contact_avatar)
|
||||
CircleImageView avatar;
|
||||
|
||||
@BindView(R.id.sharing_contact_name)
|
||||
TextView name;
|
||||
|
||||
SharingContactViewHolder(View view) {
|
||||
super(view);
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
@OnClick(R.id.sharing_contact_container)
|
||||
void onClickContact() {
|
||||
// int type = getItemViewType();
|
||||
//
|
||||
// if (type == GROUP_TYPE) {
|
||||
// mItemClickListener.onOpenGroupMessage(mGroups.get(getAdapterPosition()));
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// mItemClickListener.onOpenPrivateMessage(mGroups.get(getAdapterPosition()));
|
||||
|
||||
mItemClickListener.onConfirmSharingFile(mGroups.get(getAdapterPosition()));
|
||||
// TODO: open file confirmation!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import de.beyondsoft.ownchat.data.api.services.ChatService;
|
||||
import de.beyondsoft.ownchat.di.P3DSEndpoint;
|
||||
import de.beyondsoft.ownchat.di.scopes.ActivityScope;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
@Module
|
||||
public class SharingModule {
|
||||
@Provides
|
||||
@ActivityScope
|
||||
ChatService provideChatService(@P3DSEndpoint Retrofit retrofit) {
|
||||
return retrofit.create(ChatService.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import de.beyondsoft.ownchat.R;
|
||||
import de.beyondsoft.ownchat.data.AppPrefsConstants;
|
||||
import de.beyondsoft.ownchat.data.api.common.ErrorHandlingResponseConverter;
|
||||
import de.beyondsoft.ownchat.data.api.responses.MessageResponse;
|
||||
import de.beyondsoft.ownchat.data.api.services.ChatService;
|
||||
import de.beyondsoft.ownchat.data.events.GroupUpdatedEvent;
|
||||
import de.beyondsoft.ownchat.data.prefs.LongPreference;
|
||||
import de.beyondsoft.ownchat.data.prefs.StringPreference;
|
||||
import de.beyondsoft.ownchat.data.repository.GroupRealmRepository;
|
||||
import de.beyondsoft.ownchat.data.repository.RealmProvider;
|
||||
import de.beyondsoft.ownchat.data.repository.UserRealmRepository;
|
||||
import de.beyondsoft.ownchat.data.repository.specifications.GroupByIdSpecification;
|
||||
import de.beyondsoft.ownchat.data.repository.specifications.GroupSpecification;
|
||||
import de.beyondsoft.ownchat.data.repository.specifications.MessageByIdSpecification;
|
||||
import de.beyondsoft.ownchat.data.repository.specifications.UserByIdSpecification;
|
||||
import de.beyondsoft.ownchat.model.Group;
|
||||
import de.beyondsoft.ownchat.model.Message;
|
||||
import de.beyondsoft.ownchat.page.mvp.MVPAbstractPresenter;
|
||||
import de.beyondsoft.ownchat.utils.Constants;
|
||||
import de.beyondsoft.ownchat.utils.FileUtils;
|
||||
import de.beyondsoft.ownchat.utils.SystemUtils;
|
||||
import de.beyondsoft.ownchat.utils.rx.RxUtils;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import rx.Completable;
|
||||
import rx.Observable;
|
||||
import rx.Subscriber;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
import static de.beyondsoft.ownchat.data.AppPrefsConstants.USER_ID;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
public class SharingPresenter extends MVPAbstractPresenter<SharingView> implements SharingView.Callback {
|
||||
@Inject
|
||||
Context mContext;
|
||||
|
||||
@Inject
|
||||
ChatService mChatService;
|
||||
|
||||
@Inject
|
||||
GroupRealmRepository mGroupRealmRepository;
|
||||
|
||||
@Inject
|
||||
UserRealmRepository mUserRealmRepository;
|
||||
|
||||
@Inject
|
||||
SystemUtils mSystemUtils;
|
||||
|
||||
@Inject
|
||||
@Named(AppPrefsConstants.USER_ID)
|
||||
LongPreference mUserIdReference;
|
||||
|
||||
@Inject
|
||||
@Named(AppPrefsConstants.AVATAR)
|
||||
StringPreference mAvatarPreference;
|
||||
|
||||
@Inject
|
||||
@Named(AppPrefsConstants.NAME)
|
||||
StringPreference mProfileNamePreference;
|
||||
|
||||
@Inject
|
||||
@Named(AppPrefsConstants.UPLOAD_MAX_SIZE)
|
||||
LongPreference mUploadMaxSizePreference;
|
||||
|
||||
@Inject
|
||||
RealmProvider mRealmProvider;
|
||||
|
||||
@Inject
|
||||
SharingPresenter() {}
|
||||
|
||||
@Override
|
||||
protected void afterAttachView(@NonNull SharingView view) {
|
||||
super.afterAttachView(view);
|
||||
view.setCallback(this);
|
||||
view.showProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeDetachView(@NonNull SharingView view) {
|
||||
view.setCallback(null);
|
||||
super.beforeDetachView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGroups() {
|
||||
Subscription subscription = mChatService.getGroups()
|
||||
.flatMap(new ErrorHandlingResponseConverter<>())
|
||||
.flatMap(groupResponse -> {
|
||||
if (groupResponse.response.groups != null) {
|
||||
for (Group group : groupResponse.response.groups) {
|
||||
group.userId = mUserIdReference.get();
|
||||
|
||||
if (group.lastMessage != null) {
|
||||
group.lastMessage.status = Message.Status.DELIVERED;
|
||||
group.lastMessage.userId = mUserIdReference.get();
|
||||
}
|
||||
|
||||
mUserRealmRepository.update(group.users, new UserByIdSpecification()).subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
return Observable.just(groupResponse.response.groups);
|
||||
})
|
||||
.compose(RxUtils.provideDefaultTransformer())
|
||||
.subscribe(this::groupsSuccess, this::groupsError);
|
||||
addSubscription(subscription);
|
||||
}
|
||||
|
||||
private void groupsError(Throwable throwable) {
|
||||
if (mSystemUtils.isNetworkUnavailable()) {
|
||||
getView().showError(R.string.error_no_internet_connection);
|
||||
return;
|
||||
}
|
||||
|
||||
getView().showError(R.string.error_something_went_wrong);
|
||||
}
|
||||
|
||||
private void groupsSuccess(ArrayList<Group> groups) {
|
||||
mGroupRealmRepository.update(groups, new GroupByIdSpecification(mUserIdReference.get()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Subscriber<Object>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
loadGroupsFromDB();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
Log.e(SharingPresenter.class.getSimpleName(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
// nop here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadGroupsFromDB() {
|
||||
GroupSpecification groupSpecification = new GroupSpecification(mUserIdReference.get());
|
||||
mGroupRealmRepository.queryForAll(groupSpecification)
|
||||
.map(ArrayList::new)
|
||||
.compose(RxUtils.provideDefaultTransformer())
|
||||
.subscribe(this::loadGroupsFromDBSuccessful, this::loadGroupsError);
|
||||
}
|
||||
|
||||
private void loadGroupsFromDBSuccessful(ArrayList<Group> groups) {
|
||||
if (!isAttached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
getView().hideProgress();
|
||||
getView().setGroups(groups);
|
||||
}
|
||||
|
||||
private void loadGroupsError(Throwable throwable) {
|
||||
Log.e(SharingPresenter.class.getSimpleName(), throwable.getMessage(), throwable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendFile(Uri fileUri, String messageText, long groupId) {
|
||||
File file = getView().getFile(fileUri);
|
||||
|
||||
final String mimeType = FileUtils.getMimeType(file);
|
||||
|
||||
if (file == null || !file.exists()) {
|
||||
getView().showToastMessage(R.string.error_file_not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mUploadMaxSizePreference.get() > 0 && file.length() > mUploadMaxSizePreference.get()) {
|
||||
getView().showError(R.string.error_file_is_too_large);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mimeType != null && (mimeType.equals(Constants.MIME_TYPE_JPEG) || mimeType.equals(Constants.MIME_TYPE_PNG)) && (file.length() > (1000L * 1024L))) {
|
||||
FileUtils.clearTmpFilesDir(mContext);
|
||||
|
||||
file = FileUtils.scaleImageDown(file, mContext);
|
||||
}
|
||||
|
||||
final File fileToSend = file;
|
||||
|
||||
String[] split = fileToSend.getAbsolutePath().split("/");
|
||||
|
||||
Message message = new Message(findUniqueRandomId(mRealmProvider, mUserIdReference.get()),
|
||||
groupId,
|
||||
System.currentTimeMillis() / 1000,
|
||||
messageText,
|
||||
fileToSend.getPath(),
|
||||
fileToSend.getPath(),
|
||||
split[split.length - 1],
|
||||
fileUri.toString(),
|
||||
mUserIdReference.get(),
|
||||
mProfileNamePreference.get(),
|
||||
mAvatarPreference.get(),
|
||||
Message.Status.PENDING,
|
||||
true,
|
||||
mUserIdReference.get(),
|
||||
false);
|
||||
|
||||
storeMessageLocally(message).subscribe(new Subscriber<Object>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
if (mSystemUtils.isNetworkUnavailable()) {
|
||||
getView().showError(R.string.error_no_internet_connection);
|
||||
message.status = Message.Status.FAILED;
|
||||
message.isRead = true;
|
||||
storeMessageLocally(message, message.id, true);
|
||||
storeGroupLocally(groupId, message);
|
||||
return;
|
||||
}
|
||||
|
||||
checkForEmptyMessageAndHideIt();
|
||||
message.createdAt *= 1000;
|
||||
getView().addOneMessage(message);
|
||||
|
||||
// create RequestBody instance from file
|
||||
RequestBody requestFile = RequestBody.create(getView().getMediaType(mimeType), fileToSend);
|
||||
|
||||
// MultipartBody.Part is used to send also the actual file name
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("file", fileToSend.getName(), requestFile);
|
||||
|
||||
// add another part within the multipart request
|
||||
String groupIdString = String.valueOf(groupId);
|
||||
RequestBody groupIdBody = RequestBody.create(okhttp3.MultipartBody.FORM, groupIdString);
|
||||
|
||||
RequestBody textBody = RequestBody.create(okhttp3.MultipartBody.FORM, message.message == null ? "" : message.message);
|
||||
|
||||
mChatService.sendFile(groupIdBody, body, textBody)
|
||||
.compose(RxUtils.provideDefaultTransformer())
|
||||
.flatMap(new ErrorHandlingResponseConverter<>())
|
||||
.subscribe(new Subscriber<MessageResponse>() {
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
FileUtils.clearTmpFilesDir(mContext);
|
||||
Log.e("sendFile", "onError", e);
|
||||
@SuppressWarnings("UnnecessaryLocalVariable") Message newMessage = message;
|
||||
newMessage.createdAt /= 1000;
|
||||
newMessage.status = Message.Status.FAILED;
|
||||
newMessage.isRead = true;
|
||||
storeMessageLocally(newMessage, message.id, false);
|
||||
storeGroupLocally(groupId, message);
|
||||
|
||||
if (!isAttached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSystemUtils.isNetworkUnavailable()) {
|
||||
getView().showError(R.string.error_no_internet_connection);
|
||||
return;
|
||||
}
|
||||
|
||||
getView().showError(R.string.error_something_went_wrong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(MessageResponse baseResponse) {
|
||||
FileUtils.clearTmpFilesDir(mContext);
|
||||
deleteLocalMessage(message);
|
||||
baseResponse.response.message.status = Message.Status.DELIVERED;
|
||||
baseResponse.response.message.isRead = true;
|
||||
storeMessageLocally(baseResponse.response.message, message.id, false);
|
||||
storeGroupLocally(groupId, baseResponse.response.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
Log.e("sendFile", "onError", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Completable storeMessageLocally(Message message) {
|
||||
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdReference.get());
|
||||
messageByIdSpecification.setMessageId(message.id);
|
||||
|
||||
return mMessageRealmRepository.update(message, messageByIdSpecification)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
private void storeGroupLocally(long groupId, Message message) {
|
||||
Group group = new Group();
|
||||
group.id = groupId;
|
||||
group.lastMessage = message;
|
||||
mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdReference.get()))
|
||||
.subscribe(() -> {
|
||||
if (!isAttached()) {
|
||||
EventBus.getDefault().post(new GroupUpdatedEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package de.beyondsoft.ownchat.page.sharing;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.StringRes;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.beyondsoft.ownchat.model.Group;
|
||||
import de.beyondsoft.ownchat.page.mvp.MVPView;
|
||||
|
||||
/**
|
||||
* Created by Lyndon on 03-Nov-2017.
|
||||
*/
|
||||
|
||||
public interface SharingView extends MVPView {
|
||||
void setCallback(Callback setCallback);
|
||||
|
||||
void showProgress();
|
||||
|
||||
void hideProgress();
|
||||
|
||||
void setGroups(ArrayList<Group> groups);
|
||||
|
||||
void showError(@StringRes int message);
|
||||
|
||||
File getFile(Uri fileUri);
|
||||
|
||||
void showToastMessage(@StringRes int message);
|
||||
|
||||
interface Callback {
|
||||
void loadGroups();
|
||||
|
||||
void loadGroupsFromDB();
|
||||
|
||||
void sendFile(Uri fileUri, String messageText);
|
||||
}
|
||||
}
|
||||
@@ -1233,6 +1233,8 @@ public class FileUtils {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Utils.copyExifData(image, tmpFile, null);
|
||||
|
||||
return tmpFile;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,30 @@
|
||||
package de.beyondsoft.ownchat.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import org.apache.sanselan.ImageReadException;
|
||||
import org.apache.sanselan.ImageWriteException;
|
||||
import org.apache.sanselan.Sanselan;
|
||||
import org.apache.sanselan.common.IImageMetadata;
|
||||
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
|
||||
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
|
||||
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
|
||||
import org.apache.sanselan.formats.tiff.constants.TagInfo;
|
||||
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
|
||||
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
|
||||
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
|
||||
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by imarneanu on 2/13/17.
|
||||
@@ -26,7 +38,7 @@ public class Utils {
|
||||
public static void clearFullBackStack(AppCompatActivity appCompatActivity) {
|
||||
FragmentManager fragmentManager = appCompatActivity.getSupportFragmentManager();
|
||||
|
||||
if (fragmentManager.getBackStackEntryCount() > 0) {
|
||||
if(fragmentManager.getBackStackEntryCount() > 0) {
|
||||
// clear the fragment back stack
|
||||
FragmentManager.BackStackEntry first = fragmentManager.getBackStackEntryAt(0);
|
||||
fragmentManager.popBackStackImmediate(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
@@ -43,4 +55,131 @@ public class Utils {
|
||||
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
public static boolean copyExifData(File sourceFile, File destFile, List<TagInfo> excludedFields) {
|
||||
String tempFileName = destFile.getAbsolutePath() + ".tmp";
|
||||
File tempFile = null;
|
||||
OutputStream tempStream = null;
|
||||
|
||||
try {
|
||||
tempFile = new File (tempFileName);
|
||||
|
||||
TiffOutputSet sourceSet = getSanselanOutputSet(sourceFile, TiffConstants.DEFAULT_TIFF_BYTE_ORDER);
|
||||
TiffOutputSet destSet = getSanselanOutputSet(destFile, sourceSet.byteOrder);
|
||||
|
||||
// If the EXIF data endianess of the source and destination files
|
||||
// differ then fail. This only happens if the source and
|
||||
// destination images were created on different devices. It's
|
||||
// technically possible to copy this data by changing the byte
|
||||
// order of the data, but handling this case is outside the scope
|
||||
// of this implementation
|
||||
if (sourceSet.byteOrder != destSet.byteOrder) return false;
|
||||
|
||||
destSet.getOrCreateExifDirectory();
|
||||
|
||||
// Go through the source directories
|
||||
List<?> sourceDirectories = sourceSet.getDirectories();
|
||||
for (int i=0; i<sourceDirectories.size(); i++) {
|
||||
TiffOutputDirectory sourceDirectory = (TiffOutputDirectory)sourceDirectories.get(i);
|
||||
TiffOutputDirectory destinationDirectory = getOrCreateExifDirectory(destSet, sourceDirectory);
|
||||
|
||||
if (destinationDirectory == null) continue; // failed to create
|
||||
|
||||
// Loop the fields
|
||||
List<?> sourceFields = sourceDirectory.getFields();
|
||||
for (int j=0; j<sourceFields.size(); j++) {
|
||||
// Get the source field
|
||||
TiffOutputField sourceField = (TiffOutputField) sourceFields.get(j);
|
||||
|
||||
// Check exclusion list
|
||||
if (excludedFields != null && excludedFields.contains(sourceField.tagInfo)) {
|
||||
destinationDirectory.removeField(sourceField.tagInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove any existing field
|
||||
destinationDirectory.removeField(sourceField.tagInfo);
|
||||
|
||||
// Add field
|
||||
destinationDirectory.add(sourceField);
|
||||
}
|
||||
}
|
||||
|
||||
// Save data to destination
|
||||
tempStream = new BufferedOutputStream(new FileOutputStream(tempFile));
|
||||
new ExifRewriter().updateExifMetadataLossless(destFile, tempStream, destSet);
|
||||
tempStream.close();
|
||||
|
||||
// Replace file
|
||||
if (destFile.delete()) {
|
||||
tempFile.renameTo(destFile);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (ImageReadException | ImageWriteException | IOException exception) {
|
||||
exception.printStackTrace();
|
||||
} finally {
|
||||
if (tempStream != null) {
|
||||
try {
|
||||
tempStream.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (tempFile != null) {
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static TiffOutputSet getSanselanOutputSet(File jpegImageFile, int defaultByteOrder) throws IOException, ImageReadException, ImageWriteException {
|
||||
TiffImageMetadata exif = null;
|
||||
TiffOutputSet outputSet = null;
|
||||
|
||||
IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
|
||||
JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
|
||||
|
||||
if(jpegMetadata != null) {
|
||||
exif = jpegMetadata.getExif();
|
||||
|
||||
if (exif != null) {
|
||||
outputSet = exif.getOutputSet();
|
||||
}
|
||||
}
|
||||
|
||||
// If JPEG file contains no EXIF metadata, create an empty set
|
||||
// of EXIF metadata. Otherwise, use existing EXIF metadata to
|
||||
// keep all other existing tags
|
||||
if (outputSet == null) {
|
||||
outputSet = new TiffOutputSet(exif==null?defaultByteOrder:exif.contents.header.byteOrder);
|
||||
}
|
||||
|
||||
return outputSet;
|
||||
}
|
||||
|
||||
private static TiffOutputDirectory getOrCreateExifDirectory(TiffOutputSet outputSet, TiffOutputDirectory outputDirectory) {
|
||||
TiffOutputDirectory result = outputSet.findDirectory(outputDirectory.type);
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = new TiffOutputDirectory(outputDirectory.type);
|
||||
|
||||
try {
|
||||
outputSet.addDirectory(result);
|
||||
}
|
||||
catch (ImageWriteException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sharing_contacts_coordinator_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include layout="@layout/chat_toolbar" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/sharing_contacts_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorWhite" />
|
||||
|
||||
<de.beyondsoft.ownchat.utils.ui.EmptyLayout
|
||||
android:id="@+id/sharing_contacts_empty_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/contacts_coordinator_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/sharing_contact_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/item_contact_height"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/sharing_contact_avatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="@dimen/item_contact_avatar_padding"
|
||||
android:layout_marginRight="@dimen/item_contact_avatar_padding"
|
||||
android:contentDescription="@null"
|
||||
tools:src="@drawable/sasha_grey" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sharing_contact_name"
|
||||
style="@style/AppTheme.ListTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Sasha Grey" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/file_sharing_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:src="@drawable/send"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -86,5 +86,6 @@
|
||||
<string name="select_photo_text">Eine Mediendatei anhängen</string>
|
||||
<string name="select_doc_text">Ein Dokument anhängen</string>
|
||||
<string name="error_file_is_too_large">Die Datei, die Sie versuchen hochzuladen, ist zu groß</string>
|
||||
<string name="sharing_title">Datei teilen</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user