mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-22 04:55:29 +03:00
Merge pull request #3936 from nextcloud/contacts_activity
Contacts activity
This commit is contained in:
commit
eb18abef43
26 changed files with 1205 additions and 316 deletions
|
@ -51,3 +51,4 @@ ktlint_standard_import-ordering = disabled
|
|||
ktlint_standard_wrapping = enabled
|
||||
ij_kotlin_allow_trailing_comma = false
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||
ktlint_function_naming_ignore_when_annotated_with = Composable
|
||||
|
|
|
@ -2,6 +2,42 @@
|
|||
<profile version="1.0">
|
||||
<option name="myName" value="ktlint" />
|
||||
<inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RedundantSemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
</component>
|
|
@ -26,6 +26,7 @@ apply plugin: 'com.github.spotbugs'
|
|||
apply plugin: 'io.gitlab.arturbosch.detekt'
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
|
@ -45,6 +46,7 @@ android {
|
|||
flavorDimensions "default"
|
||||
renderscriptTargetApi 19
|
||||
renderscriptSupportModeEnabled true
|
||||
javaCompileOptions.annotationProcessorOptions.arguments['dagger.hilt.disableModulesHaveInstallInCheck'] = 'true'
|
||||
|
||||
productFlavors {
|
||||
// used for f-droid
|
||||
|
@ -121,6 +123,11 @@ android {
|
|||
buildFeatures {
|
||||
viewBinding true
|
||||
buildConfig = true
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.13"
|
||||
}
|
||||
|
||||
lint {
|
||||
|
@ -130,6 +137,9 @@ android {
|
|||
htmlReport true
|
||||
}
|
||||
}
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
||||
ext {
|
||||
androidxCameraVersion = "1.3.4"
|
||||
|
@ -259,6 +269,7 @@ dependencies {
|
|||
implementation "com.afollestad.material-dialogs:lifecycle:${materialDialogsVersion}"
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.11.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
|
||||
|
||||
implementation "androidx.media3:media3-exoplayer:$media3_version"
|
||||
implementation "androidx.media3:media3-ui:$media3_version"
|
||||
|
@ -280,6 +291,19 @@ dependencies {
|
|||
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
|
||||
//compose
|
||||
implementation(platform("androidx.compose:compose-bom:2024.02.01"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation 'androidx.activity:activity-compose:1.9.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.1'
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
|
||||
//tests
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.12.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.12.0'
|
||||
|
@ -307,6 +331,11 @@ dependencies {
|
|||
implementation 'com.github.nextcloud.android-common:ui:0.22.0'
|
||||
|
||||
implementation 'com.github.nextcloud-deps:android-talk-webrtc:121.6167.0'
|
||||
implementation(platform("androidx.compose:compose-bom:2024.06.00"))
|
||||
implementation("io.coil-kt:coil-compose:2.6.0")
|
||||
|
||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
}
|
||||
|
||||
tasks.register('installGitHooks', Copy) {
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
android:name=".account.WebViewLoginActivity"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity android:name=".contacts.ContactsActivityCompose"
|
||||
android:theme="@style/AppTheme"/>
|
||||
|
||||
<activity
|
||||
android:name=".account.AccountVerificationActivity"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2021 Andy Scherzinger <infoi@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.adapters.items;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.databinding.RvItemContactBinding;
|
||||
import com.nextcloud.talk.extensions.ImageViewExtensionsKt;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
import eu.davidea.flexibleadapter.items.ISectionable;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
|
||||
public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemViewHolder> implements
|
||||
ISectionable<ContactItem.ContactItemViewHolder, GenericTextHeaderItem>, IFilterable<String> {
|
||||
|
||||
private final Participant participant;
|
||||
private final User user;
|
||||
private GenericTextHeaderItem header;
|
||||
private final ViewThemeUtils viewThemeUtils;
|
||||
public boolean isOnline = true;
|
||||
|
||||
public ContactItem(Participant participant,
|
||||
User user,
|
||||
GenericTextHeaderItem genericTextHeaderItem,
|
||||
ViewThemeUtils viewThemeUtils) {
|
||||
this.participant = participant;
|
||||
this.user = user;
|
||||
this.header = genericTextHeaderItem;
|
||||
this.viewThemeUtils = viewThemeUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof ContactItem inItem) {
|
||||
return participant.getCalculatedActorType() == inItem.getModel().getCalculatedActorType() &&
|
||||
participant.getCalculatedActorId().equals(inItem.getModel().getCalculatedActorId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return participant.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the model object
|
||||
*/
|
||||
public Participant getModel() {
|
||||
return participant;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getLayoutRes() {
|
||||
return R.layout.rv_item_contact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
return new ContactItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, ContactItemViewHolder holder, int position, List payloads) {
|
||||
|
||||
if (participant.getSelected()) {
|
||||
viewThemeUtils.platform.colorImageView(holder.binding.checkedImageView);
|
||||
holder.binding.checkedImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.binding.checkedImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!isOnline) {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.medium_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarView.setAlpha(0.38f);
|
||||
} else {
|
||||
holder.binding.nameText.setTextColor(ResourcesCompat.getColor(
|
||||
holder.binding.nameText.getContext().getResources(),
|
||||
R.color.high_emphasis_text,
|
||||
null)
|
||||
);
|
||||
holder.binding.avatarView.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
holder.binding.nameText.setText(participant.getDisplayName());
|
||||
|
||||
if (adapter.hasFilter()) {
|
||||
viewThemeUtils.talk.themeAndHighlightText(holder.binding.nameText,
|
||||
participant.getDisplayName(),
|
||||
String.valueOf(adapter.getFilter(String.class)));
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(participant.getDisplayName()) &&
|
||||
(participant.getType() == Participant.ParticipantType.GUEST ||
|
||||
participant.getType() == Participant.ParticipantType.USER_FOLLOWING_LINK)) {
|
||||
holder.binding.nameText.setText(NextcloudTalkApplication
|
||||
.Companion
|
||||
.getSharedApplication()
|
||||
.getString(R.string.nc_guest));
|
||||
}
|
||||
|
||||
if (
|
||||
participant.getCalculatedActorType() == Participant.ActorType.GROUPS ||
|
||||
participant.getCalculatedActorType() == Participant.ActorType.CIRCLES) {
|
||||
|
||||
setGenericAvatar(holder, R.drawable.ic_avatar_group, R.drawable.ic_circular_group);
|
||||
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.EMAILS) {
|
||||
|
||||
setGenericAvatar(holder, R.drawable.ic_avatar_mail, R.drawable.ic_circular_mail);
|
||||
|
||||
} else if (
|
||||
participant.getCalculatedActorType() == Participant.ActorType.GUESTS ||
|
||||
participant.getType() == Participant.ParticipantType.GUEST ||
|
||||
participant.getType() == Participant.ParticipantType.GUEST_MODERATOR) {
|
||||
|
||||
String displayName;
|
||||
|
||||
if (!TextUtils.isEmpty(participant.getDisplayName())) {
|
||||
displayName = participant.getDisplayName();
|
||||
} else {
|
||||
displayName = Objects.requireNonNull(NextcloudTalkApplication.Companion.getSharedApplication())
|
||||
.getResources().getString(R.string.nc_guest);
|
||||
}
|
||||
|
||||
// absolute fallback to prevent NPE deference
|
||||
if (displayName == null) {
|
||||
displayName = "Guest";
|
||||
}
|
||||
|
||||
ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, user, displayName, true, false);
|
||||
} else if (participant.getCalculatedActorType() == Participant.ActorType.USERS) {
|
||||
ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView,
|
||||
user,
|
||||
participant.getCalculatedActorId(),
|
||||
true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setGenericAvatar(
|
||||
ContactItemViewHolder holder,
|
||||
int roundPlaceholderDrawable,
|
||||
int fallbackImageResource) {
|
||||
Object avatar;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
avatar = viewThemeUtils.talk.themePlaceholderAvatar(
|
||||
holder.binding.avatarView,
|
||||
roundPlaceholderDrawable
|
||||
);
|
||||
|
||||
} else {
|
||||
avatar = fallbackImageResource;
|
||||
}
|
||||
|
||||
ImageViewExtensionsKt.loadUserAvatar(holder.binding.avatarView, avatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(String constraint) {
|
||||
return participant.getDisplayName() != null &&
|
||||
(Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
|
||||
.matcher(participant.getDisplayName().trim())
|
||||
.find() ||
|
||||
Pattern.compile(constraint, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
|
||||
.matcher(participant.getCalculatedActorId().trim())
|
||||
.find());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericTextHeaderItem getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(GenericTextHeaderItem header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
static class ContactItemViewHolder extends FlexibleViewHolder {
|
||||
|
||||
RvItemContactBinding binding;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
ContactItemViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter);
|
||||
binding = RvItemContactBinding.bind(view);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2021 Andy Scherzinger <infoi@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.adapters.items
|
||||
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.adapters.items.ContactItem.ContactItemViewHolder
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.RvItemContactBinding
|
||||
import com.nextcloud.talk.extensions.loadUserAvatar
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
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.flexibleadapter.items.ISectionable
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import java.util.Objects
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ContactItem(
|
||||
/**
|
||||
* @return the model object
|
||||
*/
|
||||
val model: Participant,
|
||||
private val user: User,
|
||||
private var header: GenericTextHeaderItem?,
|
||||
private val viewThemeUtils: ViewThemeUtils
|
||||
) : AbstractFlexibleItem<ContactItemViewHolder?>(),
|
||||
ISectionable<ContactItemViewHolder?, GenericTextHeaderItem?>,
|
||||
IFilterable<String?> {
|
||||
var isOnline: Boolean = true
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (o is ContactItem) {
|
||||
return model.calculatedActorType == o.model.calculatedActorType &&
|
||||
model.calculatedActorId == o.model.calculatedActorId
|
||||
}
|
||||
return false
|
||||
}
|
||||
override fun hashCode(): Int {
|
||||
return model.hashCode()
|
||||
}
|
||||
|
||||
override fun filter(constraint: String?): Boolean {
|
||||
return model.displayName != null &&
|
||||
(
|
||||
Pattern.compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL)
|
||||
.matcher(model.displayName!!.trim { it <= ' ' })
|
||||
.find() ||
|
||||
Pattern.compile(constraint!!, Pattern.CASE_INSENSITIVE or Pattern.LITERAL)
|
||||
.matcher(model.calculatedActorId!!.trim { it <= ' ' })
|
||||
.find()
|
||||
)
|
||||
}
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.rv_item_contact
|
||||
}
|
||||
|
||||
override fun createViewHolder(
|
||||
view: View?,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?
|
||||
): ContactItemViewHolder {
|
||||
return ContactItemViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?,
|
||||
holder: ContactItemViewHolder?,
|
||||
position: Int,
|
||||
payloads: List<Any>?
|
||||
) {
|
||||
if (model.selected) {
|
||||
holder?.binding?.checkedImageView?.let { viewThemeUtils.platform.colorImageView(it) }
|
||||
holder?.binding?.checkedImageView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder?.binding?.checkedImageView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (!isOnline) {
|
||||
holder?.binding?.nameText?.setTextColor(
|
||||
ResourcesCompat.getColor(
|
||||
holder.binding.nameText.context.resources,
|
||||
R.color.medium_emphasis_text,
|
||||
null
|
||||
)
|
||||
)
|
||||
holder?.binding?.avatarView?.alpha = 0.38f
|
||||
} else {
|
||||
holder?.binding?.nameText?.setTextColor(
|
||||
ResourcesCompat.getColor(
|
||||
holder.binding.nameText.context.resources,
|
||||
R.color.high_emphasis_text,
|
||||
null
|
||||
)
|
||||
)
|
||||
holder?.binding?.avatarView?.alpha = 1.0f
|
||||
}
|
||||
|
||||
holder?.binding?.nameText?.text = model.displayName
|
||||
|
||||
if (adapter != null) {
|
||||
if (adapter.hasFilter()) {
|
||||
holder?.binding?.let {
|
||||
viewThemeUtils.talk.themeAndHighlightText(
|
||||
it.nameText,
|
||||
model.displayName,
|
||||
adapter.getFilter(String::class.java).toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(model.displayName) &&
|
||||
(
|
||||
model.type == Participant.ParticipantType.GUEST ||
|
||||
model.type == Participant.ParticipantType.USER_FOLLOWING_LINK
|
||||
)
|
||||
) {
|
||||
holder?.binding?.nameText?.text = sharedApplication!!.getString(R.string.nc_guest)
|
||||
}
|
||||
|
||||
if (model.calculatedActorType == Participant.ActorType.GROUPS ||
|
||||
model.calculatedActorType == Participant.ActorType.CIRCLES
|
||||
) {
|
||||
setGenericAvatar(holder!!, R.drawable.ic_avatar_group, R.drawable.ic_circular_group)
|
||||
} else if (model.calculatedActorType == Participant.ActorType.EMAILS) {
|
||||
setGenericAvatar(holder!!, R.drawable.ic_avatar_mail, R.drawable.ic_circular_mail)
|
||||
} else if (model.calculatedActorType == Participant.ActorType.GUESTS ||
|
||||
model.type == Participant.ParticipantType.GUEST || model.type == Participant.ParticipantType.GUEST_MODERATOR
|
||||
) {
|
||||
var displayName: String?
|
||||
|
||||
displayName = if (!TextUtils.isEmpty(model.displayName)) {
|
||||
model.displayName
|
||||
} else {
|
||||
Objects.requireNonNull(sharedApplication)!!.resources!!.getString(R.string.nc_guest)
|
||||
}
|
||||
|
||||
// absolute fallback to prevent NPE deference
|
||||
if (displayName == null) {
|
||||
displayName = "Guest"
|
||||
}
|
||||
|
||||
holder?.binding?.avatarView?.loadUserAvatar(user, displayName, true, false)
|
||||
} else if (model.calculatedActorType == Participant.ActorType.USERS) {
|
||||
holder?.binding?.avatarView
|
||||
?.loadUserAvatar(
|
||||
user,
|
||||
model.calculatedActorId!!,
|
||||
true,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setGenericAvatar(
|
||||
holder: ContactItemViewHolder,
|
||||
roundPlaceholderDrawable: Int,
|
||||
fallbackImageResource: Int
|
||||
) {
|
||||
val avatar = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
viewThemeUtils.talk.themePlaceholderAvatar(
|
||||
holder.binding.avatarView,
|
||||
roundPlaceholderDrawable
|
||||
)
|
||||
} else {
|
||||
fallbackImageResource
|
||||
}
|
||||
|
||||
holder.binding.avatarView.loadUserAvatar(avatar)
|
||||
}
|
||||
|
||||
override fun getHeader(): GenericTextHeaderItem? {
|
||||
return header
|
||||
}
|
||||
|
||||
override fun setHeader(p0: GenericTextHeaderItem?) {
|
||||
this.header = header
|
||||
}
|
||||
|
||||
class ContactItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter) {
|
||||
var binding: RvItemContactBinding =
|
||||
RvItemContactBinding.bind(view!!)
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <infoi@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.adapters.items;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding;
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
|
||||
public class GenericTextHeaderItem extends AbstractHeaderItem<GenericTextHeaderItem.HeaderViewHolder> {
|
||||
private static final String TAG = "GenericTextHeaderItem";
|
||||
|
||||
private final String title;
|
||||
private final ViewThemeUtils viewThemeUtils;
|
||||
|
||||
public GenericTextHeaderItem(String title, ViewThemeUtils viewThemeUtils) {
|
||||
super();
|
||||
setHidden(false);
|
||||
setSelectable(false);
|
||||
this.title = title;
|
||||
this.viewThemeUtils = viewThemeUtils;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof GenericTextHeaderItem) {
|
||||
GenericTextHeaderItem inItem = (GenericTextHeaderItem) o;
|
||||
return title.equals(inItem.getModel());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutRes() {
|
||||
return R.layout.rv_item_title_header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder, int position, List<Object> payloads) {
|
||||
if (payloads.size() > 0) {
|
||||
Log.d(TAG, "We have payloads, so ignoring!");
|
||||
} else {
|
||||
holder.binding.titleTextView.setText(title);
|
||||
viewThemeUtils.platform.colorPrimaryTextViewElement(holder.binding.titleTextView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
return new HeaderViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
static class HeaderViewHolder extends FlexibleViewHolder {
|
||||
|
||||
RvItemTitleHeaderBinding binding;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
HeaderViewHolder(View view, FlexibleAdapter adapter) {
|
||||
super(view, adapter, true);
|
||||
binding = RvItemTitleHeaderBinding.bind(view);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <infoi@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.adapters.items
|
||||
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.databinding.RvItemTitleHeaderBinding
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import java.util.Objects
|
||||
|
||||
open class GenericTextHeaderItem(title: String, viewThemeUtils: ViewThemeUtils) :
|
||||
AbstractHeaderItem<GenericTextHeaderItem.HeaderViewHolder>() {
|
||||
val model: String
|
||||
private val viewThemeUtils: ViewThemeUtils
|
||||
|
||||
init {
|
||||
isHidden = false
|
||||
isSelectable = false
|
||||
this.model = title
|
||||
this.viewThemeUtils = viewThemeUtils
|
||||
}
|
||||
|
||||
override fun equals(o: Any?): Boolean {
|
||||
if (o is GenericTextHeaderItem) {
|
||||
return model == o.model
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(model)
|
||||
}
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.rv_item_title_header
|
||||
}
|
||||
|
||||
override fun createViewHolder(
|
||||
view: View?,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?
|
||||
): HeaderViewHolder {
|
||||
return HeaderViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>?>?,
|
||||
holder: HeaderViewHolder,
|
||||
position: Int,
|
||||
payloads: List<Any>
|
||||
) {
|
||||
if (payloads.size > 0) {
|
||||
Log.d(TAG, "We have payloads, so ignoring!")
|
||||
} else {
|
||||
holder.binding.titleTextView.text = model
|
||||
viewThemeUtils.platform.colorPrimaryTextViewElement(holder.binding.titleTextView)
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter, true) {
|
||||
var binding: RvItemTitleHeaderBinding =
|
||||
RvItemTitleHeaderBinding.bind(view!!)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GenericTextHeaderItem"
|
||||
}
|
||||
}
|
42
app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Normal file
42
app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.api
|
||||
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.QueryMap
|
||||
import retrofit2.http.Url
|
||||
|
||||
interface NcApiCoroutines {
|
||||
@GET
|
||||
@JvmSuppressWildcards
|
||||
suspend fun getContactsWithSearchParam(
|
||||
@Header("Authorization") authorization: String?,
|
||||
@Url url: String?,
|
||||
@Query("shareTypes[]") listOfShareTypes: List<String>?,
|
||||
@QueryMap options: Map<String, Any>?
|
||||
): AutocompleteOverall
|
||||
|
||||
/*
|
||||
QueryMap items are as follows:
|
||||
- "roomType" : ""
|
||||
- "invite" : ""
|
||||
|
||||
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room
|
||||
*/
|
||||
@POST
|
||||
suspend fun createRoom(
|
||||
@Header("Authorization") authorization: String?,
|
||||
@Url url: String?,
|
||||
@QueryMap options: Map<String, String>?
|
||||
): RoomOverall
|
||||
}
|
|
@ -125,8 +125,10 @@ class ContactsActivity :
|
|||
|
||||
existingParticipants = ArrayList()
|
||||
if (intent.hasExtra(BundleKeys.KEY_NEW_CONVERSATION)) {
|
||||
// adding a new conversation, setting a flag.
|
||||
isNewConversationView = true
|
||||
} else if (intent.hasExtra(BundleKeys.KEY_ADD_PARTICIPANTS)) {
|
||||
// adding the participants in the conversation also opens this activity, setting a flag for it.
|
||||
isAddingParticipantsView = true
|
||||
conversationToken = intent.getStringExtra(BundleKeys.KEY_TOKEN)
|
||||
if (intent.hasExtra(BundleKeys.KEY_EXISTING_PARTICIPANTS)) {
|
||||
|
@ -258,6 +260,7 @@ class ContactsActivity :
|
|||
|
||||
private fun selectionDone() {
|
||||
if (isAddingParticipantsView) {
|
||||
// add participants in the view
|
||||
addParticipantsToConversation()
|
||||
} else {
|
||||
// if there is only 1 participant, directly add him while creating room (which can only add 'one')
|
||||
|
@ -477,9 +480,11 @@ class ContactsActivity :
|
|||
}
|
||||
|
||||
override fun onNext(responseBody: ResponseBody) {
|
||||
// getting contacts
|
||||
val newUserItemList = processAutocompleteUserList(responseBody)
|
||||
|
||||
userHeaderItems = HashMap()
|
||||
// getting the contact list from the endpoints.
|
||||
contactItems!!.addAll(newUserItemList)
|
||||
|
||||
sortUserItems(newUserItemList)
|
||||
|
@ -539,7 +544,7 @@ class ContactsActivity :
|
|||
}
|
||||
val newContactItem = ContactItem(
|
||||
participant,
|
||||
currentUser,
|
||||
currentUser!!,
|
||||
userHeaderItems[headerTitle],
|
||||
viewThemeUtils
|
||||
)
|
||||
|
@ -551,6 +556,7 @@ class ContactsActivity :
|
|||
return newUserItemList
|
||||
}
|
||||
|
||||
// this function displays the title of the contacts activity
|
||||
private fun getHeaderTitle(participant: Participant): String {
|
||||
return when {
|
||||
participant.calculatedActorType == Participant.ActorType.GROUPS -> {
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import autodagger.AutoInjector
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ContactsActivityCompose : ComponentActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
private lateinit var contactsViewModel: ContactsViewModel
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
|
||||
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
val context = LocalContext.current
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppBar(
|
||||
title = stringResource(R.string.nc_app_product_name),
|
||||
context = context,
|
||||
contactsViewModel = contactsViewModel
|
||||
)
|
||||
},
|
||||
content = {
|
||||
val uiState = contactsViewModel.contactsViewState.collectAsState()
|
||||
Column(Modifier.padding(it)) {
|
||||
ConversationCreationOptions(context = context)
|
||||
ContactsList(
|
||||
contactsUiState = uiState.value,
|
||||
contactsViewModel = contactsViewModel,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) {
|
||||
when (contactsUiState) {
|
||||
is ContactsUiState.None -> {
|
||||
}
|
||||
is ContactsUiState.Loading -> {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
is ContactsUiState.Success -> {
|
||||
val contacts = contactsUiState.contacts
|
||||
Log.d(CompanionClass.TAG, "Contacts:$contacts")
|
||||
if (contacts != null) {
|
||||
ContactsItem(contacts, contactsViewModel, context)
|
||||
}
|
||||
}
|
||||
is ContactsUiState.Error -> {
|
||||
val errorMessage = contactsUiState.message
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(text = "Error: $errorMessage", color = Color.Red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ContactsItem(contacts: List<AutocompleteUser>, contactsViewModel: ContactsViewModel, context: Context) {
|
||||
val groupedContacts: Map<String, List<AutocompleteUser>> = contacts.groupBy { contact ->
|
||||
(
|
||||
if (contact.source == "users") {
|
||||
contact.label?.first()?.uppercase()
|
||||
} else {
|
||||
contact.source?.replaceFirstChar { actorType ->
|
||||
actorType.uppercase()
|
||||
}
|
||||
}
|
||||
).toString()
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(all = 10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
groupedContacts.forEach { (initial, contactsForInitial) ->
|
||||
stickyHeader {
|
||||
Column {
|
||||
Surface(Modifier.fillParentMaxWidth()) {
|
||||
Header(initial)
|
||||
}
|
||||
HorizontalDivider(thickness = 0.1.dp, color = Color.Black)
|
||||
}
|
||||
}
|
||||
items(contactsForInitial) { contact ->
|
||||
ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Header(header: String) {
|
||||
Text(
|
||||
text = header,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Transparent)
|
||||
.padding(start = 60.dp),
|
||||
color = Color.Blue,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context) {
|
||||
val roomUiState by contactsViewModel.roomViewState.collectAsState()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
contactsViewModel.createRoom(
|
||||
CompanionClass.ROOM_TYPE_ONE_ONE,
|
||||
contact.source!!,
|
||||
contact.id!!,
|
||||
null
|
||||
)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val imageUri = contact.id?.let { contactsViewModel.getImageUri(it, true) }
|
||||
val imageRequest = ImageRequest.Builder(context)
|
||||
.data(imageUri)
|
||||
.transformations(CircleCropTransformation())
|
||||
.error(R.drawable.account_circle_96dp)
|
||||
.placeholder(R.drawable.account_circle_96dp)
|
||||
.build()
|
||||
|
||||
AsyncImage(
|
||||
model = imageRequest,
|
||||
contentDescription = stringResource(R.string.user_avatar),
|
||||
modifier = Modifier.size(width = 45.dp, height = 45.dp)
|
||||
)
|
||||
Text(modifier = Modifier.padding(16.dp), text = contact.label!!)
|
||||
}
|
||||
when (roomUiState) {
|
||||
is RoomUiState.Success -> {
|
||||
val conversation = (roomUiState as RoomUiState.Success).conversation
|
||||
val bundle = Bundle()
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation?.token)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation?.roomId)
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
context.startActivity(chatIntent)
|
||||
}
|
||||
is RoomUiState.Error -> {
|
||||
val errorMessage = (roomUiState as RoomUiState.Error).message
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(text = "Error: $errorMessage", color = Color.Red)
|
||||
}
|
||||
}
|
||||
is RoomUiState.None -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) {
|
||||
val searchQuery by contactsViewModel.searchQuery.collectAsState()
|
||||
val searchState = contactsViewModel.searchState.collectAsState()
|
||||
TopAppBar(
|
||||
title = { Text(text = title) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
(context as? Activity)?.finish()
|
||||
}) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
contactsViewModel.updateSearchState(true)
|
||||
}) {
|
||||
Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon))
|
||||
}
|
||||
}
|
||||
)
|
||||
if (searchState.value) {
|
||||
DisplaySearch(
|
||||
text = searchQuery,
|
||||
onTextChange = { searchQuery ->
|
||||
contactsViewModel.updateSearchQuery(query = searchQuery)
|
||||
contactsViewModel.getContactsFromSearchParams()
|
||||
},
|
||||
contactsViewModel = contactsViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConversationCreationOptions(context: Context) {
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.width(40.dp)
|
||||
.height(40.dp)
|
||||
.padding(8.dp),
|
||||
painter = painterResource(R.drawable.baseline_chat_bubble_outline_24),
|
||||
contentDescription = stringResource(R.string.new_conversation_creation_icon)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
text = stringResource(R.string.nc_create_new_conversation),
|
||||
maxLines = 1,
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
|
||||
.clickable {
|
||||
val intent = Intent(context, ListOpenConversationsActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.width(40.dp)
|
||||
.height(40.dp)
|
||||
.padding(8.dp),
|
||||
painter = painterResource(R.drawable.baseline_format_list_bulleted_24),
|
||||
contentDescription = stringResource(R.string.join_open_conversations_icon)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
text = stringResource(R.string.nc_join_open_conversations),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompanionClass {
|
||||
companion object {
|
||||
internal val TAG = ContactsActivityCompose::class.simpleName
|
||||
internal const val ROOM_TYPE_ONE_ONE = "1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
|
||||
interface ContactsRepository {
|
||||
suspend fun getContacts(searchQuery: String?, shareTypes: List<String>): AutocompleteOverall
|
||||
suspend fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?): RoomOverall
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import com.nextcloud.talk.api.NcApiCoroutines
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.RetrofitBucket
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ContactUtils
|
||||
|
||||
class ContactsRepositoryImpl(
|
||||
private val ncApiCoroutines: NcApiCoroutines,
|
||||
private val userManager: UserManager
|
||||
) : ContactsRepository {
|
||||
private val _currentUser = userManager.currentUser.blockingGet()
|
||||
val currentUser: User = _currentUser
|
||||
val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token)
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(_currentUser, intArrayOf(ApiUtils.API_V4, 1))
|
||||
|
||||
override suspend fun getContacts(searchQuery: String?, shareTypes: List<String>): AutocompleteOverall {
|
||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForContactsSearchFor14(
|
||||
currentUser.baseUrl!!,
|
||||
searchQuery
|
||||
)
|
||||
|
||||
val modifiedQueryMap: HashMap<String, Any> = HashMap(retrofitBucket.queryMap)
|
||||
modifiedQueryMap["limit"] = ContactUtils.MAX_CONTACT_LIMIT
|
||||
modifiedQueryMap["shareTypes[]"] = shareTypes
|
||||
val response = ncApiCoroutines.getContactsWithSearchParam(
|
||||
credentials,
|
||||
retrofitBucket.url,
|
||||
shareTypes,
|
||||
modifiedQueryMap
|
||||
)
|
||||
return response
|
||||
}
|
||||
|
||||
override suspend fun createRoom(
|
||||
roomType: String,
|
||||
sourceType: String,
|
||||
userId: String,
|
||||
conversationName: String?
|
||||
): RoomOverall {
|
||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
_currentUser.baseUrl,
|
||||
roomType,
|
||||
sourceType,
|
||||
userId,
|
||||
conversationName
|
||||
)
|
||||
val response = ncApiCoroutines.createRoom(
|
||||
credentials,
|
||||
retrofitBucket.url,
|
||||
retrofitBucket.queryMap
|
||||
)
|
||||
return response
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContactsViewModel @Inject constructor(
|
||||
private val repository: ContactsRepository,
|
||||
private val userManager: UserManager
|
||||
) : ViewModel() {
|
||||
|
||||
private val _contactsViewState = MutableStateFlow<ContactsUiState>(ContactsUiState.None)
|
||||
val contactsViewState: StateFlow<ContactsUiState> = _contactsViewState
|
||||
private val _roomViewState = MutableStateFlow<RoomUiState>(RoomUiState.None)
|
||||
val roomViewState: StateFlow<RoomUiState> = _roomViewState
|
||||
private val _currentUser = userManager.currentUser.blockingGet()
|
||||
val currentUser: User = _currentUser
|
||||
private val _searchQuery = MutableStateFlow("")
|
||||
val searchQuery: StateFlow<String> = _searchQuery
|
||||
private val shareTypes: MutableList<String> = mutableListOf(ShareType.User.shareType)
|
||||
val shareTypeList: List<String> = shareTypes
|
||||
private val _searchState = MutableStateFlow(false)
|
||||
val searchState: StateFlow<Boolean> = _searchState
|
||||
|
||||
init {
|
||||
getContactsFromSearchParams()
|
||||
}
|
||||
|
||||
fun updateSearchQuery(query: String) {
|
||||
_searchQuery.value = query
|
||||
}
|
||||
|
||||
fun updateSearchState(searchState: Boolean) {
|
||||
_searchState.value = searchState
|
||||
}
|
||||
|
||||
fun updateShareTypes(value: String) {
|
||||
shareTypes.add(value)
|
||||
}
|
||||
|
||||
fun getContactsFromSearchParams() {
|
||||
_contactsViewState.value = ContactsUiState.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val contacts = repository.getContacts(
|
||||
searchQuery.value,
|
||||
shareTypeList
|
||||
)
|
||||
val contactsList: List<AutocompleteUser>? = contacts.ocs!!.data
|
||||
_contactsViewState.value = ContactsUiState.Success(contactsList)
|
||||
} catch (exception: Exception) {
|
||||
_contactsViewState.value = ContactsUiState.Error(exception.message ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val room = repository.createRoom(
|
||||
roomType,
|
||||
sourceType,
|
||||
userId,
|
||||
conversationName
|
||||
)
|
||||
|
||||
val conversation: Conversation? = room.ocs?.data
|
||||
_roomViewState.value = RoomUiState.Success(conversation)
|
||||
} catch (exception: Exception) {
|
||||
_roomViewState.value = RoomUiState.Error(exception.message ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
|
||||
return ApiUtils.getUrlForAvatar(
|
||||
_currentUser.baseUrl,
|
||||
avatarId,
|
||||
requestBigSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ContactsUiState {
|
||||
data object None : ContactsUiState()
|
||||
data object Loading : ContactsUiState()
|
||||
data class Success(val contacts: List<AutocompleteUser>?) : ContactsUiState()
|
||||
data class Error(val message: String) : ContactsUiState()
|
||||
}
|
||||
|
||||
sealed class RoomUiState {
|
||||
data object None : RoomUiState()
|
||||
data class Success(val conversation: Conversation?) : RoomUiState()
|
||||
data class Error(val message: String) : RoomUiState()
|
||||
}
|
115
app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt
Normal file
115
app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.nextcloud.talk.R
|
||||
|
||||
@Composable
|
||||
fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewModel: ContactsViewModel) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp)
|
||||
.background(Color.White)
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
value = text,
|
||||
onValueChange = { onTextChange(it) },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.nc_search),
|
||||
color = Color.DarkGray
|
||||
)
|
||||
},
|
||||
|
||||
textStyle = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 16.sp
|
||||
),
|
||||
singleLine = true,
|
||||
leadingIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onTextChange("")
|
||||
contactsViewModel.updateSearchState(false)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back_button),
|
||||
tint = Color.Black
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
trailingIcon = {
|
||||
if (text.isNotEmpty()) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onTextChange("")
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.close_icon),
|
||||
tint = Color.Black
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Search
|
||||
),
|
||||
|
||||
keyboardActions = KeyboardActions(
|
||||
onSearch = {
|
||||
if (text.trim().isNotEmpty()) {
|
||||
keyboardController?.hide()
|
||||
} else {
|
||||
return@KeyboardActions
|
||||
}
|
||||
}
|
||||
),
|
||||
maxLines = 1,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.White,
|
||||
unfocusedContainerColor = Color.White,
|
||||
disabledContainerColor = Color.White,
|
||||
focusedTextColor = Color.Black,
|
||||
cursorColor = Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
16
app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt
Normal file
16
app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts
|
||||
|
||||
enum class ShareType(val shareType: String) {
|
||||
User("0"),
|
||||
Group("1"),
|
||||
Email(""),
|
||||
Circle(""),
|
||||
Federated("")
|
||||
}
|
|
@ -78,7 +78,7 @@ import com.nextcloud.talk.api.NcApi
|
|||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.contacts.ContactsActivity
|
||||
import com.nextcloud.talk.contacts.ContactsActivityCompose
|
||||
import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ActivityConversationsBinding
|
||||
|
@ -1072,7 +1072,7 @@ class ConversationsListActivity :
|
|||
conversation.type === Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||
|
||||
private fun showNewConversationsScreen() {
|
||||
val intent = Intent(context, ContactsActivity::class.java)
|
||||
val intent = Intent(context, ContactsActivityCompose::class.java)
|
||||
intent.putExtra(KEY_NEW_CONVERSATION, true)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package com.nextcloud.talk.dagger.modules;
|
|||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
package com.nextcloud.talk.dagger.modules
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.api.NcApiCoroutines
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.chat.data.network.NetworkChatRepositoryImpl
|
||||
import com.nextcloud.talk.contacts.ContactsRepository
|
||||
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
||||
|
@ -45,6 +48,7 @@ import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository
|
|||
import com.nextcloud.talk.shareditems.repositories.SharedItemsRepositoryImpl
|
||||
import com.nextcloud.talk.translate.repositories.TranslateRepository
|
||||
import com.nextcloud.talk.translate.repositories.TranslateRepositoryImpl
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.DateUtils
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import dagger.Module
|
||||
|
@ -150,4 +154,9 @@ class RepositoryModule {
|
|||
fun provideInvitationsRepository(ncApi: NcApi): InvitationsRepository {
|
||||
return InvitationsRepositoryImpl(ncApi)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideContactsRepository(ncApiCoroutines: NcApiCoroutines, userManager: UserManager): ContactsRepository {
|
||||
return ContactsRepositoryImpl(ncApiCoroutines, userManager)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.github.aurae.retrofit2.LoganSquareConverterFactory;
|
|||
import com.nextcloud.talk.BuildConfig;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.api.NcApiCoroutines;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.users.UserManager;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
|
@ -35,6 +36,7 @@ import java.security.UnrecoverableKeyException;
|
|||
import java.security.cert.CertificateException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
|
@ -58,6 +60,7 @@ import okhttp3.internal.tls.OkHostnameVerifier;
|
|||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
@Module(includes = DatabaseModule.class)
|
||||
public class RestModule {
|
||||
|
@ -75,6 +78,13 @@ public class RestModule {
|
|||
return retrofit.create(NcApi.class);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
NcApiCoroutines provideNcApiCoroutines(Retrofit retrofit) {
|
||||
return retrofit.create(NcApiCoroutines.class);
|
||||
}
|
||||
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
Proxy provideProxy(AppPreferences appPreferences) {
|
||||
|
|
|
@ -10,6 +10,7 @@ package com.nextcloud.talk.dagger.modules
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||
import com.nextcloud.talk.contacts.ContactsViewModel
|
||||
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
||||
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
||||
|
@ -150,4 +151,9 @@ abstract class ViewModelModule {
|
|||
@IntoMap
|
||||
@ViewModelKey(InvitationsViewModel::class)
|
||||
abstract fun invitationsViewModel(viewModel: InvitationsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ContactsViewModel::class)
|
||||
abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import android.provider.ContactsContract
|
|||
|
||||
object ContactUtils {
|
||||
|
||||
const val MAX_CONTACT_LIMIT = 50
|
||||
|
||||
fun getDisplayNameFromDeviceContact(context: Context, id: String?): String? {
|
||||
var displayName: String? = null
|
||||
val whereName =
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white"
|
||||
android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v18l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,16L6,16l-2,2L4,4h16v12z"/>
|
||||
|
||||
</vector>
|
|
@ -240,6 +240,8 @@ How to translate with transifex:
|
|||
<string name="nc_mark_as_unread">Mark as unread</string>
|
||||
<string name="nc_add_to_favorites">Add to favorites</string>
|
||||
<string name="nc_remove_from_favorites">Remove from favorites</string>
|
||||
<string name="nc_create_new_conversation">Create a new conversation</string>
|
||||
<string name="nc_join_open_conversations">Join open conversations</string>
|
||||
|
||||
<string name="added_to_favorites">Added conversation %1$s to favorites</string>
|
||||
<string name="removed_from_favorites">Removed conversation %1$s from favorites</string>
|
||||
|
@ -259,7 +261,10 @@ How to translate with transifex:
|
|||
<string name="nc_select_participants">Select participants</string>
|
||||
<string name="nc_add_participants">Add participants</string>
|
||||
<string name="nc_contacts_done">Done</string>
|
||||
|
||||
<string name="user_avatar">User avatar</string>
|
||||
<string name="back_button">Back button</string>
|
||||
<string name="new_conversation_creation_icon">New Conversation Creation Icon</string>
|
||||
<string name="join_open_conversations_icon">Join Open Conversations Icon</string>
|
||||
<!-- Permissions -->
|
||||
<string name="nc_permissions_rationale_dialog_title">Please allow permissions</string>
|
||||
<string name="nc_permissions_denied">Some permissions were denied.</string>
|
||||
|
@ -379,6 +384,7 @@ How to translate with transifex:
|
|||
<string name="openConversations">Open conversations</string>
|
||||
<string name="error_loading_chats">There was a problem loading your chats</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="close_icon">Close Icon</string>
|
||||
<string name="nc_refresh">Refresh</string>
|
||||
<string name="nc_check_your_internet">Please check your internet connection</string>
|
||||
|
||||
|
@ -681,6 +687,7 @@ How to translate with transifex:
|
|||
<string name="message_search_hint">Search …</string>
|
||||
<string name="message_search_begin_typing">Start typing to search …</string>
|
||||
<string name="message_search_begin_empty">No search results</string>
|
||||
<string name="search_icon">Search Icon</string>
|
||||
|
||||
<!-- Polls -->
|
||||
<string name="message_poll_tap_to_open">Tap to open poll</string>
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
buildscript {
|
||||
|
||||
ext {
|
||||
kotlinVersion = '2.0.0'
|
||||
kotlinVersion = '1.9.23'
|
||||
hilt_version = '2.44'
|
||||
kotlinVersion = '2.0.0'
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
|
@ -25,7 +28,8 @@ buildscript {
|
|||
classpath "org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}"
|
||||
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.19'
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6"
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:12.1.1"
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:12.1.0"
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
<trust group="com.github.nextcloud-deps" name="android-talk-webrtc" version="110.5481.0" reason="ships OS specific artifacts (win/linux) - temp global trust"/>
|
||||
<trust file=".*-sources[.]jar" regex="true"/>
|
||||
<trust file="tensorflow-lite-metadata-0.1.0-rc2.pom" reason="differing hash on every CI run - temp global trust"/>
|
||||
<trust group="com.google.dagger" />
|
||||
<trust group="org.javassist" name="javassist" version="3.26.0-GA" reason="java assist"/>
|
||||
<trust group="androidx.fragment"/>
|
||||
</trusted-artifacts>
|
||||
<ignored-keys>
|
||||
<ignored-key id="23778689FBFBE047" reason="Key couldn't be downloaded from any key server"/>
|
||||
|
@ -137,6 +140,7 @@
|
|||
<trusting group="androidx.sqlite"/>
|
||||
<trusting group="androidx.webkit"/>
|
||||
<trusting group="androidx.work"/>
|
||||
<trusting group="androidx.compose.foundation"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
||||
<trusting group="org.apache" name="apache"/>
|
||||
|
@ -219,6 +223,7 @@
|
|||
<trusted-key id="E4AC7874F3479A0F1F8ECF9960BB45F36B649F22" group="fr.dudie" name="nominatim-api" version="3.4"/>
|
||||
<trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
|
||||
<trusted-key id="E7DC75FC24FB3C8DFE8086AD3D5839A2262CBBFB" group="org.jetbrains.kotlinx"/>
|
||||
<trusted-key id="64B9B09F164AA0BF88742EB61188B69F6D6259CA" group="com.google.accompanist"/>
|
||||
<trusted-key id="E82D2EAF2E83830CE1F7F6BE571A5291E827E1C7" group="net.java" name="jvnet-parent" version="3"/>
|
||||
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
||||
<trusted-key id="EAA526B91DD83BA3E1B9636FA730529CA355A63E" group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2.1"/>
|
||||
|
@ -305,6 +310,50 @@
|
|||
<sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.exifinterface" name="exifinterface" version="1.3.6">
|
||||
<artifact name="exifinterface-1.3.6.aar">
|
||||
<sha256 value="1804105e9e05fdd8f760413bad5de498c381aa329f4f9d94c851bc891ac654c6" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="exifinterface-1.3.6.module">
|
||||
<sha256 value="5e9fd84ca3fd3b7706f6856fa4383107de8676bf7c42b7d4b8108949414d6201" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.core" name="core" version="1.1.0">
|
||||
<artifact name="core-1.1.0.pom">
|
||||
<sha256 value="dae46132cdcd46b798425f7cb78fd65890869b6d26101ccdcd43461a4f51754c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.core" name="core" version="1.3.2">
|
||||
<artifact name="core-1.3.2.pom">
|
||||
<sha256 value="afb5ea494dd083ed404cd51f580d218e37362f8ae326e893bee521290ed34920" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.test.ext" name="junit" version="1.1.5">
|
||||
<artifact name="junit-1.1.5.aar">
|
||||
<sha256 value="4307c0e60f5d701db9c59bcd9115af705113c36a9132fa3dbad58db1294e9bfd" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="junit-1.1.5.pom">
|
||||
<sha256 value="4cff0df04cae25831e821ef2f9129245783460e98d0fd67d8f6824065a134c4e" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.core" name="core-ktx" version="1.8.0">
|
||||
<artifact name="core-ktx-1.8.0.module">
|
||||
<sha256 value="a91bc3e02f209f643dd8275345a9e3003ce20d64fc0760eccf479c1709842f72" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation-experimental" version="1.3.0">
|
||||
<artifact name="annotation-experimental-1.3.0.aar">
|
||||
<sha256 value="abfd29c8556e5bd0325a9f769ab9e9d154ff4a5515c476cdd5a2a8285b1b19dc" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="annotation-experimental-1.3.0.module">
|
||||
<sha256 value="5eebeaff01d042e06dcf292abf8964ad391e4b0159f0090f16253d6045d38da0" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation-experimental" version="1.1.0-rc01">
|
||||
<artifact name="annotation-experimental-1.1.0-rc01.module">
|
||||
<sha256 value="d45ac493e84d968aabb2bea2b7744031a98cf5074447c0f3b862d600fc44b55c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation" version="1.5.0">
|
||||
<artifact name="annotation-1.5.0.jar">
|
||||
<sha256 value="261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -321,6 +370,14 @@
|
|||
<sha256 value="fbc64f5c44a7added8b6eab517cf7d70555e25153bf5d44a6ed9b0e5312f7de9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.exifinterface" name="exifinterface" version="1.3.2">
|
||||
<artifact name="exifinterface-1.3.2.aar">
|
||||
<sha256 value="8770c180103e0b8c04a07eb4c59153af639b09eca25deae9bdcdaf869d1e5b6b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="exifinterface-1.3.2.module">
|
||||
<sha256 value="10ba5b5cbea7f5c8758be4fdaec60a3545e891a1130d830a442b88cf5336a885" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.annotation" name="annotation-experimental" version="1.0.0">
|
||||
<artifact name="annotation-experimental-1.0.0.pom">
|
||||
<sha256 value="6b73ff6608f4b1d6cbab620b65708a382d0b39901cf4e6b0d16f84a1b04d7732" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
@ -342,6 +399,37 @@
|
|||
<sha256 value="9b6974a7dfe26d3c209dd63e16f8ee2461b57a091789160ca1eb492bb1bf3f84" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.activity" name="activity-compose" version="1.7.0">
|
||||
<artifact name="activity-compose-1.7.0.aar">
|
||||
<sha256 value="caa72885d1ce7979c1d6c59a8b255c6097b770780d4d4da95d56979a348646cd" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="activity-compose-1.7.0.module">
|
||||
<sha256 value="f7a29bcba338575dcf89a553cff9cfad3f140340eaf2b56fd0193244da602c0a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.compose.runtime" name="runtime" version="1.0.1">
|
||||
<artifact name="runtime-1.0.1.module">
|
||||
<sha256 value="2543a8c7edc16bde91f140286b4fd3773d7204a283a4ec99f6e5e286aa92c0c3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.compose.runtime" name="runtime-saveable" version="1.0.1">
|
||||
<artifact name="runtime-saveable-1.0.1.module">
|
||||
<sha256 value="c0d6f142542d8d74f65481ef6526d2be265f01f812a112948fcde87a458f4fb6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.compose.ui" name="ui" version="1.0.1">
|
||||
<artifact name="ui-1.0.1.aar">
|
||||
<sha256 value="1943daa4a3412861b9a2bdc1a7c8c2ff05d9b8191c1d3e56ebb223d2eb4a8526" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="ui-1.0.1.module">
|
||||
<sha256 value="57031a6ac9b60e5b56792ebf5cde6e16812ff566ed9190cbd188b00b46c13779" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.compose" name="compose-bom" version="2024.06.00">
|
||||
<artifact name="compose-bom-2024.06.00.pom">
|
||||
<sha256 value="1b391a969ff81c0bb43b3711e92d977e8bfa72457a11d8a37910a7051bdc3045" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="androidx.appcompat" name="appcompat" version="1.1.0">
|
||||
<artifact name="appcompat-1.1.0.pom">
|
||||
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
|
|
Loading…
Reference in a new issue