diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle b/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle index 67fc768..b51657b 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/build.gradle @@ -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") { diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/libs/sanselan-0_97-android-1.0.0.jar b/BeWoPlanerChat/bewoplaner-chat-android/app/libs/sanselan-0_97-android-1.0.0.jar new file mode 100644 index 0000000..c6aecdf Binary files /dev/null and b/BeWoPlanerChat/bewoplaner-chat-android/app/libs/sanselan-0_97-android-1.0.0.jar differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/AndroidManifest.xml b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/AndroidManifest.xml index 4e18d66..03e4bed 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/AndroidManifest.xml +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/AndroidManifest.xml @@ -122,6 +122,17 @@ android:theme="@style/ChatStyle" /> + + + + + + + + \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_audio-web.png b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_audio-web.png deleted file mode 100644 index 8fb84bb..0000000 Binary files a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_audio-web.png and /dev/null differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_document-web.png b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_document-web.png deleted file mode 100644 index c701955..0000000 Binary files a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_document-web.png and /dev/null differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_gallery-web.png b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_gallery-web.png deleted file mode 100644 index 6e856ec..0000000 Binary files a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_dialog_gallery-web.png and /dev/null differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_picker_document-web.png b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_picker_document-web.png deleted file mode 100644 index b1f9597..0000000 Binary files a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_file_picker_document-web.png and /dev/null differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_launcher-web.png b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_launcher-web.png deleted file mode 100644 index c14e119..0000000 Binary files a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/ic_launcher-web.png and /dev/null differ diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/di/InjectionHelper.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/di/InjectionHelper.java index 6a03067..48e18b3 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/di/InjectionHelper.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/di/InjectionHelper.java @@ -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() diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java index 3055288..3762bcf 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/chat/BaseChatActivity.java @@ -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); diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/contacts/ContactsPresenter.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/contacts/ContactsPresenter.java index 3f28ba9..5e9d3df 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/contacts/ContactsPresenter.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/contacts/ContactsPresenter.java @@ -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 - implements ContactsView.Callback { +class ContactsPresenter extends MVPAbstractPresenter implements ContactsView.Callback { @Inject ChatService mChatService; @@ -80,21 +77,21 @@ class ContactsPresenter extends MVPAbstractPresenter public void loadGroups() { Subscription subscription = mChatService.getGroups() .flatMap(new ErrorHandlingResponseConverter<>()) - .flatMap(new Func1>>() { - @Override - public Observable> 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 getView().showError(R.string.error_no_internet_connection); return; } + getView().showError(R.string.error_something_went_wrong); } diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingActivity.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingActivity.java new file mode 100644 index 0000000..504711a --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingActivity.java @@ -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 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(); + } +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingComponent.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingComponent.java new file mode 100644 index 0000000..093172e --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingComponent.java @@ -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); +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingContactsAdapter.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingContactsAdapter.java new file mode 100644 index 0000000..c85983b --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingContactsAdapter.java @@ -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 { + private static final int GROUP_TYPE = 0; + private static final int CONTACT_TYPE = 1; + + private ArrayList 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 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! + } + } +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingModule.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingModule.java new file mode 100644 index 0000000..1eb1bf7 --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingModule.java @@ -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); + } +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingPresenter.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingPresenter.java new file mode 100644 index 0000000..6e29c01 --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingPresenter.java @@ -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 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 groups) { + mGroupRealmRepository.update(groups, new GroupByIdSpecification(mUserIdReference.get())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Subscriber() { + @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 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() { + @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() { + @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()); + } + }); + } +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingView.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingView.java new file mode 100644 index 0000000..0964019 --- /dev/null +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/page/sharing/SharingView.java @@ -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 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); + } +} diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/FileUtils.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/FileUtils.java index d009a11..2cd522e 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/FileUtils.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/FileUtils.java @@ -1233,6 +1233,8 @@ public class FileUtils { e.printStackTrace(); } + Utils.copyExifData(image, tmpFile, null); + return tmpFile; } } \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/Utils.java b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/Utils.java index e1b770a..514e7c8 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/Utils.java +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/java/de/beyondsoft/ownchat/utils/Utils.java @@ -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 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 sourceFields = sourceDirectory.getFields(); + for (int j=0; j + + + + + + + + + + + + \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/fragment_contacts.xml b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/fragment_contacts.xml index 2970a2e..ce2da8b 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/fragment_contacts.xml +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/layout/fragment_contacts.xml @@ -1,5 +1,6 @@ - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/values/strings.xml b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/values/strings.xml index 536c768..65b44bd 100644 --- a/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/values/strings.xml +++ b/BeWoPlanerChat/bewoplaner-chat-android/app/src/main/res/values/strings.xml @@ -86,5 +86,6 @@ Eine Mediendatei anhängen Ein Dokument anhängen Die Datei, die Sie versuchen hochzuladen, ist zu groß + Datei teilen \ No newline at end of file