diff --git a/.idea/copyright/GPL3.xml b/.idea/copyright/GPL3.xml index 8a6afea9b..421c5b53b 100644 --- a/.idea/copyright/GPL3.xml +++ b/.idea/copyright/GPL3.xml @@ -1,6 +1,7 @@ - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7cf5d455e..b67a39049 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,27 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'findbugs' +apply plugin: 'kotlin-kapt' def taskRequest = getGradle().getStartParameter().getTaskRequests().toString() if (taskRequest.contains("Gplay") || taskRequest.contains("findbugs") || taskRequest.contains("lint")) { @@ -17,8 +38,8 @@ android { targetSdkVersion 28 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 92 - versionName "6.0.0beta4" + versionCode 93 + versionName "6.0.0" flavorDimensions "default" renderscriptTargetApi 19 @@ -110,8 +131,9 @@ configurations.all { exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' } + dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(include: ['*'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha4' @@ -121,6 +143,13 @@ dependencies { implementation "android.arch.work:work-rxjava2:${workVersion}" implementation 'com.google.android:flexbox:1.1.0' androidTestImplementation "android.arch.work:work-testing:${workVersion}" + implementation ('com.gitlab.bitfireAT:dav4jvm:f2078bc846', { + exclude group: 'org.ogce', module: 'xpp3' // Android comes with its own XmlPullParser + }) + compile 'org.conscrypt:conscrypt-android:2.0.0' + + + implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation 'androidx.biometric:biometric:1.0.0-alpha04' implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" @@ -139,76 +168,57 @@ dependencies { implementation 'com.bluelinelabs:logansquare:1.3.7' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.8' - annotationProcessor 'com.bluelinelabs:logansquare-compiler:1.3.7' + kapt 'com.bluelinelabs:logansquare-compiler:1.3.7' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.github.aurae.retrofit2:converter-logansquare:1.4.1' implementation 'com.google.dagger:dagger:2.21' - annotationProcessor 'com.google.dagger:dagger-compiler:2.21' + kapt 'com.google.dagger:dagger-compiler:2.21' implementation 'com.github.lukaspili.autodagger2:autodagger2:1.1' - annotationProcessor 'com.github.lukaspili.autodagger2:autodagger2-compiler:1.1' - - compileOnly 'javax.annotation:jsr250-api:1.0' // Android only - + kapt 'com.github.lukaspili.autodagger2:autodagger2-compiler:1.1' + compileOnly 'javax.annotation:jsr250-api:1.0' + // Android only implementation 'org.greenrobot:eventbus:3.1.1' - implementation 'io.requery:requery:1.5.1' implementation 'io.requery:requery-android:1.5.1' implementation 'net.zetetic:android-database-sqlcipher:3.5.9' - annotationProcessor 'io.requery:requery-processor:1.5.1' - + kapt 'io.requery:requery-processor:1.5.1' implementation 'org.parceler:parceler-api:1.1.12' - annotationProcessor 'org.parceler:parceler:1.1.12' - + kapt 'org.parceler:parceler:1.1.12' implementation 'net.orange-box.storebox:storebox-lib:1.4.0' - - compileOnly "org.projectlombok:lombok:1.18.6" + compileOnly 'org.projectlombok:lombok:1.18.6' annotationProcessor "org.projectlombok:lombok:1.18.6" - implementation 'com.jakewharton:butterknife:10.1.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' - + kapt 'com.jakewharton:butterknife-compiler:10.1.0' implementation 'com.github.HITGIF:TextFieldBoxes:1.4.3' - implementation 'eu.davidea:flexible-adapter:5.1.0' implementation 'eu.davidea:flexible-adapter-ui:1.0.0' - - implementation 'com.github.bumptech.glide:glide:4.9.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' - implementation 'com.github.bumptech.glide:okhttp3-integration:4.9.0@aar' - implementation 'com.facebook.fresco:fresco:1.13.0' - implementation 'com.facebook.fresco:animated-webp:1.13.0' - implementation 'com.facebook.fresco:webpsupport:1.13.0' - implementation 'com.facebook.fresco:animated-gif:1.13.0' - implementation "com.facebook.fresco:imagepipeline-okhttp3:1.13.0" implementation 'org.webrtc:google-webrtc:1.0.23295' implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}" - implementation 'com.yarolegovich:lovely-dialog:1.1.0' implementation 'com.yarolegovich:lovelyinput:1.0.9' implementation 'com.yarolegovich:mp:1.0.9' - implementation 'me.zhanghai.android.effortlesspermissions:library:1.1.0' - - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1' - + implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'com.github.wooplr:Spotlight:1.3' + implementation('com.github.mario:chatkit:d63d61db95', { + exclude group: 'com.facebook.fresco' + }) - implementation 'com.github.mario:chatkit:d63d61db95' + implementation 'com.mario.fresco:fresco:1.11.1-headers' + implementation 'com.mario.fresco:animated-webp:1.11.1-headers' + implementation 'com.mario.fresco:webpsupport:1.11.1-headers' + implementation 'com.mario.fresco:animated-gif:1.11.1-headers' + implementation "com.mario.fresco:imagepipeline-okhttp3:1.11.1-headers" implementation 'com.github.natario1:Autocomplete:v1.1.0' implementation 'com.github.Kennyc1012:BottomSheet:2.4.1' - implementation 'eu.davidea:flipview:1.2.0' implementation 'com.github.mario:PopupBubble:a365177d96' - implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - implementation 'com.kevalpatel2106:emoticongifkeyboard:1.1' - - implementation group: 'eu.medsea.mimeutil', name: 'mime-util', version: '2.1.3' - + implementation 'eu.medsea.mimeutil:mime-util:2.1.3' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.26.0' testImplementation 'org.powermock:powermock-core:2.0.0' @@ -218,7 +228,14 @@ dependencies { androidTestImplementation ('androidx.test.espresso:espresso-core:3.1.0-alpha4', { exclude group: 'com.android.support', module: 'support-annotations' }) - findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0' findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.3' } + +gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + options.compilerArgs += + ['-Adagger.floatingBindsMethods=enabled', + '-AparcelerStacktrace',] + } +} diff --git a/app/gplay.gradle b/app/gplay.gradle index 32b727920..e22deaf3a 100644 --- a/app/gplay.gradle +++ b/app/gplay.gradle @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + dependencies { implementation "com.google.firebase:firebase-messaging:17.3.4" } diff --git a/app/src/gplay/AndroidManifest.xml b/app/src/gplay/AndroidManifest.xml index 2f388c5f5..d01c10ff7 100644 --- a/app/src/gplay/AndroidManifest.xml +++ b/app/src/gplay/AndroidManifest.xml @@ -1,3 +1,23 @@ + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 78dbc8c2c..22facacdf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,3 +1,23 @@ + + diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java index 40a502e53..529b98bcb 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java @@ -26,26 +26,23 @@ import android.widget.*; import androidx.annotation.Nullable; import butterknife.BindView; import butterknife.ButterKnife; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.request.RequestOptions; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.glide.GlideApp; +import com.nextcloud.talk.utils.DisplayUtils; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFilterable; import eu.davidea.flexibleadapter.utils.FlexibleUtils; -import eu.davidea.flipview.FlipView; import eu.davidea.viewholders.FlexibleViewHolder; -import org.apache.commons.lang3.StringUtils; import java.util.List; +import java.util.regex.Pattern; public class AdvancedUserItem extends AbstractFlexibleItem implements IFilterable { @@ -116,23 +113,15 @@ public class AdvancedUserItem extends AbstractFlexibleItem implements IFilterable { @@ -134,21 +130,14 @@ public class CallItem extends AbstractFlexibleItem .nc_description_more_menu_one_to_one), conversation.getDisplayName())); if (!TextUtils.isEmpty(conversation.getName())) { - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - conversation.getName(), R.dimen.avatar_size), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); - - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(glideUrl) - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(holder.avatarImageView.getFrontImageView()); - + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + conversation.getLastMessage().getActorId(), + R.dimen.avatar_size), null)) + .build(); + holder.avatarImageView.setController(draweeController); } else { holder.avatarImageView.setVisibility(View.GONE); } @@ -156,19 +145,13 @@ public class CallItem extends AbstractFlexibleItem case ROOM_GROUP_CALL: holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string .nc_description_more_menu_group), conversation.getDisplayName())); - - holder.avatarImageView.setFrontImageBitmap(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(resources, - R.drawable.ic_people_group_white_24px)); + holder.avatarImageView.setActualImageResource(R.drawable.ic_people_group_white_24px); holder.avatarImageView.setVisibility(View.VISIBLE); break; case ROOM_PUBLIC_CALL: holder.moreMenuButton.setContentDescription(String.format(resources.getString(R.string .nc_description_more_menu_public), conversation.getDisplayName())); - - holder.avatarImageView.setFrontImageBitmap(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(resources, - R.drawable.ic_link_white_24px)); + holder.avatarImageView.setActualImageResource(R.drawable.ic_link_white_24px); holder.avatarImageView.setVisibility(View.VISIBLE); break; default: @@ -182,8 +165,7 @@ public class CallItem extends AbstractFlexibleItem @Override public boolean filter(String constraint) { return conversation.getDisplayName() != null && - StringUtils.containsIgnoreCase(conversation.getDisplayName().trim(), constraint); - + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(conversation.getDisplayName().trim()).find(); } static class RoomItemViewHolder extends FlexibleViewHolder { @@ -193,7 +175,7 @@ public class CallItem extends AbstractFlexibleItem @BindView(R.id.secondary_text) public TextView roomLastPing; @BindView(R.id.avatar_image) - public FlipView avatarImageView; + public SimpleDraweeView avatarImageView; @BindView(R.id.more_menu) public ImageButton moreMenuButton; @BindView(R.id.password_protected_image_view) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java index 7eae2be1c..28febe848 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.java @@ -21,6 +21,7 @@ package com.nextcloud.talk.adapters.items; import android.content.Context; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.text.TextUtils; @@ -31,11 +32,9 @@ import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; import com.amulyakhare.textdrawable.TextDrawable; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.request.RequestOptions; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.models.database.UserEntity; @@ -43,7 +42,6 @@ import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.models.json.rooms.Conversation; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.glide.GlideApp; import com.vanniktech.emoji.EmojiTextView; import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; @@ -51,9 +49,9 @@ import eu.davidea.flexibleadapter.items.IFilterable; import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.flexibleadapter.utils.FlexibleUtils; import eu.davidea.viewholders.FlexibleViewHolder; -import org.apache.commons.lang3.StringUtils; import java.util.List; +import java.util.regex.Pattern; public class ConversationItem extends AbstractFlexibleItem implements IFilterable { @@ -146,10 +144,10 @@ public class ConversationItem extends AbstractFlexibleItem", context.getResources().getColor(R.color.black)); @@ -213,8 +201,6 @@ public class ConversationItem extends AbstractFlexibleItem implements IFilterable { +public class GenericTextHeaderItem extends AbstractHeaderItem { private static final String TAG = "GenericTextHeaderItem"; private String title; @@ -47,11 +45,6 @@ public class GenericTextHeaderItem extends AbstractHeaderItem implements IFilterable { @@ -115,32 +111,23 @@ public class MentionAutocompleteItem extends AbstractFlexibleItem adapter, NotificationSoundItemViewHolder holder, int position, List payloads) { - flipView = holder.magicFlipView; - - holder.magicFlipView.flipSilently(adapter.isSelected(position) || isSelected()); - - if (isSelected() && !adapter.isSelected(position)) { - adapter.toggleSelection(position); - selected = false; - } - holder.notificationName.setText(notificationSoundName); - if (position == 0) { - holder.magicFlipView.setFrontImage(R.drawable.ic_stop_white_24dp); + if (adapter.isSelected(position)) { + holder.checkedImageView.setVisibility(View.VISIBLE); } else { - holder.magicFlipView.setFrontImage(R.drawable.ic_play_circle_outline_white_24dp); + holder.checkedImageView.setVisibility(View.GONE); + } + + Resources resources = NextcloudTalkApplication.getSharedApplication().getResources(); + holder.simpleDraweeView.getHierarchy().setBackgroundImage(new ColorDrawable(resources.getColor(R.color.colorPrimary))); + if (position == 0) { + holder.simpleDraweeView.getHierarchy().setImage(resources.getDrawable(R.drawable.ic_stop_white_24dp), 100, + true); + } else { + holder.simpleDraweeView.getHierarchy().setImage(resources.getDrawable(R.drawable.ic_play_circle_outline_white_24dp), 100, + true); } } static class NotificationSoundItemViewHolder extends FlexibleViewHolder { @BindView(R.id.notificationNameTextView) public TextView notificationName; - @BindView(R.id.magicFlipView) - MagicFlipView magicFlipView; + @BindView(R.id.simpleDraweeView) + SimpleDraweeView simpleDraweeView; + @BindView(R.id.checkedImageView) + ImageView checkedImageView; /** * Default constructor. diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java b/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java index 6af2fb468..14e5e74ef 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ProgressItem.java @@ -139,4 +139,4 @@ public class ProgressItem extends AbstractFlexibleItem implements ISectionable, IFilterable { @@ -59,9 +56,6 @@ public class UserItem extends AbstractFlexibleItem private UserEntity userEntity; private GenericTextHeaderItem header; - private FlipView flipView; - - public UserItem(Participant participant, UserEntity userEntity, GenericTextHeaderItem genericTextHeaderItem) { this.participant = participant; this.userEntity = userEntity; @@ -94,9 +88,6 @@ public class UserItem extends AbstractFlexibleItem return userEntity; } - public void flipItemSelection() { - flipView.flip(!flipView.isFlipped()); - } @Override public int getLayoutRes() { @@ -115,9 +106,13 @@ public class UserItem extends AbstractFlexibleItem @Override public void bindViewHolder(FlexibleAdapter adapter, UserItemViewHolder holder, int position, List payloads) { - flipView = holder.avatarFlipView; - - flipView.flipSilently(adapter.isSelected(position)); + if (holder.checkedImageView != null) { + if (adapter.isSelected(position)) { + holder.checkedImageView.setVisibility(View.VISIBLE); + } else { + holder.checkedImageView.setVisibility(View.GONE); + } + } if (adapter.hasFilter()) { FlexibleUtils.highlightText(holder.contactDisplayName, participant.getDisplayName(), @@ -132,35 +127,23 @@ public class UserItem extends AbstractFlexibleItem } } - int avatarSize = Math.round(NextcloudTalkApplication - .getSharedApplication().getResources().getDimension(R.dimen.avatar_size)); - if (TextUtils.isEmpty(participant.getSource()) || participant.getSource().equals("users")) { if (Participant.ParticipantType.GUEST.equals(participant.getType()) || Participant.ParticipantType.USER_FOLLOWING_LINK.equals(participant.getType())) { // TODO: Show generated avatar for guests } else { - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), - participant.getUserId(), R.dimen.avatar_size), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.simpleDraweeView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userEntity.getBaseUrl(), + participant.getUserId(), R.dimen.avatar_size), null)) + .build(); + holder.simpleDraweeView.setController(draweeController); - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(glideUrl) - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(flipView.getFrontImageView()); } } else if ("groups".equals(participant.getSource())) { - - flipView.setFrontImageBitmap(DisplayUtils - .getRoundedBitmapFromVectorDrawableResource(NextcloudTalkApplication.getSharedApplication().getResources(), - R.drawable.ic_people_group_white_24px)); + holder.simpleDraweeView.getHierarchy().setImage(new BitmapDrawable(DisplayUtils.getRoundedBitmapFromVectorDrawableResource(NextcloudTalkApplication.getSharedApplication().getResources(), R.drawable.ic_people_group_white_24px)), 100, true); } if (!isEnabled()) { @@ -206,37 +189,40 @@ public class UserItem extends AbstractFlexibleItem break; } - String userType = ""; - switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) { - case 1: - //userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_owner); - //break; - case 2: - userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_moderator); - break; - case 3: - userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_user); - break; - case 4: - userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest); - break; - case 5: - userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_following_link); - break; - default: - break; + if (holder.contactMentionId != null) { + String userType = ""; + + switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) { + case 1: + //userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_owner); + //break; + case 2: + userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_moderator); + break; + case 3: + userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_user); + break; + case 4: + userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest); + break; + case 5: + userType = NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_following_link); + break; + default: + break; + } + + holder.contactMentionId.setText(userType); + holder.contactMentionId.setTextColor(NextcloudTalkApplication.getSharedApplication().getResources().getColor(R.color.colorPrimary)); } - - holder.contactMentionId.setText(userType); - holder.contactMentionId.setTextColor(NextcloudTalkApplication.getSharedApplication().getResources().getColor(R.color.colorPrimary)); } } @Override public boolean filter(String constraint) { return participant.getDisplayName() != null && - StringUtils.containsIgnoreCase(participant.getDisplayName().trim(), constraint); + Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL).matcher(participant.getDisplayName().trim()).find(); } @Override @@ -254,8 +240,8 @@ public class UserItem extends AbstractFlexibleItem @BindView(R.id.name_text) public TextView contactDisplayName; - @BindView(R.id.avatar_flip_view) - public FlipView avatarFlipView; + @BindView(R.id.simple_drawee_view) + public SimpleDraweeView simpleDraweeView; @Nullable @BindView(R.id.secondary_text) public TextView contactMentionId; @@ -265,6 +251,9 @@ public class UserItem extends AbstractFlexibleItem @Nullable @BindView(R.id.videoCallImageView) ImageView videoCallImageView; + @Nullable + @BindView(R.id.checkedImageView) + ImageView checkedImageView; /** * Default constructor. diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java index 8684b31aa..e980add5f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.java @@ -145,14 +145,14 @@ public class MagicIncomingTextMessageViewHolder Map individualHashMap = message.getMessageParameters().get(key); if (individualHashMap != null) { if (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call")) { - if (individualHashMap.get("id").equals(message.getActiveUserId())) { + if (individualHashMap.get("id").equals(message.getActiveUser().getUserId())) { messageString = DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), messageString, individualHashMap.get("id"), individualHashMap.get("name"), individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUserId()), + userUtils.getUserById(message.getActiveUser().getUserId()), R.xml.chip_accent_background); } else { messageString = @@ -161,7 +161,7 @@ public class MagicIncomingTextMessageViewHolder individualHashMap.get("id"), individualHashMap.get("name"), individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUserId()), + userUtils.getUserById(message.getActiveUser().getUserId()), R.xml.chip_incoming_others); } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java index 7f1cca527..755243b20 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.java @@ -92,14 +92,14 @@ public class MagicOutcomingTextMessageViewHolder extends MessageHolders.Outcomin Map individualHashMap = message.getMessageParameters().get(key); if (individualHashMap != null) { if (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call")) { - if (!individualHashMap.get("id").equals(message.getActiveUserId())) { + if (!individualHashMap.get("id").equals(message.getActiveUser().getUserId())) { messageString = DisplayUtils.searchAndReplaceWithMentionSpan(messageText.getContext(), messageString, individualHashMap.get("id"), individualHashMap.get("name"), individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUserId()), + userUtils.getUserById(message.getActiveUser().getUserId()), R.xml.chip_outgoing_others); } else { messageString = @@ -108,7 +108,7 @@ public class MagicOutcomingTextMessageViewHolder extends MessageHolders.Outcomin individualHashMap.get("id"), individualHashMap.get("name"), individualHashMap.get("type"), - userUtils.getUserById(message.getActiveUserId()), + userUtils.getUserById(message.getActiveUser().getUserId()), R.xml.chip_outgoing_own_mention); } } else if (individualHashMap.get("type").equals("file")) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index 105a8c3a4..4c22b1494 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -21,23 +21,39 @@ package com.nextcloud.talk.adapters.messages; import android.annotation.SuppressLint; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.net.Uri; +import android.os.Bundle; import android.view.View; import autodagger.AutoInjector; import butterknife.BindView; import butterknife.ButterKnife; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.components.filebrowser.models.BrowserFile; +import com.nextcloud.talk.components.filebrowser.models.DavResponse; +import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; +import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; +import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.DisplayUtils; +import com.nextcloud.talk.utils.DrawableUtils; +import com.nextcloud.talk.utils.bundle.BundleKeys; import com.stfalcon.chatkit.messages.MessageHolders; import com.vanniktech.emoji.EmojiTextView; +import io.reactivex.Single; +import io.reactivex.SingleObserver; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; import javax.inject.Inject; +import java.util.List; +import java.util.concurrent.Callable; @AutoInjector(NextcloudTalkApplication.class) public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageMessageViewHolder { @@ -48,6 +64,9 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM @Inject Context context; + @Inject + OkHttpClient okHttpClient; + public MagicPreviewMessageViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); @@ -58,7 +77,6 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM @Override public void onBind(ChatMessage message) { super.onBind(message); - if (userAvatar != null) { if (message.isGrouped) { userAvatar.setVisibility(View.INVISIBLE); @@ -80,10 +98,32 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM // it's a preview for a Nextcloud share messageText.setText(message.getSelectedIndividualHashMap().get("name")); DisplayUtils.setClickableString(message.getSelectedIndividualHashMap().get("name"), message.getSelectedIndividualHashMap().get("link"), messageText); + if (message.getSelectedIndividualHashMap().containsKey("mimetype")) { + image.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.getDrawableResourceIdForMimeType(message.getSelectedIndividualHashMap().get("mimetype")))); + } else { + fetchFileInformation("/" + message.getSelectedIndividualHashMap().get("path"), message.getActiveUser()); + } + image.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getSelectedIndividualHashMap().get("link"))); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - NextcloudTalkApplication.getSharedApplication().getApplicationContext().startActivity(browserIntent); + + String accountString = + message.getActiveUser().getUsername() + "@" + message.getActiveUser().getBaseUrl().replace("https://", "").replace("http://", ""); + + if (AccountUtils.canWeOpenFilesApp(context, accountString)) { + Intent filesAppIntent = new Intent(Intent.ACTION_VIEW, null); + final ComponentName componentName = new ComponentName(context.getString(R.string.nc_import_accounts_from), "com.owncloud.android.ui.activity.FileDisplayActivity"); + filesAppIntent.setComponent(componentName); + filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)); + Bundle options = new Bundle(); + options.putString(BundleKeys.KEY_ACCOUNT, accountString); + options.putString(BundleKeys.KEY_FILE_PATH, "/" + message.getSelectedIndividualHashMap().get("path")); + context.startActivity(filesAppIntent, options); + } else { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(message.getSelectedIndividualHashMap().get("link"))); + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(browserIntent); + } }); } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { messageText.setText("GIPHY"); @@ -95,4 +135,36 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM messageText.setText(""); } } + + private void fetchFileInformation(String url, UserEntity activeUser) { + Single.fromCallable(new Callable() { + @Override + public ReadFilesystemOperation call() { + return new ReadFilesystemOperation(okHttpClient, activeUser, url, 0); + } + }).subscribeOn(Schedulers.newThread()) + .subscribe(new SingleObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { + DavResponse davResponse = readFilesystemOperation.readRemotePath(); + if (davResponse.getData() != null) { + List browserFileList = (List) davResponse.getData(); + if (!browserFileList.isEmpty()) { + image.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.getDrawableResourceIdForMimeType(browserFileList.get(0).getMimeType()))); + } + + } + } + + @Override + public void onError(Throwable e) { + } + }); + + } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java index ec6f34260..ba4db9aed 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicSystemMessageViewHolder.java @@ -61,7 +61,7 @@ public class MagicSystemMessageViewHolder extends MessageHolders.IncomingTextMes int color; if (individualHashMap != null && (individualHashMap.get("type").equals("user") || individualHashMap.get("type").equals("guest") || individualHashMap.get("type").equals("call"))) { - if (individualHashMap.get("id").equals(message.getActiveUserId())) { + if (individualHashMap.get("id").equals(message.getActiveUser().getUserId())) { color = context.getResources().getColor(R.color.nc_incoming_text_mention_you); } else { color = context.getResources().getColor(R.color.nc_incoming_text_mention_others); diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index c4707072a..6a7c6d375 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -306,4 +306,11 @@ public interface NcApi { @PUT Observable setReadOnlyState(@Header("Authorization") String authorization, @Url String url, @Field("state") int state); + + @FormUrlEncoded + @POST + Observable createRemoteShare(@Nullable @Header("Authorization") String authorization, @Url String url, + @Field("path") String remotePath, + @Field("shareWith") String roomToken, + @Field("shareType") String shareType); } diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java index 82bf39023..bd2b15e33 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java @@ -35,6 +35,7 @@ import autodagger.AutoInjector; import com.facebook.cache.disk.DiskCacheConfig; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.imagepipeline.core.ImagePipelineConfig; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; import com.nextcloud.talk.dagger.modules.BusModule; import com.nextcloud.talk.dagger.modules.ContextModule; import com.nextcloud.talk.dagger.modules.DatabaseModule; @@ -53,12 +54,14 @@ import com.nextcloud.talk.webrtc.MagicWebRTCUtils; import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.twitter.TwitterEmojiProvider; import okhttp3.OkHttpClient; +import org.conscrypt.Conscrypt; import org.webrtc.PeerConnectionFactory; import org.webrtc.voiceengine.WebRtcAudioManager; import org.webrtc.voiceengine.WebRtcAudioUtils; import javax.inject.Inject; import javax.inject.Singleton; +import java.security.Security; import java.util.concurrent.TimeUnit; @AutoComponent( @@ -121,6 +124,7 @@ public class NextcloudTalkApplication extends MultiDexApplication implements Lif initializeWebRtc(); DisplayUtils.useCompatVectorIfNeeded(); buildComponent(); + DavUtils.registerCustomFactories(); componentApplication.inject(this); @@ -134,6 +138,7 @@ public class NextcloudTalkApplication extends MultiDexApplication implements Lif .build(); Fresco.initialize(this, imagePipelineConfig); + Security.insertProviderAt(Conscrypt.newProvider(), 1); new ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync(); DeviceUtils.ignoreSpecialBatteryFeatures(); diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java new file mode 100644 index 000000000..38b7aa862 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/adapters/items/BrowserFileItem.java @@ -0,0 +1,186 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.adapters.items; + +import android.content.Context; +import android.text.format.Formatter; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; +import autodagger.AutoInjector; +import butterknife.BindView; +import butterknife.ButterKnife; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; +import com.nextcloud.talk.R; +import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.components.filebrowser.models.BrowserFile; +import com.nextcloud.talk.interfaces.SelectionInterface; +import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.utils.ApiUtils; +import com.nextcloud.talk.utils.DateUtils; +import com.nextcloud.talk.utils.DisplayUtils; +import com.nextcloud.talk.utils.DrawableUtils; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; +import eu.davidea.flexibleadapter.items.IFilterable; +import eu.davidea.flexibleadapter.items.IFlexible; +import eu.davidea.viewholders.FlexibleViewHolder; + +import javax.inject.Inject; +import java.util.List; + +@AutoInjector(NextcloudTalkApplication.class) +public class BrowserFileItem extends AbstractFlexibleItem implements IFilterable { + @Inject + Context context; + private BrowserFile browserFile; + private UserEntity activeUser; + private SelectionInterface selectionInterface; + private boolean selected; + + public BrowserFileItem(BrowserFile browserFile, UserEntity activeUser, SelectionInterface selectionInterface) { + this.browserFile = browserFile; + this.activeUser = activeUser; + this.selectionInterface = selectionInterface; + NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); + } + + @Override + public boolean equals(Object o) { + if (o instanceof BrowserFileItem) { + BrowserFileItem inItem = (BrowserFileItem) o; + return browserFile.getPath().equals(inItem.getModel().getPath()); + } + + return false; + } + + public BrowserFile getModel() { + return browserFile; + } + + @Override + public int getLayoutRes() { + return R.layout.rv_item_browser_file; + } + + @Override + public ViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new ViewHolder(view, adapter); + + } + + private boolean isSelected() { + return selected; + } + + private void setSelected(boolean selected) { + this.selected = selected; + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) { + holder.fileIconImageView.setController(null); + + if (browserFile.isEncrypted()) { + holder.fileEncryptedImageView.setVisibility(View.VISIBLE); + holder.itemView.setEnabled(false); + holder.itemView.setAlpha(0.38f); + } else { + holder.fileEncryptedImageView.setVisibility(View.GONE); + holder.itemView.setEnabled(true); + holder.itemView.setAlpha(1.0f); + } + + if (browserFile.isFavorite()) { + holder.fileFavoriteImageView.setVisibility(View.VISIBLE); + } else { + holder.fileFavoriteImageView.setVisibility(View.GONE); + } + + holder.fileIconImageView.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.getDrawableResourceIdForMimeType(browserFile.getMimeType()))); + + if (browserFile.isHasPreview()) { + String path = ApiUtils.getUrlForFilePreviewWithRemotePath(activeUser.getBaseUrl(), + browserFile.getPath(), + context.getResources().getDimensionPixelSize(R.dimen.small_item_height)); + + if (path.length() > 0) { + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(path, null)) + .build(); + holder.fileIconImageView.setController(draweeController); + } + } + + holder.filenameTextView.setText(browserFile.getDisplayName()); + holder.fileModifiedTextView.setText(String.format(context.getString(R.string.nc_last_modified), + Formatter.formatShortFileSize(context, browserFile.getSize()), + DateUtils.getLocalDateTimeStringFromTimestamp(context, browserFile.getModifiedTimestamp()))); + setSelected(selectionInterface.isPathSelected(browserFile.getPath())); + holder.selectFileCheckbox.setChecked(isSelected()); + + if (!browserFile.isEncrypted()) { + holder.selectFileCheckbox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (((CheckBox) v).isChecked() != isSelected()) { + setSelected(((CheckBox) v).isChecked()); + selectionInterface.toggleBrowserItemSelection(browserFile.getPath()); + } + } + }); + } + + holder.filenameTextView.setSelected(true); + holder.fileModifiedTextView.setSelected(true); + } + + @Override + public boolean filter(String constraint) { + return false; + } + + static class ViewHolder extends FlexibleViewHolder { + + @BindView(R.id.file_icon) + public SimpleDraweeView fileIconImageView; + @BindView(R.id.file_modified_info) + public TextView fileModifiedTextView; + @BindView(R.id.filename_text_view) + public TextView filenameTextView; + @BindView(R.id.select_file_checkbox) + public CheckBox selectFileCheckbox; + @BindView(R.id.fileEncryptedImageView) + public ImageView fileEncryptedImageView; + @BindView(R.id.fileFavoriteImageView) + public ImageView fileFavoriteImageView; + + ViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java new file mode 100644 index 000000000..ed07d4c21 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/controllers/BrowserController.java @@ -0,0 +1,333 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.controllers; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.view.*; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; +import autodagger.AutoInjector; +import butterknife.BindView; +import butterknife.OnClick; +import com.google.android.material.bottomnavigation.BottomNavigationItemView; +import com.nextcloud.talk.R; +import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.components.filebrowser.adapters.items.BrowserFileItem; +import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface; +import com.nextcloud.talk.components.filebrowser.models.BrowserFile; +import com.nextcloud.talk.components.filebrowser.models.DavResponse; +import com.nextcloud.talk.components.filebrowser.operations.DavListing; +import com.nextcloud.talk.components.filebrowser.operations.ListingAbstractClass; +import com.nextcloud.talk.controllers.base.BaseController; +import com.nextcloud.talk.interfaces.SelectionInterface; +import com.nextcloud.talk.jobs.ShareOperationWorker; +import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.utils.bundle.BundleKeys; +import com.nextcloud.talk.utils.database.user.UserUtils; +import eu.davidea.fastscroller.FastScroller; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; +import eu.davidea.flexibleadapter.items.IFlexible; +import okhttp3.OkHttpClient; +import org.parceler.Parcel; +import org.parceler.Parcels; + +import javax.inject.Inject; +import java.io.File; +import java.util.*; + +@AutoInjector(NextcloudTalkApplication.class) +public class BrowserController extends BaseController implements ListingInterface, + FlexibleAdapter.OnItemClickListener, SelectionInterface { + private final Set selectedPaths; + @Inject + UserUtils userUtils; + @BindView(R.id.recycler_view) + RecyclerView recyclerView; + @BindView(R.id.fast_scroller) + FastScroller fastScroller; + @BindView(R.id.action_back) + BottomNavigationItemView backMenuItem; + @BindView(R.id.action_refresh) + BottomNavigationItemView actionRefreshMenuItem; + @Inject + Context context; + @Inject + OkHttpClient okHttpClient; + + private MenuItem filesSelectionDoneMenuItem; + private RecyclerView.LayoutManager layoutManager; + + private FlexibleAdapter adapter; + private List recyclerViewItems = new ArrayList<>(); + + private ListingAbstractClass listingAbstractClass; + private BrowserType browserType; + private String currentPath; + private UserEntity activeUser; + private String roomToken; + + public BrowserController(Bundle args) { + super(args); + setHasOptionsMenu(true); + NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); + browserType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_BROWSER_TYPE)); + activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_USER_ENTITY)); + roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN); + + currentPath = "/"; + if (BrowserType.DAV_BROWSER.equals(browserType)) { + listingAbstractClass = new DavListing(this); + } else { + //listingAbstractClass = new LocalListing(this); + } + + selectedPaths = Collections.synchronizedSet(new TreeSet<>()); + } + + @Override + protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { + return inflater.inflate(R.layout.controller_browser, container, false); + } + + @Override + protected void onViewBound(@NonNull View view) { + super.onViewBound(view); + if (adapter == null) { + adapter = new FlexibleAdapter<>(recyclerViewItems, context, false); + } + + changeEnabledStatusForBarItems(true); + prepareViews(); + } + + private void onFileSelectionDone() { + synchronized (selectedPaths) { + Iterator iterator = selectedPaths.iterator(); + + List paths = new ArrayList<>(); + Data data; + OneTimeWorkRequest shareWorker; + + while (iterator.hasNext()) { + String path = iterator.next(); + paths.add(path); + iterator.remove(); + if (paths.size() == 10 || !iterator.hasNext()) { + data = new Data.Builder() + .putLong(BundleKeys.KEY_INTERNAL_USER_ID, activeUser.getId()) + .putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + .putStringArray(BundleKeys.KEY_FILE_PATHS, paths.toArray(new String[0])) + .build(); + shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class) + .setInputData(data) + .build(); + WorkManager.getInstance().enqueue(shareWorker); + paths = new ArrayList<>(); + } + } + } + + getRouter().popCurrentController(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_share_files, menu); + filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done); + filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case R.id.files_selection_done: + onFileSelectionDone(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + refreshCurrentPath(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + listingAbstractClass.tearDown(); + } + + @Override + protected String getTitle() { + return currentPath; + } + + @OnClick(R.id.action_back) + void goBack() { + fetchPath(new File(currentPath).getParent()); + } + + @OnClick(R.id.action_refresh) + void refreshCurrentPath() { + fetchPath(currentPath); + } + + @SuppressLint("RestrictedApi") + private void changeEnabledStatusForBarItems(boolean shouldBeEnabled) { + if (actionRefreshMenuItem != null) { + actionRefreshMenuItem.setEnabled(shouldBeEnabled); + } + + if (backMenuItem != null) { + backMenuItem.setEnabled(shouldBeEnabled && !currentPath.equals("/")); + } + } + + private void fetchPath(String path) { + listingAbstractClass.cancelAllJobs(); + changeEnabledStatusForBarItems(false); + + listingAbstractClass.getFiles(path, activeUser, BrowserType.DAV_BROWSER.equals(browserType) ? okHttpClient : null); + } + + @Override + public void listingResult(DavResponse davResponse) { + adapter.clear(); + List fileBrowserItems = new ArrayList<>(); + if (davResponse.getData() != null) { + final List objectList = (List) davResponse.getData(); + + currentPath = objectList.get(0).getPath(); + + Objects.requireNonNull(getActivity()).runOnUiThread(this::setTitle); + + for (int i = 1; i < objectList.size(); i++) { + fileBrowserItems.add(new BrowserFileItem(objectList.get(i), activeUser, this)); + } + } + + adapter.addItems(0, fileBrowserItems); + Objects.requireNonNull(getActivity()).runOnUiThread(() -> { + adapter.notifyDataSetChanged(); + changeEnabledStatusForBarItems(true); + }); + } + + private boolean shouldPathBeSelectedDueToParent(String currentPath) { + if (selectedPaths.size() > 0) { + File file = new File(currentPath); + if (!file.getParent().equals("/")) { + while (file.getParent() != null) { + String parent = file.getParent(); + if (new File(file.getParent()).getParent() != null) { + parent += "/"; + } + + if (selectedPaths.contains(parent)) { + return true; + } + + file = new File(file.getParent()); + } + } + } + + return false; + } + + private void checkAndRemoveAnySelectedParents(String currentPath) { + File file = new File(currentPath); + selectedPaths.remove(currentPath); + while (file.getParent() != null) { + selectedPaths.remove(file.getParent() + "/"); + file = new File(file.getParent()); + } + + adapter.notifyDataSetChanged(); + } + + @Override + public boolean onItemClick(View view, int position) { + BrowserFile browserFile = ((BrowserFileItem) adapter.getItem(position)).getModel(); + if ("inode/directory".equals((browserFile.getMimeType()))) { + fetchPath(browserFile.getPath()); + return true; + } + + return false; + } + + private void prepareViews() { + if (getActivity() != null) { + layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); + + adapter.setFastScroller(fastScroller); + adapter.addListener(this); + + fastScroller.setBubbleTextCreator(position -> { + IFlexible abstractFlexibleItem = adapter.getItem(position); + if (abstractFlexibleItem instanceof BrowserFileItem) { + return String.valueOf(((BrowserFileItem) adapter.getItem(position)).getModel().getDisplayName().charAt(0)); + } else { + return ""; + } + }); + } + } + + @SuppressLint("RestrictedApi") + @Override + public void toggleBrowserItemSelection(String path) { + if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) { + checkAndRemoveAnySelectedParents(path); + } else { + // TOOD: if it's a folder, remove all the children we added manually + selectedPaths.add(path); + } + + filesSelectionDoneMenuItem.setVisible(selectedPaths.size() > 0); + } + + @Override + public boolean isPathSelected(String path) { + return (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)); + } + + @Parcel + public enum BrowserType { + FILE_BROWSER, + DAV_BROWSER, + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/MagicFlipView.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java similarity index 57% rename from app/src/main/java/com/nextcloud/talk/utils/MagicFlipView.java rename to app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java index b86637bce..c953d8a59 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/MagicFlipView.java +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/interfaces/ListingInterface.java @@ -18,27 +18,10 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.utils; +package com.nextcloud.talk.components.filebrowser.interfaces; -import android.content.Context; -import android.util.AttributeSet; -import eu.davidea.flipview.FlipView; +import com.nextcloud.talk.components.filebrowser.models.DavResponse; -public class MagicFlipView extends FlipView { - public MagicFlipView(Context context) { - super(context); - } - - public MagicFlipView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onDetachedFromWindow() { - try { - super.onDetachedFromWindow(); - } catch (IllegalArgumentException e) { - stopFlipping(); - } - } +public interface ListingInterface { + void listingResult(DavResponse davResponse); } diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java new file mode 100644 index 000000000..5fd0e11c8 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.java @@ -0,0 +1,106 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models; + +import android.net.Uri; +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.Response; +import at.bitfire.dav4android.property.DisplayName; +import at.bitfire.dav4android.property.GetContentType; +import at.bitfire.dav4android.property.GetLastModified; +import at.bitfire.dav4android.property.ResourceType; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.components.filebrowser.models.properties.*; +import lombok.Data; +import org.parceler.Parcel; + +import java.io.File; +import java.util.List; + +@Data +@JsonObject +@Parcel +public class BrowserFile { + public String path; + public String displayName; + public String mimeType; + public long modifiedTimestamp; + public long size; + public boolean isFile; + // Used for remote files + public String remoteId; + public boolean hasPreview; + public boolean favorite; + public boolean encrypted; + + public static BrowserFile getModelFromResponse(Response response, String remotePath) { + BrowserFile browserFile = new BrowserFile(); + browserFile.setPath(Uri.decode(remotePath)); + browserFile.setDisplayName(Uri.decode(new File(remotePath).getName())); + final List properties = response.getProperties(); + + for (Property property : properties) { + if (property instanceof OCId) { + browserFile.setRemoteId(((OCId) property).getOcId()); + } + + if (property instanceof ResourceType) { + browserFile.isFile = + !(((ResourceType) property).getTypes().contains(ResourceType.Companion.getCOLLECTION())); + } + + if (property instanceof GetLastModified) { + browserFile.setModifiedTimestamp(((GetLastModified) property).getLastModified()); + } + + if (property instanceof GetContentType) { + browserFile.setMimeType(((GetContentType) property).getType()); + } + + if (property instanceof OCSize) { + browserFile.setSize(((OCSize) property).getOcSize()); + } + + if (property instanceof NCPreview) { + browserFile.setHasPreview(((NCPreview) property).isNcPreview()); + } + + if (property instanceof OCFavorite) { + browserFile.setFavorite(((OCFavorite) property).isOcFavorite()); + } + + if (property instanceof DisplayName) { + browserFile.setDisplayName(((DisplayName) property).getDisplayName()); + } + + if (property instanceof NCEncrypted) { + browserFile.setEncrypted(((NCEncrypted) property).isNcEncrypted()); + } + } + + if (TextUtils.isEmpty(browserFile.getMimeType()) && !browserFile.isFile()) { + browserFile.setMimeType("inode/directory"); + } + + return browserFile; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java new file mode 100644 index 000000000..5e3e53ea3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java @@ -0,0 +1,30 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models; + +import at.bitfire.dav4android.Response; +import lombok.Data; + +@Data +public class DavResponse { + Response response; + Object data; +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java new file mode 100644 index 000000000..ee69353fd --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCEncrypted.java @@ -0,0 +1,73 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models.properties; + +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.XmlUtils; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class NCEncrypted implements Property { + public static final Name NAME = new Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_IS_ENCRYPTED); + + @Getter + @Setter + private boolean ncEncrypted; + + private NCEncrypted(boolean isEncrypted) { + ncEncrypted = isEncrypted; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new NCEncrypted(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new NCEncrypted(false); + } + + @NotNull + @Override + public Name getName() { + return NAME; + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java new file mode 100644 index 000000000..2d387d4b7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/NCPreview.java @@ -0,0 +1,73 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models.properties; + +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.XmlUtils; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class NCPreview implements Property { + public static final Property.Name NAME = new Property.Name(DavUtils.NC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_HAS_PREVIEW); + + @Getter + @Setter + private boolean ncPreview; + + private NCPreview(boolean hasPreview) { + ncPreview = hasPreview; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new NCPreview(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCFavorite(false); + } + + @NotNull + @Override + public Property.Name getName() { + return NAME; + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java new file mode 100644 index 000000000..d1d34fa28 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCFavorite.java @@ -0,0 +1,73 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models.properties; + +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.XmlUtils; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class OCFavorite implements Property { + public static final Property.Name NAME = new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_FAVORITE); + + @Getter + @Setter + private boolean ocFavorite; + + OCFavorite(boolean isFavorite) { + ocFavorite = isFavorite; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCFavorite(Boolean.parseBoolean(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCFavorite(false); + } + + @NotNull + @Override + public Property.Name getName() { + return NAME; + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java new file mode 100644 index 000000000..ca5b69b37 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCId.java @@ -0,0 +1,73 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models.properties; + +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.XmlUtils; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class OCId implements Property { + public static final Name NAME = new Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_REMOTE_ID); + + @Getter + @Setter + private String ocId; + + private OCId(String id) { + ocId = id; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCId(text); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCId(""); + } + + @NotNull + @Override + public Name getName() { + return NAME; + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java new file mode 100644 index 000000000..26b5e4dd4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/properties/OCSize.java @@ -0,0 +1,73 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.models.properties; + +import android.text.TextUtils; +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.XmlUtils; +import com.nextcloud.talk.components.filebrowser.webdav.DavUtils; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class OCSize implements Property { + public static final Property.Name NAME = new Property.Name(DavUtils.OC_NAMESPACE, DavUtils.EXTENDED_PROPERTY_NAME_SIZE); + + @Getter + @Setter + private long ocSize; + + private OCSize(long size) { + ocSize = size; + } + + public static class Factory implements PropertyFactory { + + @Nullable + @Override + public Property create(@NotNull XmlPullParser xmlPullParser) { + try { + String text = XmlUtils.INSTANCE.readText(xmlPullParser); + if (!TextUtils.isEmpty(text)) { + return new OCSize(Long.parseLong(text)); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return new OCSize(-1); + } + + @NotNull + @Override + public Name getName() { + return NAME; + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java new file mode 100644 index 000000000..e5ea94e91 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/DavListing.java @@ -0,0 +1,69 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.operations; + +import androidx.annotation.Nullable; +import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface; +import com.nextcloud.talk.components.filebrowser.models.DavResponse; +import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; +import com.nextcloud.talk.models.database.UserEntity; +import io.reactivex.Single; +import io.reactivex.SingleObserver; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; + +import java.util.concurrent.Callable; + +public class DavListing extends ListingAbstractClass { + private DavResponse davResponse = new DavResponse(); + + public DavListing(ListingInterface listingInterface) { + super(listingInterface); + } + + @Override + public void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient) { + Single.fromCallable(new Callable() { + @Override + public ReadFilesystemOperation call() { + return new ReadFilesystemOperation(okHttpClient, currentUser, path, 1); + } + }).subscribeOn(Schedulers.newThread()) + .subscribe(new SingleObserver() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onSuccess(ReadFilesystemOperation readFilesystemOperation) { + davResponse = readFilesystemOperation.readRemotePath(); + listingInterface.listingResult(davResponse); + } + + @Override + public void onError(Throwable e) { + listingInterface.listingResult(davResponse); + } + }); + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java new file mode 100644 index 000000000..b01bc31cc --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/operations/ListingAbstractClass.java @@ -0,0 +1,48 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.operations; + +import android.os.Handler; +import androidx.annotation.Nullable; +import com.nextcloud.talk.components.filebrowser.interfaces.ListingInterface; +import com.nextcloud.talk.models.database.UserEntity; +import okhttp3.OkHttpClient; + +public abstract class ListingAbstractClass { + Handler handler; + ListingInterface listingInterface; + + ListingAbstractClass(ListingInterface listingInterface) { + handler = new Handler(); + this.listingInterface = listingInterface; + } + + public abstract void getFiles(String path, UserEntity currentUser, @Nullable OkHttpClient okHttpClient); + + public void cancelAllJobs() { + handler.removeCallbacksAndMessages(null); + } + + public void tearDown() { + cancelAllJobs(); + handler = null; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java new file mode 100644 index 000000000..579f726be --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java @@ -0,0 +1,110 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.webdav; + +import at.bitfire.dav4android.Property; +import at.bitfire.dav4android.PropertyFactory; +import at.bitfire.dav4android.PropertyRegistry; +import at.bitfire.dav4android.property.*; +import com.nextcloud.talk.components.filebrowser.models.properties.*; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DavUtils { + public static final String OC_NAMESPACE = "http://owncloud.org/ns"; + public static final String NC_NAMESPACE = "http://nextcloud.org/ns"; + public static final String DAV_PATH = "/remote.php/dav/files/"; + + public static final String EXTENDED_PROPERTY_NAME_PERMISSIONS = "permissions"; + public static final String EXTENDED_PROPERTY_NAME_REMOTE_ID = "id"; + public static final String EXTENDED_PROPERTY_NAME_SIZE = "size"; + public static final String EXTENDED_PROPERTY_FAVORITE = "favorite"; + public static final String EXTENDED_PROPERTY_IS_ENCRYPTED = "is-encrypted"; + public static final String EXTENDED_PROPERTY_MOUNT_TYPE = "mount-type"; + public static final String EXTENDED_PROPERTY_OWNER_ID = "owner-id"; + public static final String EXTENDED_PROPERTY_OWNER_DISPLAY_NAME = "owner-display-name"; + public static final String EXTENDED_PROPERTY_UNREAD_COMMENTS = "comments-unread"; + public static final String EXTENDED_PROPERTY_HAS_PREVIEW = "has-preview"; + public static final String EXTENDED_PROPERTY_NOTE = "note"; + public static final String TRASHBIN_FILENAME = "trashbin-filename"; + public static final String TRASHBIN_ORIGINAL_LOCATION = "trashbin-original-location"; + public static final String TRASHBIN_DELETION_TIME = "trashbin-deletion-time"; + + public static final String PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes"; + public static final String PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes"; + + static Property.Name[] getAllPropSet() { + List propSet = new ArrayList<>(); + + propSet.add(DisplayName.NAME); + propSet.add(GetContentType.NAME); + propSet.add(GetContentLength.NAME); + propSet.add(GetContentType.NAME); + propSet.add(GetContentLength.NAME); + propSet.add(GetLastModified.NAME); + propSet.add(CreationDate.NAME); + propSet.add(GetETag.NAME); + propSet.add(ResourceType.NAME); + + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_NAME_PERMISSIONS)); + propSet.add(OCId.NAME); + propSet.add(OCSize.NAME); + propSet.add(OCFavorite.NAME); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_ID)); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_OWNER_DISPLAY_NAME)); + propSet.add(new Property.Name(OC_NAMESPACE, EXTENDED_PROPERTY_UNREAD_COMMENTS)); + + propSet.add(NCEncrypted.NAME); + propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_MOUNT_TYPE)); + propSet.add(NCPreview.NAME); + propSet.add(new Property.Name(NC_NAMESPACE, EXTENDED_PROPERTY_NOTE)); + + return propSet.toArray(new Property.Name[0]); + } + + public static void registerCustomFactories() { + PropertyRegistry propertyRegistry = PropertyRegistry.INSTANCE; + try { + Field factories = propertyRegistry.getClass().getDeclaredField("factories"); + factories.setAccessible(true); + Map reflectionMap = (HashMap) factories.get(propertyRegistry); + + reflectionMap.put(OCId.NAME, new OCId.Factory()); + reflectionMap.put(NCPreview.NAME, new NCPreview.Factory()); + reflectionMap.put(NCEncrypted.NAME, new NCEncrypted.Factory()); + reflectionMap.put(OCFavorite.NAME, new OCFavorite.Factory()); + reflectionMap.put(OCSize.NAME, new OCSize.Factory()); + + factories.set(propertyRegistry, reflectionMap); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java new file mode 100644 index 000000000..fa30cdac7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java @@ -0,0 +1,100 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.components.filebrowser.webdav; + +import at.bitfire.dav4android.DavResource; +import at.bitfire.dav4android.Response; +import at.bitfire.dav4android.exception.DavException; +import com.nextcloud.talk.components.filebrowser.models.BrowserFile; +import com.nextcloud.talk.components.filebrowser.models.DavResponse; +import com.nextcloud.talk.dagger.modules.RestModule; +import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.utils.ApiUtils; +import kotlin.Unit; +import kotlin.jvm.functions.Function2; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ReadFilesystemOperation { + private final OkHttpClient okHttpClient; + private final String url; + private final String basePath; + private final int depth; + + public ReadFilesystemOperation(OkHttpClient okHttpClient, UserEntity currentUser, String path, int depth) { + OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder(); + okHttpClientBuilder.followRedirects(false); + okHttpClientBuilder.followSslRedirects(false); + okHttpClientBuilder.authenticator(new RestModule.MagicAuthenticator(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), "Authorization")); + this.okHttpClient = okHttpClientBuilder.build(); + basePath = currentUser.getBaseUrl() + DavUtils.DAV_PATH + currentUser.getUserId(); + this.url = basePath + path; + this.depth = depth; + } + + public DavResponse readRemotePath() { + DavResponse davResponse = new DavResponse(); + final List memberElements = new ArrayList<>(); + final Response[] rootElement = new Response[1]; + final List remoteFiles = new ArrayList<>(); + + try { + new DavResource(okHttpClient, HttpUrl.parse(url)).propfind(depth, DavUtils.getAllPropSet(), + new Function2() { + @Override + public Unit invoke(Response response, Response.HrefRelation hrefRelation) { + davResponse.setResponse(response); + switch (hrefRelation) { + case MEMBER: + memberElements.add(response); + break; + case SELF: + rootElement[0] = response; + break; + case OTHER: + default: + } + return Unit.INSTANCE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + } catch (DavException e) { + e.printStackTrace(); + } + + + remoteFiles.add(BrowserFile.getModelFromResponse(rootElement[0], + rootElement[0].getHref().toString().substring(basePath.length()))); + for (Response memberElement : memberElements) { + remoteFiles.add(BrowserFile.getModelFromResponse(memberElement, + memberElement.getHref().toString().substring(basePath.length()))); + } + + davResponse.setData(remoteFiles); + return davResponse; + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java index 59c96cffc..9661692d5 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -44,9 +44,9 @@ import butterknife.BindView; import butterknife.OnClick; import butterknife.OnLongClick; import com.bluelinelabs.logansquare.LoganSquare; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.request.RequestOptions; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -65,18 +65,16 @@ import com.nextcloud.talk.models.json.signaling.*; import com.nextcloud.talk.models.json.signaling.settings.IceServer; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.utils.ApiUtils; -import com.nextcloud.talk.utils.MagicFlipView; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.NotificationUtils; import com.nextcloud.talk.utils.animations.PulseAnimation; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.power.PowerManagerUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder; import com.nextcloud.talk.webrtc.*; import com.wooplr.spotlight.SpotlightView; -import eu.davidea.flipview.FlipView; import io.reactivex.Observable; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -117,7 +115,7 @@ public class CallController extends BaseController { }; @BindView(R.id.callControlEnableSpeaker) - MagicFlipView callControlEnableSpeaker; + SimpleDraweeView callControlEnableSpeaker; @BindView(R.id.pip_video_view) SurfaceViewRenderer pipVideoView; @@ -129,11 +127,11 @@ public class CallController extends BaseController { @BindView(R.id.callControlsRelativeLayout) RelativeLayout callControls; @BindView(R.id.call_control_microphone) - FlipView microphoneControlButton; + SimpleDraweeView microphoneControlButton; @BindView(R.id.call_control_camera) - FlipView cameraControlButton; + SimpleDraweeView cameraControlButton; @BindView(R.id.call_control_switch_camera) - FlipView cameraSwitchButton; + SimpleDraweeView cameraSwitchButton; @BindView(R.id.connectingTextView) TextView connectingTextView; @@ -260,7 +258,7 @@ public class CallController extends BaseController { microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener()); videoOnClickListener = new VideoClickListener(); - pulseAnimation = PulseAnimation.create().with(microphoneControlButton.getFrontImageView()) + pulseAnimation = PulseAnimation.create().with(microphoneControlButton) .setDuration(310) .setRepeatCount(PulseAnimation.INFINITE) .setRepeatMode(PulseAnimation.REVERSE); @@ -454,7 +452,7 @@ public class CallController extends BaseController { onCameraClick(); } } else { - cameraControlButton.getFrontImageView().setImageResource(R.drawable.ic_videocam_off_white_24px); + cameraControlButton.setImageResource(R.drawable.ic_videocam_off_white_24px); cameraControlButton.setAlpha(0.7f); cameraSwitchButton.setVisibility(View.GONE); } @@ -465,7 +463,7 @@ public class CallController extends BaseController { onMicrophoneClick(); } } else { - microphoneControlButton.getFrontImageView().setImageResource(R.drawable.ic_mic_off_white_24px); + microphoneControlButton.setImageResource(R.drawable.ic_mic_off_white_24px); } if (!inCall) { @@ -584,7 +582,7 @@ public class CallController extends BaseController { public void onEnableSpeakerphoneClick() { if (audioManager != null) { audioManager.toggleUseSpeakerphone(); - callControlEnableSpeaker.flipSilently(!callControlEnableSpeaker.isFlipped()); + //callControlEnableSpeaker.flipSilently(!callControlEnableSpeaker.isFlipped()); } } @@ -620,14 +618,14 @@ public class CallController extends BaseController { audioOn = !audioOn; if (audioOn) { - microphoneControlButton.getFrontImageView().setImageResource(R.drawable.ic_mic_white_24px); + microphoneControlButton.setActualImageResource(R.drawable.ic_mic_white_24px); } else { - microphoneControlButton.getFrontImageView().setImageResource(R.drawable.ic_mic_off_white_24px); + microphoneControlButton.setActualImageResource(R.drawable.ic_mic_off_white_24px); } toggleMedia(audioOn, false); } else { - microphoneControlButton.getFrontImageView().setImageResource(R.drawable.ic_mic_white_24px); + microphoneControlButton.setActualImageResource(R.drawable.ic_mic_white_24px); pulseAnimation.start(); toggleMedia(true, false); } @@ -663,12 +661,12 @@ public class CallController extends BaseController { videoOn = !videoOn; if (videoOn) { - cameraControlButton.getFrontImageView().setImageResource(R.drawable.ic_videocam_white_24px); + cameraControlButton.setActualImageResource(R.drawable.ic_videocam_white_24px); if (cameraEnumerator.getDeviceNames().length > 1) { cameraSwitchButton.setVisibility(View.VISIBLE); } } else { - cameraControlButton.getFrontImageView().setImageResource(R.drawable.ic_videocam_off_white_24px); + cameraControlButton.setActualImageResource(R.drawable.ic_videocam_off_white_24px); cameraSwitchButton.setVisibility(View.GONE); } @@ -1792,21 +1790,19 @@ public class CallController extends BaseController { if (remoteRenderersLayout != null) { RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+video"); if (relativeLayout != null) { - ImageView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); + SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); if (participantMap.containsKey(session) && avatarImageView.getDrawable() == null) { - int size = Math.round(getResources().getDimension(R.dimen.avatar_size_big)); - if (getActivity() != null) { - GlideApp.with(getActivity()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(ApiUtils.getUrlForAvatarWithName(baseUrl, participantMap.get(session).getUserId(), R.dimen.avatar_size_big)) - .centerInside() - .override(size, size) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(avatarImageView); + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(baseUrl, + participantMap.get(session).getUserId(), + R.dimen.avatar_size_big), null)) + .build(); + avatarImageView.setController(draweeController); } } } @@ -1822,7 +1818,7 @@ public class CallController extends BaseController { RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+" + videoStreamType); SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - ImageView imageView = relativeLayout.findViewById(R.id.avatarImageView); + SimpleDraweeView imageView = relativeLayout.findViewById(R.id.avatarImageView); if (mediaStream != null && mediaStream.videoTracks != null && mediaStream.videoTracks.size() > 0 && enable) { VideoTrack videoTrack = mediaStream.videoTracks.get(0); @@ -1847,7 +1843,7 @@ public class CallController extends BaseController { RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); if (relativeLayout != null) { ImageView imageView; - ImageView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); + SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); if (video) { @@ -1942,7 +1938,7 @@ public class CallController extends BaseController { v.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP && isPTTActive) { isPTTActive = false; - microphoneControlButton.getFrontImageView().setImageResource(R.drawable.ic_mic_off_white_24px); + microphoneControlButton.setActualImageResource(R.drawable.ic_mic_off_white_24px); pulseAnimation.stop(); toggleMedia(false, false); animateCallControls(false, 5000); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java index 435eb41f1..dbabeeb27 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java @@ -30,10 +30,7 @@ import android.media.AudioAttributes; import android.media.MediaPlayer; import android.net.Uri; import android.os.*; -import android.renderscript.Allocation; -import android.renderscript.Element; import android.renderscript.RenderScript; -import android.renderscript.ScriptIntrinsicBlur; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -50,14 +47,16 @@ import butterknife.OnClick; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.load.resource.bitmap.TransformationUtils; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.transition.Transition; +import com.facebook.common.executors.UiThreadImmediateExecutorService; +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.postprocessors.BlurPostProcessor; +import com.facebook.imagepipeline.request.ImageRequest; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -70,10 +69,9 @@ import com.nextcloud.talk.models.json.participants.ParticipantsOverall; import com.nextcloud.talk.models.json.rooms.Conversation; import com.nextcloud.talk.models.json.rooms.RoomsOverall; import com.nextcloud.talk.utils.ApiUtils; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DoNotDisturbUtils; -import com.nextcloud.talk.utils.MagicFlipView; import com.nextcloud.talk.utils.bundle.BundleKeys; -import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder; import io.reactivex.Observer; @@ -87,6 +85,7 @@ import org.greenrobot.eventbus.ThreadMode; import org.michaelevans.colorart.library.ColorArt; import org.parceler.Parcels; +import javax.annotation.Nullable; import javax.inject.Inject; import java.io.IOException; import java.util.ArrayList; @@ -113,13 +112,13 @@ public class CallNotificationController extends BaseController { TextView conversationNameTextView; @BindView(R.id.avatarImageView) - ImageView avatarImageView; + SimpleDraweeView avatarImageView; @BindView(R.id.callAnswerVoiceOnlyView) - MagicFlipView callAnswerVoiceOnlyView; + SimpleDraweeView callAnswerVoiceOnlyView; @BindView(R.id.callAnswerCameraView) - MagicFlipView callAnswerCameraView; + SimpleDraweeView callAnswerCameraView; @BindView(R.id.backgroundImageView) ImageView backgroundImageView; @@ -148,7 +147,6 @@ public class CallNotificationController extends BaseController { this.userBeingCalled = args.getParcelable(BundleKeys.KEY_USER_ENTITY); this.originalBundle = args; - credentials = ApiUtils.getCredentials(userBeingCalled.getUsername(), userBeingCalled.getToken()); } @@ -384,7 +382,6 @@ public class CallNotificationController extends BaseController { layoutParams.width = dimen; layoutParams.height = dimen; - avatarImageView.setLayoutParams(layoutParams); } @@ -402,92 +399,59 @@ public class CallNotificationController extends BaseController { } private void loadAvatar() { - int avatarSize = Math.round(NextcloudTalkApplication - .getSharedApplication().getResources().getDimension(R.dimen.avatar_fetching_size_very_big)); - switch (currentConversation.getType()) { case ROOM_TYPE_ONE_TO_ONE_CALL: avatarImageView.setVisibility(View.VISIBLE); - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(), - currentConversation.getName(), R.dimen.avatar_size_very_big), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); + ImageRequest imageRequest = + DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(), + currentConversation.getName(), R.dimen.avatar_size_very_big), null); - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(glideUrl) - .centerInside() - .override(avatarSize, avatarSize) - .into(new SimpleTarget() { - @Override - public void onResourceReady(Bitmap resource, Transition transition) { - if (getActivity() != null && avatarImageView != null) { - avatarImageView.setImageBitmap(TransformationUtils.circleCrop(GlideApp.get - (getActivity()).getBitmapPool(), resource, avatarSize, avatarSize)); - } + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null); - if (getResources() != null && incomingTextRelativeLayout != null) { - incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable - .incoming_gradient)); - } + dataSource.subscribe(new BaseBitmapDataSubscriber() { + @Override + protected void onNewResultImpl(@Nullable Bitmap bitmap) { + avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100, + true); - if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 && - userBeingCalled.hasSpreedCapabilityWithName("no-ping")) { - final Allocation input = Allocation.createFromBitmap(renderScript, resource); - final Allocation output = Allocation.createTyped(renderScript, input.getType()); - final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(renderScript, Element - .U8_4(renderScript)); - script.setRadius(15f); - script.setInput(input); - script.forEach(output); - output.copyTo(resource); + if (getResources() != null) { + incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable + .incoming_gradient)); + } - if (backgroundImageView != null) { - backgroundImageView.setImageDrawable(new BitmapDrawable(resource)); - } - } else if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 201) { - ColorArt colorArt = new ColorArt(resource); - int color = colorArt.getBackgroundColor(); - - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] *= 0.75f; - color = Color.HSVToColor(hsv); - - if (backgroundImageView != null) { - backgroundImageView.setImageDrawable(new ColorDrawable(color)); - } - } + if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 && + userBeingCalled.hasSpreedCapabilityWithName("no-ping")) { + if (getActivity() != null) { + Bitmap backgroundBitmap = bitmap.copy(bitmap.getConfig(), true); + new BlurPostProcessor(5, getActivity()).process(backgroundBitmap); + backgroundImageView.setImageDrawable(new BitmapDrawable(backgroundBitmap)); } - }); + } else if (AvatarStatusCodeHolder.getInstance().getStatusCode() == 201) { + ColorArt colorArt = new ColorArt(bitmap); + int color = colorArt.getBackgroundColor(); + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[2] *= 0.75f; + color = Color.HSVToColor(hsv); + + backgroundImageView.setImageDrawable(new ColorDrawable(color)); + } + } + + @Override + protected void onFailureImpl(DataSource> dataSource) { + + } + }, UiThreadImmediateExecutorService.getInstance()); break; case ROOM_GROUP_CALL: - if (avatarImageView != null) { - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(R.drawable.ic_people_group_white_24px) - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(avatarImageView); - } + avatarImageView.setActualImageResource(R.drawable.ic_people_group_white_24px); case ROOM_PUBLIC_CALL: - if (avatarImageView != null) { - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .asBitmap() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .load(R.drawable.ic_link_white_24px) - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(avatarImageView); - } + avatarImageView.setActualImageResource(R.drawable.ic_link_white_24px); break; default: } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java index 1abd93023..d61b43d39 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.java @@ -46,6 +46,7 @@ import butterknife.BindView; import butterknife.OnClick; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; +import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; @@ -58,6 +59,7 @@ import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.callbacks.MentionAutocompleteCallback; +import com.nextcloud.talk.components.filebrowser.controllers.BrowserController; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.UserMentionClickEvent; import com.nextcloud.talk.models.RetrofitBucket; @@ -318,7 +320,7 @@ public class ChatController extends BaseController implements MessagesListAdapte @Override public void loadImage(SimpleDraweeView imageView, String url) { DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setImageRequest(DisplayUtils.getImageRequestForUrl(url)) + .setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser)) .setControllerListener(DisplayUtils.getImageControllerListener(imageView)) .setOldController(imageView.getController()) .setAutoPlayAnimations(true) @@ -415,6 +417,13 @@ public class ChatController extends BaseController implements MessagesListAdapte } }); + messageInputView.setAttachmentsListener(new MessageInput.AttachmentsListener() { + @Override + public void onAddAttachments() { + showBrowserScreen(BrowserController.BrowserType.DAV_BROWSER); + } + }); + messageInputView.getButton().setOnClickListener(v -> submitMessage()); messageInputView.getButton().setContentDescription(getResources() .getString(R.string.nc_description_send_message_button)); @@ -479,6 +488,16 @@ public class ChatController extends BaseController implements MessagesListAdapte } + private void showBrowserScreen(BrowserController.BrowserType browserType) { + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap(browserType)); + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(conversationUser)); + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken); + getRouter().pushController((RouterTransaction.with(new BrowserController(bundle)) + .pushChangeHandler(new VerticalChangeHandler()) + .popChangeHandler(new VerticalChangeHandler()))); + } + private void showConversationInfoScreen() { Bundle bundle = new Bundle(); bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser); @@ -486,7 +505,6 @@ public class ChatController extends BaseController implements MessagesListAdapte getRouter().pushController((RouterTransaction.with(new ConversationInfoController(bundle)) .pushChangeHandler(new HorizontalChangeHandler()) .popChangeHandler(new HorizontalChangeHandler()))); - } private void setupMentionAutocomplete() { @@ -532,7 +550,8 @@ public class ChatController extends BaseController implements MessagesListAdapte @Override public void onEmojiPopupDismiss() { if (smileyButton != null) { - smileyButton.clearColorFilter(); + smileyButton.setColorFilter(getResources().getColor(R.color.emoji_icons), + PorterDuff.Mode.SRC_IN); } } }).setOnEmojiClickListener(new OnEmojiClickListener() { @@ -950,8 +969,8 @@ public class ChatController extends BaseController implements MessagesListAdapte ChatMessage chatMessage = chatMessageList.get(i); chatMessage.setLinkPreviewAllowed(isLinkPreviewAllowed); - chatMessage.setBaseUrl(conversationUser.getBaseUrl()); - chatMessage.setActiveUserId(conversationUser.getUserId()); + chatMessage.setActiveUser(conversationUser); + if (globalLastKnownPastMessageId == -1 || chatMessageList.get(i).getJsonMessageId() < globalLastKnownPastMessageId) { globalLastKnownPastMessageId = chatMessageList.get(i).getJsonMessageId(); @@ -975,8 +994,7 @@ public class ChatController extends BaseController implements MessagesListAdapte for (int i = 0; i < chatMessageList.size(); i++) { chatMessage = chatMessageList.get(i); - chatMessage.setBaseUrl(conversationUser.getBaseUrl()); - chatMessage.setActiveUserId(conversationUser.getUserId()); + chatMessage.setActiveUser(conversationUser); chatMessage.setLinkPreviewAllowed(isLinkPreviewAllowed); // if credentials are empty, we're acting as a guest diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java index dcc7ac738..065220430 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.java @@ -80,7 +80,6 @@ import eu.davidea.flexibleadapter.SelectableAdapter; import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.flipview.FlipView; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -190,7 +189,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ if (isNewConversationView) { toggleNewCallHeaderVisibility(!isPublicCall); } - } @Override @@ -198,9 +196,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ super.onViewBound(view); NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); - FlipView.resetLayoutAnimationDelay(true, 1000L); - FlipView.stopLayoutAnimation(); - currentUser = userUtils.getCurrentUser(); if (currentUser != null) { @@ -208,7 +203,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ } if (adapter == null) { - adapter = new FlexibleAdapter<>(contactItems, getActivity(), false); + adapter = new FlexibleAdapter<>(contactItems, getActivity(), true); if (currentUser != null) { fetchData(true); @@ -598,6 +593,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ }); + adapter.clearSelection(); if (!shouldFilterManually) { adapter.updateDataSet(newUserItemList, false); } else { @@ -660,7 +656,7 @@ public class ContactsController extends BaseController implements SearchView.OnQ private void prepareViews() { layoutManager = new SmoothScrollLinearLayoutManager(getActivity()); - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getActivity())); + recyclerView.setLayoutManager(layoutManager); recyclerView.setHasFixedSize(true); recyclerView.setAdapter(adapter); @@ -911,7 +907,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ } }); } else { - ((UserItem) adapter.getItem(position)).flipItemSelection(); adapter.toggleSelection(position); if (currentUser.hasSpreedCapabilityWithName("last-room-activity") @@ -921,13 +916,14 @@ public class ContactsController extends BaseController implements SearchView.OnQ List selectedPositions = adapter.getSelectedPositions(); for (int i = 0; i < selectedPositions.size(); i++) { if (!selectedPositions.get(i).equals(position) && "groups".equals(((UserItem) adapter.getItem(selectedPositions.get(i))).getModel().getSource())) { - ((UserItem) adapter.getItem(selectedPositions.get(i))).flipItemSelection(); adapter.toggleSelection(selectedPositions.get(i)); } } } + adapter.notifyDataSetChanged(); + checkAndHandleDoneMenuItem(); } } @@ -955,7 +951,6 @@ public class ContactsController extends BaseController implements SearchView.OnQ if (adapter.getItem(selectedPosition) instanceof UserItem) { UserItem userItem = (UserItem) adapter.getItem(selectedPosition); if ("groups".equals(userItem.getModel().getSource())) { - ((UserItem) adapter.getItem(selectedPosition)).flipItemSelection(); adapter.toggleSelection(selectedPosition); } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.java index 567c9868c..cd50f5448 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.java @@ -473,7 +473,7 @@ public class ConversationInfoController extends BaseController { .setOldController(conversationAvatarImageView.getController()) .setAutoPlayAnimations(true) .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), - conversation.getName(), R.dimen.avatar_size_big))) + conversation.getName(), R.dimen.avatar_size_big), null)) .build(); conversationAvatarImageView.setController(draweeController); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 5a7290743..b5a396191 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -38,7 +38,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; import androidx.core.view.MenuItemCompat; -import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.work.Data; @@ -51,12 +50,15 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat; import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; import com.bluelinelabs.conductor.internal.NoOpControllerChangeHandler; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.transition.Transition; +import com.facebook.common.executors.UiThreadImmediateExecutorService; +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.postprocessors.RoundPostprocessor; +import com.facebook.imagepipeline.request.ImageRequest; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.kennyc.bottomsheet.BottomSheet; import com.nextcloud.talk.R; @@ -82,7 +84,6 @@ import com.nextcloud.talk.utils.KeyboardUtils; import com.nextcloud.talk.utils.animations.SharedElementTransition; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.preferences.AppPreferences; import com.yarolegovich.lovelydialog.LovelySaveStateHandler; import com.yarolegovich.lovelydialog.LovelyStandardDialog; @@ -111,10 +112,8 @@ public class ConversationsListController extends BaseController implements Searc .OnScrollStateChangeListener, ConversationMenuInterface { public static final String TAG = "ConversationsListController"; - - private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; public static final int ID_DELETE_CONVERSATION_DIALOG = 0; - + private static final String KEY_SEARCH_QUERY = "ContactsController.searchQuery"; @Inject UserUtils userUtils; @@ -207,27 +206,25 @@ public class ConversationsListController extends BaseController implements Searc private void loadUserAvatar(MenuItem menuItem) { if (getActivity() != null) { int avatarSize = (int) DisplayUtils.convertDpToPixel(menuItem.getIcon().getIntrinsicHeight(), getActivity()); + ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), + currentUser.getUserId(), avatarSize), null); - if (currentUser != null) { - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), - currentUser.getUserId(), avatarSize), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null); + dataSource.subscribe(new BaseBitmapDataSubscriber() { + @Override + protected void onNewResultImpl(Bitmap bitmap) { + if (bitmap != null) { + new RoundPostprocessor(true).process(bitmap); + menuItem.setIcon(new BitmapDrawable(bitmap)); + } + } - GlideApp.with(getActivity()) - .asBitmap() - .centerInside() - .override(avatarSize, avatarSize) - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .load(glideUrl) - .into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - menuItem.setIcon(new BitmapDrawable(resource)); - } - }); - } + @Override + protected void onFailureImpl(DataSource> dataSource) { + menuItem.setIcon(R.drawable.ic_settings_white_24dp); + } + }, UiThreadImmediateExecutorService.getInstance()); } } @@ -434,11 +431,6 @@ public class ConversationsListController extends BaseController implements Searc recyclerView.setAdapter(adapter); - recyclerView.addItemDecoration(new DividerItemDecoration( - recyclerView.getContext(), - layoutManager.getOrientation() - )); - swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false)); swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java b/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java index 8893a77f6..883730442 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/LockedController.java @@ -154,7 +154,7 @@ public class LockedController extends BaseController { public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS ) { + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { if (resultCode == Activity.RESULT_OK) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (SecurityUtils.checkIfWeAreAuthenticated(appPreferences.getScreenLockTimeout())) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java index 2dedbcc2e..7a8fdad88 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/RingtoneSelectionController.java @@ -21,6 +21,7 @@ package com.nextcloud.talk.controllers; import android.annotation.SuppressLint; +import android.content.Context; import android.database.Cursor; import android.media.MediaPlayer; import android.media.RingtoneManager; @@ -70,10 +71,14 @@ public class RingtoneSelectionController extends BaseController implements Flexi @Inject AppPreferences appPreferences; + @Inject + Context context; + private FlexibleAdapter adapter; + private RecyclerView.AdapterDataObserver adapterDataObserver; private List abstractFlexibleItemList = new ArrayList<>(); - private boolean callNotificationSounds = false; + private boolean callNotificationSounds; private MediaPlayer mediaPlayer; private Handler cancelMediaPlayerHandler; @@ -100,21 +105,20 @@ public class RingtoneSelectionController extends BaseController implements Flexi .setMode(SelectableAdapter.Mode.SINGLE); adapter.addListener(this); - fetchNotificationSounds(); cancelMediaPlayerHandler = new Handler(); } adapter.addListener(this); prepareViews(); + fetchNotificationSounds(); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - getRouter().popCurrentController(); - return true; + return getRouter().popCurrentController(); default: return super.onOptionsItemSelected(item); } @@ -126,38 +130,72 @@ public class RingtoneSelectionController extends BaseController implements Flexi recyclerView.setHasFixedSize(true); recyclerView.setAdapter(adapter); + adapterDataObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + findSelectedSound(); + } + }; + + adapter.registerAdapterDataObserver(adapterDataObserver); swipeRefreshLayout.setEnabled(false); } @SuppressLint("LongLogTag") - private void fetchNotificationSounds() { - abstractFlexibleItemList = new ArrayList<>(); - abstractFlexibleItemList.add(new NotificationSoundItem(getResources().getString(R.string.nc_settings_no_ringtone), - null)); - - String ringtoneString; - - if (callNotificationSounds) { - ringtoneString = "android.resource://" + getApplicationContext().getPackageName() + - "/raw/librem_by_feandesign_call"; - } else { - ringtoneString = "android.resource://" + getApplicationContext().getPackageName() + - "/raw/librem_by_feandesign_message"; - } - - abstractFlexibleItemList.add(new NotificationSoundItem(getResources() - .getString(R.string.nc_settings_default_ringtone), ringtoneString)); - + private void findSelectedSound() { boolean foundDefault = false; String preferencesString = null; if ((callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences.getCallRingtoneUri()))) || (!callNotificationSounds && TextUtils.isEmpty((preferencesString = appPreferences .getMessageRingtoneUri())))) { - ((NotificationSoundItem) abstractFlexibleItemList.get(1)).setSelected(true); + adapter.toggleSelection(1); foundDefault = true; } + if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { + try { + RingtoneSettings ringtoneSettings = LoganSquare.parse(preferencesString, RingtoneSettings.class); + if (ringtoneSettings.getRingtoneUri() == null) { + adapter.toggleSelection(0); + } else if (ringtoneSettings.getRingtoneUri().toString().equals(getRingtoneString())) { + adapter.toggleSelection(1); + } else { + NotificationSoundItem notificationSoundItem; + for (int i = 2; i < adapter.getItemCount(); i++) { + notificationSoundItem = (NotificationSoundItem) adapter.getItem(i); + if (notificationSoundItem.getNotificationSoundUri().equals(ringtoneSettings.getRingtoneUri().toString())) { + adapter.toggleSelection(i); + break; + } + } + } + } catch (IOException e) { + Log.e(TAG, "Failed to parse ringtone settings"); + } + } + + adapter.unregisterAdapterDataObserver(adapterDataObserver); + adapterDataObserver = null; + } + + private String getRingtoneString() { + if (callNotificationSounds) { + return ("android.resource://" + context.getPackageName() + + "/raw/librem_by_feandesign_call"); + } else { + return ("android.resource://" + context.getPackageName() + "/raw" + + "/librem_by_feandesign_message"); + } + + } + + private void fetchNotificationSounds() { + abstractFlexibleItemList.add(new NotificationSoundItem(getResources().getString(R.string.nc_settings_no_ringtone), null)); + abstractFlexibleItemList.add(new NotificationSoundItem(getResources() + .getString(R.string.nc_settings_default_ringtone), getRingtoneString())); + if (getActivity() != null) { RingtoneManager manager = new RingtoneManager(getActivity()); @@ -181,29 +219,10 @@ public class RingtoneSelectionController extends BaseController implements Flexi notificationSoundItem = new NotificationSoundItem(notificationTitle, completeNotificationUri); abstractFlexibleItemList.add(notificationSoundItem); - - if (!TextUtils.isEmpty(preferencesString) && !foundDefault) { - try { - RingtoneSettings ringtoneSettings = LoganSquare.parse(preferencesString, RingtoneSettings.class); - if (ringtoneSettings.getRingtoneUri() == null) { - ((NotificationSoundItem) abstractFlexibleItemList.get(0)).setSelected(true); - foundDefault = true; - } else if (completeNotificationUri.equals(ringtoneSettings.getRingtoneUri().toString())) { - notificationSoundItem.setSelected(true); - foundDefault = true; - } else if (ringtoneSettings.getRingtoneUri().toString().equals(ringtoneString)) { - ((NotificationSoundItem) abstractFlexibleItemList.get(1)).setSelected(true); - foundDefault = true; - } - } catch (IOException e) { - Log.e(TAG, "Failed to parse ringtone settings"); - } - } } - } - adapter.updateDataSet(abstractFlexibleItemList, true); + adapter.updateDataSet(abstractFlexibleItemList, false); } @Override @@ -242,14 +261,16 @@ public class RingtoneSelectionController extends BaseController implements Flexi if (callNotificationSounds) { try { appPreferences.setCallRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - toggleSelection(position); + adapter.toggleSelection(position); + adapter.notifyDataSetChanged(); } catch (IOException e) { Log.e(TAG, "Failed to store selected ringtone for calls"); } } else { try { appPreferences.setMessageRingtoneUri(LoganSquare.serialize(ringtoneSettings)); - toggleSelection(position); + adapter.toggleSelection(position); + adapter.notifyDataSetChanged(); } catch (IOException e) { Log.e(TAG, "Failed to store selected ringtone for calls"); } @@ -259,19 +280,6 @@ public class RingtoneSelectionController extends BaseController implements Flexi return true; } - private void toggleSelection(int position) { - adapter.toggleSelection(position); - ((NotificationSoundItem) adapter.getItem(position)).flipItemSelection(); - - NotificationSoundItem notificationSoundItem; - for (int i = 0; i < adapter.getItemCount(); i++) { - if (i != position) { - notificationSoundItem = (NotificationSoundItem) adapter.getItem(i); - notificationSoundItem.flipToFront(); - } - } - } - private void endMediaPlayer() { if (cancelMediaPlayerHandler != null) { cancelMediaPlayerHandler.removeCallbacksAndMessages(null); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index 5a985d2e9..47568ac83 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -36,7 +36,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.Checkable; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -49,10 +48,9 @@ import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler; import com.bluelinelabs.logansquare.LoganSquare; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; -import com.bumptech.glide.load.resource.bitmap.CircleCrop; -import com.bumptech.glide.request.RequestOptions; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; @@ -67,7 +65,6 @@ import com.nextcloud.talk.utils.DoNotDisturbUtils; import com.nextcloud.talk.utils.SecurityUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; -import com.nextcloud.talk.utils.glide.GlideApp; import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.preferences.MagicUserInputModule; import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; @@ -90,113 +87,78 @@ import java.util.*; public class SettingsController extends BaseController { public static final String TAG = "SettingsController"; - + private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0; @BindView(R.id.settings_screen) MaterialPreferenceScreen settingsScreen; - @BindView(R.id.settings_proxy_choice) MaterialChoicePreference proxyChoice; - @BindView(R.id.settings_proxy_port_edit) MaterialEditTextPreference proxyPortEditText; - @BindView(R.id.settings_licence) MaterialStandardPreference licenceButton; - @BindView(R.id.settings_privacy) MaterialStandardPreference privacyButton; - @BindView(R.id.settings_source_code) MaterialStandardPreference sourceCodeButton; - @BindView(R.id.settings_version) MaterialStandardPreference versionInfo; - @BindView(R.id.avatar_image) - ImageView avatarImageView; - + SimpleDraweeView avatarImageView; @BindView(R.id.display_name_text) TextView displayNameTextView; - @BindView(R.id.base_url_text) TextView baseUrlTextView; - @BindView(R.id.settings_call_sound) MaterialStandardPreference settingsCallSound; - @BindView(R.id.settings_message_sound) MaterialStandardPreference settingsMessageSound; - @BindView(R.id.settings_remove_account) MaterialStandardPreference removeAccountButton; - @BindView(R.id.settings_switch) MaterialStandardPreference switchAccountButton; - @BindView(R.id.settings_reauthorize) MaterialStandardPreference reauthorizeButton; - @BindView(R.id.settings_add_account) MaterialStandardPreference addAccountButton; - @BindView(R.id.message_view) MaterialPreferenceCategory messageView; - @BindView(R.id.settings_client_cert) MaterialStandardPreference certificateSetup; - @BindView(R.id.settings_always_vibrate) MaterialSwitchPreference shouldVibrateSwitchPreference; - @BindView(R.id.settings_incognito_keyboard) MaterialSwitchPreference incognitoKeyboardSwitchPreference; - @BindView(R.id.settings_screen_security) MaterialSwitchPreference screenSecuritySwitchPreference; - @BindView(R.id.settings_link_previews) MaterialSwitchPreference linkPreviewsSwitchPreference; - @BindView(R.id.settings_screen_lock) MaterialSwitchPreference screenLockSwitchPreference; - @BindView(R.id.settings_screen_lock_timeout) MaterialChoicePreference screenLockTimeoutChoicePreference; - @BindView(R.id.message_text) TextView messageText; - @Inject EventBus eventBus; - @Inject AppPreferences appPreferences; - @Inject NcApi ncApi; - @Inject UserUtils userUtils; - @Inject Context context; - private LovelySaveStateHandler saveStateHandler; - private UserEntity currentUser; private String credentials; - private OnPreferenceValueChangedListener proxyTypeChangeListener; private OnPreferenceValueChangedListener proxyCredentialsChangeListener; private OnPreferenceValueChangedListener screenSecurityChangeListener; private OnPreferenceValueChangedListener screenLockChangeListener; private OnPreferenceValueChangedListener screenLockTimeoutChangeListener; - private Disposable profileQueryDisposable; private Disposable dbQueryDisposable; - private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0; - @Override protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { return inflater.inflate(R.layout.controller_settings, container, false); @@ -636,17 +598,13 @@ public class SettingsController extends BaseController { avatarId = currentUser.getUsername(); } - GlideUrl glideUrl = new GlideUrl(ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), - avatarId, R.dimen.avatar_size_big), new LazyHeaders.Builder() - .setHeader("Accept", "image/*") - .setHeader("User-Agent", ApiUtils.getUserAgent()) - .build()); - - GlideApp.with(NextcloudTalkApplication.getSharedApplication().getApplicationContext()) - .load(glideUrl) - .centerInside() - .apply(RequestOptions.bitmapTransform(new CircleCrop())) - .into(avatarImageView); + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(avatarImageView.getController()) + .setAutoPlayAnimations(true) + .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(), + avatarId, R.dimen.avatar_size_big), null)) + .build(); + avatarImageView.setController(draweeController); } @Override diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java index 7837bfa01..0526322a5 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.java @@ -30,7 +30,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -227,11 +226,6 @@ public class SwitchAccountController extends BaseController { recyclerView.setHasFixedSize(true); recyclerView.setAdapter(adapter); - recyclerView.addItemDecoration(new DividerItemDecoration( - recyclerView.getContext(), - layoutManager.getOrientation() - )); - swipeRefreshLayout.setEnabled(false); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java index 95205bcb4..7e9efb92a 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/CallMenuController.java @@ -231,20 +231,20 @@ public class CallMenuController extends BaseController implements FlexibleAdapte if (tag > 0) { if (tag == 1 || tag == 9) { - if (tag == 1) { - Data data; - if ((data = getWorkerData()) != null) { - OneTimeWorkRequest leaveConversationWorker = - new OneTimeWorkRequest.Builder(LeaveConversationWorker.class).setInputData(data).build(); - WorkManager.getInstance().enqueue(leaveConversationWorker); - } - } else { - Bundle deleteConversationBundle; - if ((deleteConversationBundle = getDeleteConversationBundle()) != null) { - conversationMenuInterface.openLovelyDialogWithIdAndBundle(ConversationsListController.ID_DELETE_CONVERSATION_DIALOG, deleteConversationBundle); - } + if (tag == 1) { + Data data; + if ((data = getWorkerData()) != null) { + OneTimeWorkRequest leaveConversationWorker = + new OneTimeWorkRequest.Builder(LeaveConversationWorker.class).setInputData(data).build(); + WorkManager.getInstance().enqueue(leaveConversationWorker); } - eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); + } else { + Bundle deleteConversationBundle; + if ((deleteConversationBundle = getDeleteConversationBundle()) != null) { + conversationMenuInterface.openLovelyDialogWithIdAndBundle(ConversationsListController.ID_DELETE_CONVERSATION_DIALOG, deleteConversationBundle); + } + } + eventBus.post(new BottomSheetLockEvent(true, 0, false, true)); } else { bundle.putInt(BundleKeys.KEY_OPERATION_CODE, tag); if (tag != 2 && tag != 4 && tag != 6 && tag != 7) { @@ -291,11 +291,6 @@ public class CallMenuController extends BaseController implements FlexibleAdapte return true; } - @Parcel - public enum MenuType { - REGULAR, SHARE - } - private Data getWorkerData() { if (!TextUtils.isEmpty(conversation.getToken())) { Data.Builder data = new Data.Builder(); @@ -318,4 +313,9 @@ public class CallMenuController extends BaseController implements FlexibleAdapte return null; } + + @Parcel + public enum MenuType { + REGULAR, SHARE + } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java index d7f69ce79..a547d7fbc 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java @@ -197,9 +197,9 @@ public class RestModule { if (appPreferences.getProxyCredentials() && !TextUtils.isEmpty(appPreferences.getProxyUsername()) && !TextUtils.isEmpty(appPreferences.getProxyPassword())) { - httpClient.proxyAuthenticator(new ProxyAuthenticator(Credentials.basic( + httpClient.proxyAuthenticator(new MagicAuthenticator(Credentials.basic( appPreferences.getProxyUsername(), - appPreferences.getProxyPassword()))); + appPreferences.getProxyPassword()), "Proxy-Authorization")); } } @@ -210,10 +210,10 @@ public class RestModule { public static class HeadersInterceptor implements Interceptor { + @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { Request original = chain.request(); - Request request = original.newBuilder() .header("User-Agent", ApiUtils.getUserAgent()) .header("Accept", "application/json") @@ -231,24 +231,27 @@ public class RestModule { } } - private class ProxyAuthenticator implements Authenticator { + public static class MagicAuthenticator implements Authenticator { private String credentials; + private String authenticatorType; - private ProxyAuthenticator(String credentials) { + public MagicAuthenticator(@NonNull String credentials, @NonNull String authenticatorType) { this.credentials = credentials; + this.authenticatorType = authenticatorType; } @Nullable @Override - public Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException { - if (credentials.equals(response.request().header("Proxy-Authorization"))) { + public Request authenticate(@Nullable Route route, @NonNull Response response) { + if (response.request().header(authenticatorType) != null) { return null; } - int attemptsCount = 0; Response countedResponse = response; + int attemptsCount = 0; + while ((countedResponse = countedResponse.priorResponse()) != null) { attemptsCount++; if (attemptsCount == 3) { @@ -257,7 +260,7 @@ public class RestModule { } return response.request().newBuilder() - .header("Proxy-Authorization", credentials) + .header(authenticatorType, credentials) .build(); } } diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.java b/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.java new file mode 100644 index 000000000..836b54d59 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/interfaces/SelectionInterface.java @@ -0,0 +1,27 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.interfaces; + +public interface SelectionInterface { + void toggleBrowserItemSelection(String path); + + boolean isPathSelected(String path); +} diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java index 27821ce45..f69c3e6d8 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java @@ -86,6 +86,7 @@ public class DeleteConversationWorker extends Worker { .subscribeOn(Schedulers.newThread()) .blockingSubscribe(new Observer() { Disposable disposable; + @Override public void onSubscribe(Disposable d) { disposable = d; diff --git a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java index bc9938674..4cf4716ef 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java @@ -86,6 +86,7 @@ public class LeaveConversationWorker extends Worker { .subscribeOn(Schedulers.newThread()) .blockingSubscribe(new Observer() { Disposable disposable; + @Override public void onSubscribe(Disposable d) { disposable = d; diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java new file mode 100644 index 000000000..b128cfdbe --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.java @@ -0,0 +1,105 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.jobs; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.Worker; +import androidx.work.WorkerParameters; +import autodagger.AutoInjector; +import com.nextcloud.talk.api.NcApi; +import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.utils.ApiUtils; +import com.nextcloud.talk.utils.bundle.BundleKeys; +import com.nextcloud.talk.utils.database.user.UserUtils; +import io.reactivex.Observer; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@AutoInjector(NextcloudTalkApplication.class) +public class ShareOperationWorker extends Worker { + @Inject + UserUtils userUtils; + @Inject + NcApi ncApi; + private long userId; + private UserEntity operationsUser; + private String roomToken; + private List filesArray = new ArrayList<>(); + private String credentials; + private String baseUrl; + + public ShareOperationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); + Data data = workerParams.getInputData(); + userId = data.getLong(BundleKeys.KEY_INTERNAL_USER_ID, 0); + roomToken = data.getString(BundleKeys.KEY_ROOM_TOKEN); + Collections.addAll(filesArray, data.getStringArray(BundleKeys.KEY_FILE_PATHS)); + operationsUser = userUtils.getUserWithId(userId); + credentials = ApiUtils.getCredentials(operationsUser.getUsername(), operationsUser.getToken()); + baseUrl = operationsUser.getBaseUrl(); + } + + + @NonNull + @Override + public Result doWork() { + for (int i = 0; i < filesArray.size(); i++) { + ncApi.createRemoteShare(credentials, + ApiUtils.getSharingUrl(baseUrl), + filesArray.get(i), + roomToken, + "10") + .subscribeOn(Schedulers.newThread()) + .blockingSubscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(Void aVoid) { + + } + + @Override + public void onError(Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + return Result.success(); + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.java b/app/src/main/java/com/nextcloud/talk/models/database/User.java index b3de7024e..ce8610236 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/User.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/User.java @@ -75,6 +75,20 @@ public interface User extends Parcelable, Persistable, Serializable { return false; } + default boolean hasExternalCapability(String capabilityName) { + if (getCapabilities() != null) { + try { + Capabilities capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities.getExternalCapability() != null && capabilities.getExternalCapability().containsKey("v1")) { + return capabilities.getExternalCapability().get("v1").contains("capabilityName"); + } + } catch (IOException e) { + Log.e(TAG, "Failed to get capabilities for the user"); + } + } + return false; + } + default boolean hasSpreedCapabilityWithName(String capabilityName) { if (getCapabilities() != null) { try { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java index d7256e1b5..a96787056 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/Capabilities.java @@ -25,6 +25,9 @@ import com.bluelinelabs.logansquare.annotation.JsonObject; import lombok.Data; import org.parceler.Parcel; +import java.util.HashMap; +import java.util.List; + @Parcel @Data @JsonObject @@ -34,4 +37,10 @@ public class Capabilities { @JsonField(name = "notifications") NotificationsCapability notificationsCapability; + + @JsonField(name = "theming") + ThemingCapability themingCapability; + + @JsonField(name = "external") + HashMap> externalCapability; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java new file mode 100644 index 000000000..f21a4b15c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/ThemingCapability.java @@ -0,0 +1,61 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2019 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models.json.capabilities; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import lombok.Data; +import org.parceler.Parcel; + +@Parcel +@Data +@JsonObject +class ThemingCapability { + @JsonField(name = "name") + String name; + + @JsonField(name = "url") + String url; + + @JsonField(name = "slogan") + String slogan; + + @JsonField(name = "color") + String color; + + @JsonField(name = "color-text") + String colorText; + + @JsonField(name = "color-element") + String colorElement; + + @JsonField(name = "logo") + String logo; + + @JsonField(name = "background") + String background; + + @JsonField(name = "background-plain") + boolean backgroundPlain; + + @JsonField(name = "background-default") + boolean backgroundDefault; +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java index 9c2fef0ea..519000cc5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java @@ -26,6 +26,7 @@ import com.bluelinelabs.logansquare.annotation.JsonIgnore; import com.bluelinelabs.logansquare.annotation.JsonObject; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; +import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.TextMatchers; @@ -44,7 +45,7 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent @JsonIgnore public boolean isGrouped; @JsonIgnore - public String activeUserId; + public UserEntity activeUser; @JsonIgnore public Map selectedIndividualHashMap; @JsonIgnore @@ -52,7 +53,6 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent List messageTypesToIgnore = Arrays.asList(MessageType.REGULAR_TEXT_MESSAGE, MessageType.SYSTEM_MESSAGE, MessageType.SINGLE_LINK_VIDEO_MESSAGE, MessageType.SINGLE_LINK_AUDIO_MESSAGE, MessageType.SINGLE_LINK_MESSAGE); - String baseUrl; @JsonField(name = "id") int jsonMessageId; @JsonField(name = "token") @@ -96,9 +96,8 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent Map individualHashMap = messageParameters.get(key); if (individualHashMap.get("type").equals("file")) { selectedIndividualHashMap = individualHashMap; - return String.format(Locale.getDefault(), - "%s/index.php/core/preview?fileId=%s&x=%d&y=%d&forceIcon=1", - baseUrl, individualHashMap.get("id"), 480, 480); + return (ApiUtils.getUrlForFilePreviewWithFileId(getActiveUser().getBaseUrl(), + individualHashMap.get("id"), NextcloudTalkApplication.getSharedApplication().getResources().getDimensionPixelSize(R.dimen.maximum_file_preview_size))); } } } @@ -130,14 +129,6 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent this.selectedIndividualHashMap = selectedIndividualHashMap; } - public String getBaseUrl() { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; - } - @Override public String getId() { return Integer.toString(jsonMessageId); @@ -155,42 +146,42 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent if (getMessageType().equals(MessageType.SINGLE_LINK_GIPHY_MESSAGE) || getMessageType().equals(MessageType.SINGLE_LINK_TENOR_MESSAGE) || getMessageType().equals(MessageType.SINGLE_LINK_GIF_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_a_gif_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_a_gif), !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest))); } } else if (getMessageType().equals(MessageType.SINGLE_NC_ATTACHMENT_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_an_attachment_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_an_attachment), !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest))); } } else if (getMessageType().equals(MessageType.SINGLE_LINK_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_a_link_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_a_link), !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest))); } } else if (getMessageType().equals(MessageType.SINGLE_LINK_AUDIO_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_an_audio_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_an_audio), !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest))); } } else if (getMessageType().equals(MessageType.SINGLE_LINK_VIDEO_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_a_video_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_a_video), !TextUtils.isEmpty(getActorDisplayName()) ? getActorDisplayName() : NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_guest))); } } else if (getMessageType().equals(MessageType.SINGLE_LINK_IMAGE_MESSAGE)) { - if (getActorId().equals(getActiveUserId())) { + if (getActorId().equals(getActiveUser().getUserId())) { return (NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_sent_an_image_you)); } else { return (String.format(NextcloudTalkApplication.getSharedApplication().getResources().getString(R.string.nc_sent_an_image), @@ -218,7 +209,7 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent @Override public String getAvatar() { if (getActorType().equals("users")) { - return ApiUtils.getUrlForAvatarWithName(getBaseUrl(), actorId, R.dimen.avatar_size); + return ApiUtils.getUrlForAvatarWithName(getActiveUser().getBaseUrl(), actorId, R.dimen.avatar_size); } else { return null; } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java index f6f32dd98..23a4d65f2 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/mention/Mention.java @@ -20,7 +20,6 @@ package com.nextcloud.talk.models.json.mention; import com.bluelinelabs.logansquare.annotation.JsonField; -import com.bluelinelabs.logansquare.annotation.JsonIgnore; import com.bluelinelabs.logansquare.annotation.JsonObject; import lombok.Data; import org.parceler.Parcel; diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java index 0c39041f2..4a70450e5 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.java @@ -26,6 +26,7 @@ package com.nextcloud.talk.utils; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; import com.nextcloud.talk.R; @@ -101,6 +102,26 @@ public class AccountUtils { return appName; } + public static boolean canWeOpenFilesApp(Context context, String accountName) { + PackageManager pm = context.getPackageManager(); + try { + PackageInfo packageInfo = + pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0); + if (packageInfo.versionCode < 1) { + final AccountManager accMgr = AccountManager.get(context); + final Account[] accounts = accMgr.getAccountsByType(context.getString(R.string.nc_import_account_type)); + for (Account account : accounts) { + if (account.name.equals(accountName)) { + return true; + } + } + } + } catch (PackageManager.NameNotFoundException appNotFoundException) { + + } + return false; + } + public static ImportAccount getInformationFromAccount(Account account) { int lastAtPos = account.name.lastIndexOf("@"); String urlString = account.name.substring(lastAtPos + 1); diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index b0e81b0cc..ddc548457 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -60,6 +60,21 @@ public class ApiUtils { return retrofitBucket; } + public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath, int px) { + return baseUrl + "/index.php/core/preview.png?file=" + + Uri.encode(remotePath, "UTF-8") + + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + } + + public static String getUrlForFilePreviewWithFileId(String baseUrl, String fileId, int px) { + return baseUrl + "/index.php/core/preview?fileId=" + + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; + } + + public static String getSharingUrl(String baseUrl) { + return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares"; + } + public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl, @Nullable String searchQuery) { RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery); retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get"); diff --git a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.java new file mode 100644 index 000000000..76f3fd92f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.java @@ -0,0 +1,43 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.utils; + +import android.content.Context; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +public class DateUtils { + public static String getLocalDateTimeStringFromTimestamp(Context context, long timestamp) { + Calendar cal = Calendar.getInstance(); + TimeZone tz = cal.getTimeZone(); + + /* date formatter in local timezone */ + Locale currentLocale = context.getResources().getConfiguration().locale; + SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", currentLocale); + sdf.setTimeZone(tz); + + return sdf.format(new Date(timestamp)); + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index f0988a878..525340ea7 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -60,6 +60,7 @@ import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.ImageInfo; import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor; +import com.facebook.imagepipeline.postprocessors.RoundPostprocessor; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.google.android.material.chip.ChipDrawable; @@ -72,9 +73,13 @@ import com.vanniktech.emoji.EmojiEditText; import com.vanniktech.emoji.EmojiTextView; import org.greenrobot.eventbus.EventBus; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -86,14 +91,14 @@ public class DisplayUtils { SpannableString spannableString = new SpannableString(string); spannableString.setSpan(new ClickableSpan() { @Override - public void onClick(@NonNull View widget) { + public void onClick(@Nonnull View widget) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); NextcloudTalkApplication.getSharedApplication().getApplicationContext().startActivity(browserIntent); } @Override - public void updateDrawState(TextPaint ds) { + public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } @@ -104,7 +109,8 @@ public class DisplayUtils { private static void updateViewSize(@Nullable ImageInfo imageInfo, SimpleDraweeView draweeView) { if (imageInfo != null) { - draweeView.getLayoutParams().width = imageInfo.getWidth() > 480 ? 480 : imageInfo.getWidth(); + int maxSize = draweeView.getContext().getResources().getDimensionPixelSize(R.dimen.maximum_file_preview_size); + draweeView.getLayoutParams().width = imageInfo.getWidth() > maxSize ? maxSize : imageInfo.getWidth(); draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); draweeView.requestLayout(); @@ -120,7 +126,7 @@ public class DisplayUtils { public static Bitmap getRoundedBitmapFromVectorDrawableResource(Resources resources, int resource) { VectorDrawable vectorDrawable = (VectorDrawable) resources.getDrawable(resource); Bitmap bitmap = getBitmap(vectorDrawable); - new RoundAsCirclePostprocessor(true).process(bitmap); + new RoundPostprocessor(true).process(bitmap); return bitmap; } @@ -142,11 +148,18 @@ public class DisplayUtils { return bitmap; } - public static ImageRequest getImageRequestForUrl(String url) { + public static ImageRequest getImageRequestForUrl(String url, @Nullable UserEntity userEntity) { + Map headers = new HashMap<>(); + if (userEntity != null && url.startsWith(userEntity.getBaseUrl()) && url.contains("index.php/core/preview?fileId=")) { + headers.put("Authorization", ApiUtils.getCredentials(userEntity.getUsername(), + userEntity.getToken())); + } + return ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) .setProgressiveRenderingEnabled(true) .setRotationOptions(RotationOptions.autoRotate()) .disableDiskCache() + .setHeaders(headers) .build(); } @@ -252,7 +265,7 @@ public class DisplayUtils { if (!isCall) { ImageRequest imageRequest = - getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), id, R.dimen.avatar_size_big)); + getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser.getBaseUrl(), id, R.dimen.avatar_size_big), null); ImagePipeline imagePipeline = Fresco.getImagePipeline(); DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); diff --git a/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.java new file mode 100644 index 000000000..368e5914e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.java @@ -0,0 +1,161 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017/2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.utils; + +import com.nextcloud.talk.R; + +import java.util.HashMap; +import java.util.Map; + +public class DrawableUtils { + + + public static int getDrawableResourceIdForMimeType(String mimetype) { + Map drawableMap = new HashMap<>(); + + // Initial list of mimetypes was acquired from https://github.com/nextcloud/server/blob/694ba5435b2963e201f6a6d2c775836bde07aaef/core/js/mimetypelist.js + drawableMap.put("application/coreldraw", R.drawable.ic_mimetype_image); + drawableMap.put("application/epub+zip", R.drawable.ic_mimetype_text); + drawableMap.put("application/font-sfnt", R.drawable.ic_mimetype_image); + drawableMap.put("application/font-woff", R.drawable.ic_mimetype_image); + drawableMap.put("application/gpx+xml", R.drawable.ic_mimetype_location); + drawableMap.put("application/illustrator", R.drawable.ic_mimetype_image); + drawableMap.put("application/javascript", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/json", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/msaccess", R.drawable.ic_mimetype_file); + drawableMap.put("application/msexcel", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/msonenote", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/mspowerpoint", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/msword", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/octet-stream", R.drawable.ic_mimetype_file); + drawableMap.put("application/postscript", R.drawable.ic_mimetype_image); + drawableMap.put("application/rss+xml", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/vnd.android.package-archive", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/vnd.lotus-wordpro", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.garmin.tcx+xml", R.drawable.ic_mimetype_location); + drawableMap.put("application/vnd.google-earth.kml+xml", R.drawable.ic_mimetype_location); + drawableMap.put("application/vnd.google-earth.kmz", R.drawable.ic_mimetype_location); + drawableMap.put("application/vnd.ms-excel", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.ms-excel.addin.macroEnabled.12", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.ms-excel.sheet.binary.macroEnabled.12", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.ms-excel.sheet.macroEnabled.12", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.ms-excel.template.macroEnabled.12", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.ms-fontobject", R.drawable.ic_mimetype_image); + drawableMap.put("application/vnd.ms-powerpoint", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.ms-powerpoint.addin.macroEnabled.12", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.ms-powerpoint.presentation.macroEnabled.12", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.ms-powerpoint.slideshow.macroEnabled.12", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.ms-powerpoint.template.macroEnabled.12", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.ms-visio.drawing.macroEnabled.12", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-visio.drawing", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-visio.stencil.macroEnabled.12", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-visio.stencil", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-visio.template.macroEnabled.12", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-visio.template", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.ms-word.template.macroEnabled.12", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.oasis.opendocument.presentation", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.oasis.opendocument.presentation-template", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.oasis.opendocument.spreadsheet", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.oasis.opendocument.spreadsheet-template", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.oasis.opendocument.text", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.oasis.opendocument.text-master", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.oasis.opendocument.text-template", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.oasis.opendocument.text-web", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.openxmlformats-officedocument.presentationml.slideshow", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.openxmlformats-officedocument.presentationml.template", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.openxmlformats-officedocument.spreadsheetml.template", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.openxmlformats-officedocument.wordprocessingml.template", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.visio", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/vnd.wordperfect", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/x-7z-compressed", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-bzip2", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-cbr", R.drawable.ic_mimetype_text); + drawableMap.put("application/x-compressed", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-dcraw", R.drawable.ic_mimetype_image); + drawableMap.put("application/x-deb", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-fictionbook+xml", R.drawable.ic_mimetype_text); + drawableMap.put("application/x-font", R.drawable.ic_mimetype_image); + drawableMap.put("application/x-gimp", R.drawable.ic_mimetype_image); + drawableMap.put("application/x-gzip", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-iwork-keynote-sffkey", R.drawable.ic_mimetype_x_office_presentation); + drawableMap.put("application/x-iwork-numbers-sffnumbers", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("application/x-iwork-pages-sffpages", R.drawable.ic_mimetype_x_office_document); + drawableMap.put("application/x-mobipocket-ebook", R.drawable.ic_mimetype_text); + drawableMap.put("application/x-perl", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/x-photoshop", R.drawable.ic_mimetype_image); + drawableMap.put("application/x-php", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/x-rar-compressed", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-tar", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("application/x-tex", R.drawable.ic_mimetype_text); + drawableMap.put("application/xml", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/yaml", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/zip", R.drawable.ic_mimetype_package_x_generic); + drawableMap.put("database", R.drawable.ic_mimetype_file); + drawableMap.put("httpd/unix-directory", R.drawable.ic_mimetype_folder); + drawableMap.put("text/css", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/csv", R.drawable.ic_mimetype_x_office_spreadsheet); + drawableMap.put("text/html", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-c", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-c++src", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-h", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-java-source", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-ldif", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-python", R.drawable.ic_mimetype_text_code); + drawableMap.put("text/x-shellscript", R.drawable.ic_mimetype_text_code); + drawableMap.put("web", R.drawable.ic_mimetype_text_code); + drawableMap.put("application/internet-shortcut", R.drawable.ic_mimetype_link); + + drawableMap.put("inode/directory", R.drawable.ic_mimetype_folder); + drawableMap.put("unknown", R.drawable.ic_mimetype_file); + drawableMap.put("application/pdf", R.drawable.ic_mimetype_application_pdf); + + if ("DIR".equals(mimetype)) { + mimetype = "inode/directory"; + return drawableMap.get(mimetype); + } + + if (drawableMap.containsKey(mimetype)) { + return drawableMap.get(mimetype); + } + + if (mimetype.startsWith("image/")) { + return R.drawable.ic_mimetype_image; + } + + if (mimetype.startsWith("video/")) { + return R.drawable.ic_mimetype_video; + } + + if (mimetype.startsWith("text/")) { + return R.drawable.ic_mimetype_text; + } + + if (mimetype.startsWith("audio")) { + return R.drawable.ic_mimetype_audio; + } + + return drawableMap.get("unknown"); + } + +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java index e079e7d25..11c5e919e 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java @@ -24,9 +24,7 @@ import android.content.Context; import android.text.TextUtils; import android.util.Base64; import android.util.Log; - import autodagger.AutoInjector; - import com.bluelinelabs.logansquare.LoganSquare; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; @@ -38,15 +36,12 @@ import com.nextcloud.talk.models.json.push.PushConfigurationState; import com.nextcloud.talk.models.json.push.PushRegistrationOverall; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; - import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; - import org.greenrobot.eventbus.EventBus; import javax.inject.Inject; - import java.io.*; import java.security.*; import java.security.spec.InvalidKeySpecException; diff --git a/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java b/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java index 237a7b12a..3988f6e31 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java +++ b/app/src/main/java/com/nextcloud/talk/utils/TextMatchers.java @@ -24,11 +24,10 @@ package com.nextcloud.talk.utils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.util.PatternsCompat; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.vanniktech.emoji.EmojiInformation; import com.vanniktech.emoji.EmojiUtils; - -import androidx.core.util.PatternsCompat; import eu.medsea.mimeutil.MimeUtil; import eu.medsea.mimeutil.detector.ExtensionMimeDetector; import eu.medsea.mimeutil.detector.MagicMimeMimeDetector; diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java index d858c5f74..b3652a3f4 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.java @@ -51,4 +51,8 @@ public class BundleKeys { public static final String KEY_FROM_NOTIFICATION_START_CALL = "KEY_FROM_NOTIFICATION_START_CALL"; public static final String KEY_ROOM_ID = "KEY_ROOM_ID"; public static final String KEY_ARE_CALL_SOUNDS = "KEY_ARE_CALL_SOUNDS"; + public static final String KEY_BROWSER_TYPE = "KEY_BROWSER_TYPE"; + public static final String KEY_FILE_PATHS = "KEY_FILE_PATHS"; + public static final String KEY_ACCOUNT = "KEY_ACCOUNT"; + public static final String KEY_FILE_PATH = "KEY_FILE_PATH"; } diff --git a/app/src/main/java/com/nextcloud/talk/utils/glide/CachingGlideModule.java b/app/src/main/java/com/nextcloud/talk/utils/glide/CachingGlideModule.java deleted file mode 100644 index 59deda82e..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/glide/CachingGlideModule.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.utils.glide; - - -import android.content.Context; -import autodagger.AutoInjector; -import com.bumptech.glide.Glide; -import com.bumptech.glide.GlideBuilder; -import com.bumptech.glide.Registry; -import com.bumptech.glide.annotation.GlideModule; -import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.module.AppGlideModule; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import okhttp3.OkHttpClient; - -import javax.inject.Inject; -import java.io.InputStream; - -@AutoInjector(NextcloudTalkApplication.class) -@GlideModule -public class CachingGlideModule extends AppGlideModule { - @Inject - OkHttpClient okHttpClient; - - @Override - public void registerComponents(Context context, Glide glide, Registry registry) { - NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); - registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient)); - } - - @Override - public void applyOptions(Context context, GlideBuilder builder) { - } -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java index df2d0e201..c374f3d04 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/power/PowerManagerUtils.java @@ -39,40 +39,17 @@ import javax.inject.Inject; public class PowerManagerUtils { private static final String TAG = "PowerManagerUtils"; - - @Inject - Context context; - private final PowerManager.WakeLock fullLock; private final PowerManager.WakeLock partialLock; private final WifiManager.WifiLock wifiLock; - private ProximityLock proximityLock; - private final boolean wifiLockEnforced; + @Inject + Context context; + private ProximityLock proximityLock; private boolean proximityDisabled = false; private int orientation; - public enum PhoneState { - IDLE, - PROCESSING, //used when the phone is active but before the user should be alerted. - INTERACTIVE, - WITHOUT_PROXIMITY_SENSOR_LOCK, - WITH_PROXIMITY_SENSOR_LOCK - } - - public enum WakeLockState { - FULL, - PARTIAL, - SLEEP, - PROXIMITY - } - - public void setOrientation(int newOrientation) { - orientation = newOrientation; - updateInCallWakeLockState(); - } - public PowerManagerUtils() { NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); @@ -95,8 +72,13 @@ public class PowerManagerUtils { orientation = context.getResources().getConfiguration().orientation; } + public void setOrientation(int newOrientation) { + orientation = newOrientation; + updateInCallWakeLockState(); + } + public void updatePhoneState(PhoneState state) { - switch(state) { + switch (state) { case IDLE: setWakeLockState(WakeLockState.SLEEP); break; @@ -132,7 +114,7 @@ public class PowerManagerUtils { @SuppressLint("WakelockTimeout") private synchronized void setWakeLockState(WakeLockState newState) { - switch(newState) { + switch (newState) { case FULL: if (!fullLock.isHeld()) { fullLock.acquire(); @@ -183,7 +165,7 @@ public class PowerManagerUtils { } fullLock.release( - + ); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { proximityLock.acquire(); @@ -193,4 +175,19 @@ public class PowerManagerUtils { // something went very very wrong } } + + public enum PhoneState { + IDLE, + PROCESSING, //used when the phone is active but before the user should be alerted. + INTERACTIVE, + WITHOUT_PROXIMITY_SENSOR_LOCK, + WITH_PROXIMITY_SENSOR_LOCK + } + + public enum WakeLockState { + FULL, + PARTIAL, + SLEEP, + PROXIMITY + } } diff --git a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml new file mode 100644 index 000000000..8bdf963db --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_check_black_24dp.xml b/app/src/main/res/drawable/ic_check_black_24dp.xml new file mode 100644 index 000000000..180854d6f --- /dev/null +++ b/app/src/main/res/drawable/ic_check_black_24dp.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_close_grey600_24dp.xml b/app/src/main/res/drawable/ic_close_grey600_24dp.xml index 9b88ee4a6..38b94fe27 100644 --- a/app/src/main/res/drawable/ic_close_grey600_24dp.xml +++ b/app/src/main/res/drawable/ic_close_grey600_24dp.xml @@ -1,3 +1,23 @@ + + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + + + diff --git a/app/src/main/res/drawable/ic_group_grey600_24px.xml b/app/src/main/res/drawable/ic_group_grey600_24px.xml index 4acc0cfd2..29c5577eb 100644 --- a/app/src/main/res/drawable/ic_group_grey600_24px.xml +++ b/app/src/main/res/drawable/ic_group_grey600_24px.xml @@ -1,3 +1,23 @@ + + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index d5910dfc5..caf95f772 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,3 +1,23 @@ + + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + diff --git a/app/src/main/res/drawable/ic_mimetype_application.xml b/app/src/main/res/drawable/ic_mimetype_application.xml new file mode 100755 index 000000000..c00d93fc9 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_application.xml @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_application_pdf.xml b/app/src/main/res/drawable/ic_mimetype_application_pdf.xml new file mode 100755 index 000000000..72b3d8ada --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_application_pdf.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_audio.xml b/app/src/main/res/drawable/ic_mimetype_audio.xml new file mode 100755 index 000000000..f91ca57f8 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_audio.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_file.xml b/app/src/main/res/drawable/ic_mimetype_file.xml new file mode 100755 index 000000000..da6d6db19 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_file.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder.xml b/app/src/main/res/drawable/ic_mimetype_folder.xml new file mode 100755 index 000000000..a7dcdf3b6 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_drag_accept.xml b/app/src/main/res/drawable/ic_mimetype_folder_drag_accept.xml new file mode 100755 index 000000000..b61bc29ba --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_drag_accept.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_encrypted.xml b/app/src/main/res/drawable/ic_mimetype_folder_encrypted.xml new file mode 100755 index 000000000..03238e26f --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_encrypted.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_external.xml b/app/src/main/res/drawable/ic_mimetype_folder_external.xml new file mode 100755 index 000000000..9f14a0498 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_external.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_public.xml b/app/src/main/res/drawable/ic_mimetype_folder_public.xml new file mode 100755 index 000000000..f373eb4e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_public.xml @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_shared.xml b/app/src/main/res/drawable/ic_mimetype_folder_shared.xml new file mode 100755 index 000000000..2d785d73e --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_shared.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_folder_starred.xml b/app/src/main/res/drawable/ic_mimetype_folder_starred.xml new file mode 100755 index 000000000..8ce35d031 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_folder_starred.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_image.xml b/app/src/main/res/drawable/ic_mimetype_image.xml new file mode 100755 index 000000000..0f10d7a36 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_image.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_link.xml b/app/src/main/res/drawable/ic_mimetype_link.xml new file mode 100755 index 000000000..5b5a8d881 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_link.xml @@ -0,0 +1,32 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_location.xml b/app/src/main/res/drawable/ic_mimetype_location.xml new file mode 100755 index 000000000..55a4943f6 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_location.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_package_x_generic.xml b/app/src/main/res/drawable/ic_mimetype_package_x_generic.xml new file mode 100755 index 000000000..71ec9dc0c --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_package_x_generic.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_text.xml b/app/src/main/res/drawable/ic_mimetype_text.xml new file mode 100755 index 000000000..14454141d --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_text.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_text_calendar.xml b/app/src/main/res/drawable/ic_mimetype_text_calendar.xml new file mode 100755 index 000000000..7a7d719dd --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_text_calendar.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_text_code.xml b/app/src/main/res/drawable/ic_mimetype_text_code.xml new file mode 100755 index 000000000..e61e6e87c --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_text_code.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_text_vcard.xml b/app/src/main/res/drawable/ic_mimetype_text_vcard.xml new file mode 100755 index 000000000..27c588280 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_text_vcard.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_video.xml b/app/src/main/res/drawable/ic_mimetype_video.xml new file mode 100755 index 000000000..e978a9212 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_video.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_x_office_document.xml b/app/src/main/res/drawable/ic_mimetype_x_office_document.xml new file mode 100755 index 000000000..dc6f39040 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_x_office_document.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_x_office_presentation.xml b/app/src/main/res/drawable/ic_mimetype_x_office_presentation.xml new file mode 100755 index 000000000..a4a3adaf1 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_x_office_presentation.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mimetype_x_office_spreadsheet.xml b/app/src/main/res/drawable/ic_mimetype_x_office_spreadsheet.xml new file mode 100755 index 000000000..9a1428920 --- /dev/null +++ b/app/src/main/res/drawable/ic_mimetype_x_office_spreadsheet.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pencil_grey600_24dp.xml b/app/src/main/res/drawable/ic_pencil_grey600_24dp.xml index 4e39e0b19..9ce260743 100644 --- a/app/src/main/res/drawable/ic_pencil_grey600_24dp.xml +++ b/app/src/main/res/drawable/ic_pencil_grey600_24dp.xml @@ -1,3 +1,23 @@ + + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + ~ + ~ This program is free software: you can redistribute it and/or modify + ~ it under the terms of the GNU General Public License as published by + ~ the Free Software Foundation, either version 3 of the License, or + ~ at your option) any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License + ~ along with this program. If not, see . + --> + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c531e39f2..262467fab 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,4 +1,23 @@ - + + + app:titleMarginStart="0dp" /> - + android:layout_centerInParent="true" + app:roundAsCircle="true" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/controller_call.xml b/app/src/main/res/layout/controller_call.xml index 1522b1352..faff7f8b8 100644 --- a/app/src/main/res/layout/controller_call.xml +++ b/app/src/main/res/layout/controller_call.xml @@ -88,16 +88,14 @@ android:layout_margin="16dp" android:visibility="invisible" /> - + android:backgroundTint="@color/colorPrimary" + app:actualImageResource="@drawable/ic_switch_video_white_24px" /> @@ -110,16 +108,14 @@ android:layout_marginBottom="8dp" android:animateLayoutChanges="true"> - + android:tint="@color/nc_darkRed" + app:actualImageResource="@drawable/ic_call_end_white_24px" /> - + android:backgroundTint="@color/colorPrimary" + app:actualImageResource="@drawable/ic_mic_off_white_24px" /> - + android:alpha="0.7" /> - + android:visibility="gone" /> diff --git a/app/src/main/res/layout/controller_call_notification.xml b/app/src/main/res/layout/controller_call_notification.xml index a1e5c9ac6..836b4642d 100644 --- a/app/src/main/res/layout/controller_call_notification.xml +++ b/app/src/main/res/layout/controller_call_notification.xml @@ -17,18 +17,19 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - + tools:srcCompat="@tools:sample/backgrounds/scenic" /> - + android:layout_centerInParent="true" + app:roundAsCircle="true" + tools:srcCompat="@tools:sample/avatars[0]" /> + android:orientation="horizontal"> - + app:backgroundImage="@color/nc_darkGreen" + app:placeholderImage="@drawable/ic_call_white_24dp" + app:roundAsCircle="true" /> - + app:backgroundImage="@color/nc_darkRed" + app:placeholderImage="@drawable/ic_call_end_white_24px" + app:roundAsCircle="true" /> - + app:backgroundImage="@color/nc_darkGreen" + app:placeholderImage="@drawable/ic_videocam_white_24px" + app:roundAsCircle="true" /> - + diff --git a/app/src/main/res/layout/controller_chat.xml b/app/src/main/res/layout/controller_chat.xml index 9f2263dfb..73e6267a0 100644 --- a/app/src/main/res/layout/controller_chat.xml +++ b/app/src/main/res/layout/controller_chat.xml @@ -124,13 +124,17 @@ android:layout_alignParentBottom="true" android:inputType="textLongMessage|textAutoComplete" android:maxLength="1000" + app:attachmentButtonDefaultBgColor="@color/colorPrimary" + app:attachmentButtonDefaultIconColor="@color/white" + app:attachmentButtonHeight="36dp" + app:attachmentButtonWidth="36dp" app:inputButtonDefaultBgColor="@color/colorPrimary" - app:inputButtonDefaultBgPressedColor="@color/colorPrimaryDark" - app:inputButtonHeight="30dp" - app:inputButtonMargin="16dp" - app:inputButtonWidth="30dp" + app:inputButtonHeight="36dp" + app:inputButtonMargin="8dp" + app:inputButtonWidth="36dp" app:inputHint="@string/nc_hint_enter_a_message" app:inputTextColor="@color/black" - app:inputTextSize="16sp" /> + app:inputTextSize="16sp" + app:showAttachmentButton="true" /> diff --git a/app/src/main/res/layout/controller_operations_menu.xml b/app/src/main/res/layout/controller_operations_menu.xml index da4220be1..4d516d8c5 100644 --- a/app/src/main/res/layout/controller_operations_menu.xml +++ b/app/src/main/res/layout/controller_operations_menu.xml @@ -63,13 +63,13 @@ - + android:transitionName="userAvatar.transitionTag" + apc:roundAsCircle="true" /> + android:layout_height="match_parent"> + + @@ -8,7 +27,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - + - + diff --git a/app/src/main/res/layout/dialog_standard_vertical.xml b/app/src/main/res/layout/dialog_standard_vertical.xml index aa016d746..4fb8c4edf 100644 --- a/app/src/main/res/layout/dialog_standard_vertical.xml +++ b/app/src/main/res/layout/dialog_standard_vertical.xml @@ -1,5 +1,4 @@ - - + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/rv_item_contact.xml b/app/src/main/res/layout/rv_item_contact.xml index 007cc9e54..fa8a4cf22 100644 --- a/app/src/main/res/layout/rv_item_contact.xml +++ b/app/src/main/res/layout/rv_item_contact.xml @@ -24,29 +24,38 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="@dimen/item_height" + android:layout_height="@dimen/rv_item_view_height" + android:layout_margin="@dimen/margin_between_elements" android:orientation="vertical"> - + android:layout_marginStart="@dimen/double_margin_between_elements" + android:layout_marginEnd="@dimen/margin_between_elements" + app:roundAsCircle="true" /> + + diff --git a/app/src/main/res/layout/rv_item_conversation.xml b/app/src/main/res/layout/rv_item_conversation.xml index 6ff430bc3..d3a142476 100644 --- a/app/src/main/res/layout/rv_item_conversation.xml +++ b/app/src/main/res/layout/rv_item_conversation.xml @@ -23,7 +23,8 @@ + android:layout_height="@dimen/rv_item_view_height" + android:layout_margin="8dp"> + android:layout_marginEnd="@dimen/margin_between_elements"> - + android:layout_width="@dimen/small_item_height" + android:layout_height="@dimen/small_item_height" + android:layout_margin="@dimen/margin_between_elements" + app:roundAsCircle="true" /> @@ -90,10 +88,7 @@ android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:layout_marginStart="@dimen/margin_between_elements" - android:layout_marginEnd="@dimen/activity_horizontal_margin" android:background="?android:attr/selectableItemBackground" - android:paddingStart="8dp" - android:paddingEnd="8dp" android:scaleType="center" android:src="@drawable/ic_more_horiz_black_24dp" /> diff --git a/app/src/main/res/layout/rv_item_conversation_info_participant.xml b/app/src/main/res/layout/rv_item_conversation_info_participant.xml index 961e8fa3c..fec8ee7b3 100644 --- a/app/src/main/res/layout/rv_item_conversation_info_participant.xml +++ b/app/src/main/res/layout/rv_item_conversation_info_participant.xml @@ -1,5 +1,4 @@ - - + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="@dimen/item_height" + android:orientation="vertical"> - + app:roundAsCircle="true" /> + android:visibility="gone" /> + android:visibility="gone" /> @@ -65,8 +61,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" - android:layout_marginEnd="@dimen/margin_between_elements" android:layout_marginStart="@dimen/margin_between_elements" + android:layout_marginEnd="@dimen/margin_between_elements" android:layout_toEndOf="@id/frame_layout" android:orientation="vertical"> @@ -77,7 +73,7 @@ android:ellipsize="middle" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceListItem" - tools:text="Call item text"/> + tools:text="Call item text" /> + tools:text="A week ago" /> diff --git a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml index 3db4d4480..5c9aa8fec 100644 --- a/app/src/main/res/layout/rv_item_conversation_with_last_message.xml +++ b/app/src/main/res/layout/rv_item_conversation_with_last_message.xml @@ -1,4 +1,5 @@ - + android:layout_height="@dimen/rv_item_view_height" + android:layout_margin="@dimen/double_margin_between_elements"> + android:layout_height="wrap_content"> + android:layout_width="@dimen/small_item_height" + android:layout_height="@dimen/small_item_height" + android:layout_centerVertical="true" + android:layout_marginEnd="@dimen/margin_between_elements"> - @@ -66,7 +70,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignTop="@id/dialogAvatarFrameLayout" - android:layout_marginStart="8dp" android:layout_toStartOf="@id/dialogDate" android:layout_toEndOf="@id/dialogAvatarFrameLayout" android:ellipsize="end" @@ -89,16 +92,16 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/dialogName" - android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_toEndOf="@id/dialogAvatarFrameLayout"> - - + app:roundAsCircle="true" /> diff --git a/app/src/main/res/layout/rv_item_notification_sound.xml b/app/src/main/res/layout/rv_item_notification_sound.xml index 231d6167f..5c7d30ed9 100644 --- a/app/src/main/res/layout/rv_item_notification_sound.xml +++ b/app/src/main/res/layout/rv_item_notification_sound.xml @@ -19,33 +19,39 @@ --> + android:layout_height="48dp" + android:layout_margin="@dimen/double_margin_between_elements" + android:orientation="vertical" + android:animateLayoutChanges="true"> - + android:layout_marginEnd="8dp" + app:roundAsCircle="true" /> + + diff --git a/app/src/main/res/layout/view_message_input.xml b/app/src/main/res/layout/view_message_input.xml index 0be71773c..a5feb60ba 100644 --- a/app/src/main/res/layout/view_message_input.xml +++ b/app/src/main/res/layout/view_message_input.xml @@ -25,9 +25,10 @@ + android:layout_width="36dp" + android:layout_height="36dp" + android:layout_centerVertical="true" + android:scaleType="centerInside" /> + android:layout_toEndOf="@id/attachmentButtonSpace" + android:inputType="textAutoCorrect|textMultiLine|textCapSentences" /> + android:layout_toStartOf="@id/smileyButton" /> + android:src="@drawable/ic_insert_emoticon_black_24dp" + android:tint="@color/emoji_icons" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/file_browser_path.xml b/app/src/main/res/menu/file_browser_path.xml new file mode 100644 index 000000000..67c6dfd52 --- /dev/null +++ b/app/src/main/res/menu/file_browser_path.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_share_files.xml b/app/src/main/res/menu/menu_share_files.xml new file mode 100644 index 000000000..9e7f8fcb7 --- /dev/null +++ b/app/src/main/res/menu/menu_share_files.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index bbd3e0212..ef062ddda 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,4 +1,24 @@ + + diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml index 61c61728f..6c0f8d926 100644 --- a/app/src/main/res/values-land/dimens.xml +++ b/app/src/main/res/values-land/dimens.xml @@ -1,3 +1,23 @@ + + 24dp diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index 10bcfd638..3619b0852 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -1,4 +1,24 @@ + + diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml index 63fc81644..79c9ceadb 100644 --- a/app/src/main/res/values-w820dp/dimens.xml +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -1,3 +1,23 @@ + + + #0082C9 #006AA3 @@ -15,11 +35,14 @@ #E8E8E8 #757575 #D5D5D5 + #F5F5F5 #E9FFFFFF #111111 #ECEFF1 #61000000 #15000000 + + #FAFAFA diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 3d6496af5..a466381f3 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,20 +1,45 @@ + + 16dp 72dp 24dp + 48dp - 8dp 8dp + 16dp 40dp - 80dp + 96dp @dimen/avatar_fetching_size_very_big 180dp 14sp 6dp 8dp + + 48dp + 192dp + 80dp diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index 452d245b9..bbe9272b3 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -1,4 +1,24 @@ + + HvAfHtAy/QdFYqAWFFXa1VV_Iv6ZQ1.tf5swMc^45wS_vz=Wm[oyRP5D- diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b93b220f5..824468d01 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -222,7 +222,7 @@ %1$s sent a video. %1$s sent an image. You sent a link. - You sent a GIF. + You sent a GIF. You sent an attachment. You sent an audio. You sent a video. @@ -258,5 +258,13 @@ User Guest User following a public link + This conversation is locked + + + File browser" + Select files + Back + Refresh + %1$s | Last modified: %2$s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 26cad268a..7a57ea42e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,4 +1,24 @@ - + + +