Added the sharing option

This commit is contained in:
Lyndon
2017-11-03 20:25:19 +01:00
parent 58954a64b3
commit 267729f68b
24 changed files with 1011 additions and 23 deletions

View File

@@ -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") {

View File

@@ -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

View File

@@ -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()

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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!
}
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
});
}
}

View File

@@ -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);
}
}

View File

@@ -1233,6 +1233,8 @@ public class FileUtils {
e.printStackTrace();
}
Utils.copyExifData(image, tmpFile, null);
return tmpFile;
}
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>