mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-18 19:19:33 +01:00
Contacts Activity Compose
Signed-off-by: sowjanyakch <sowjanya.kch@gmail.com>
This commit is contained in:
parent
9700c35d64
commit
bb46b6adad
@ -51,3 +51,4 @@ ktlint_standard_import-ordering = disabled
|
|||||||
ktlint_standard_wrapping = enabled
|
ktlint_standard_wrapping = enabled
|
||||||
ij_kotlin_allow_trailing_comma = false
|
ij_kotlin_allow_trailing_comma = false
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = 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">
|
<profile version="1.0">
|
||||||
<option name="myName" value="ktlint" />
|
<option name="myName" value="ktlint" />
|
||||||
<inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
|
<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" />
|
<inspection_tool class="RedundantSemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
@ -26,6 +26,7 @@ apply plugin: 'com.github.spotbugs'
|
|||||||
apply plugin: 'io.gitlab.arturbosch.detekt'
|
apply plugin: 'io.gitlab.arturbosch.detekt'
|
||||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||||
apply plugin: 'kotlinx-serialization'
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
apply plugin: 'dagger.hilt.android.plugin'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 34
|
compileSdk 34
|
||||||
@ -45,6 +46,7 @@ android {
|
|||||||
flavorDimensions "default"
|
flavorDimensions "default"
|
||||||
renderscriptTargetApi 19
|
renderscriptTargetApi 19
|
||||||
renderscriptSupportModeEnabled true
|
renderscriptSupportModeEnabled true
|
||||||
|
javaCompileOptions.annotationProcessorOptions.arguments['dagger.hilt.disableModulesHaveInstallInCheck'] = 'true'
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
// used for f-droid
|
// used for f-droid
|
||||||
@ -121,6 +123,11 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion = "1.5.13"
|
||||||
}
|
}
|
||||||
|
|
||||||
lint {
|
lint {
|
||||||
@ -130,6 +137,9 @@ android {
|
|||||||
htmlReport true
|
htmlReport true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
androidxCameraVersion = "1.3.4"
|
androidxCameraVersion = "1.3.4"
|
||||||
@ -259,6 +269,7 @@ dependencies {
|
|||||||
implementation "com.afollestad.material-dialogs:lifecycle:${materialDialogsVersion}"
|
implementation "com.afollestad.material-dialogs:lifecycle:${materialDialogsVersion}"
|
||||||
|
|
||||||
implementation 'com.google.code.gson:gson:2.11.0'
|
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-exoplayer:$media3_version"
|
||||||
implementation "androidx.media3:media3-ui:$media3_version"
|
implementation "androidx.media3:media3-ui:$media3_version"
|
||||||
@ -280,6 +291,19 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.13.1'
|
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 'junit:junit:4.13.2'
|
||||||
testImplementation 'org.mockito:mockito-core:5.12.0'
|
testImplementation 'org.mockito:mockito-core:5.12.0'
|
||||||
androidTestImplementation 'org.mockito:mockito-android: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.android-common:ui:0.22.0'
|
||||||
|
|
||||||
implementation 'com.github.nextcloud-deps:android-talk-webrtc:121.6167.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) {
|
tasks.register('installGitHooks', Copy) {
|
||||||
|
@ -126,6 +126,9 @@
|
|||||||
android:name=".account.WebViewLoginActivity"
|
android:name=".account.WebViewLoginActivity"
|
||||||
android:theme="@style/AppTheme" />
|
android:theme="@style/AppTheme" />
|
||||||
|
|
||||||
|
<activity android:name=".contacts.ContactsActivityCompose"
|
||||||
|
android:theme="@style/AppTheme"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".account.AccountVerificationActivity"
|
android:name=".account.AccountVerificationActivity"
|
||||||
android:theme="@style/AppTheme" />
|
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()
|
existingParticipants = ArrayList()
|
||||||
if (intent.hasExtra(BundleKeys.KEY_NEW_CONVERSATION)) {
|
if (intent.hasExtra(BundleKeys.KEY_NEW_CONVERSATION)) {
|
||||||
|
// adding a new conversation, setting a flag.
|
||||||
isNewConversationView = true
|
isNewConversationView = true
|
||||||
} else if (intent.hasExtra(BundleKeys.KEY_ADD_PARTICIPANTS)) {
|
} 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
|
isAddingParticipantsView = true
|
||||||
conversationToken = intent.getStringExtra(BundleKeys.KEY_TOKEN)
|
conversationToken = intent.getStringExtra(BundleKeys.KEY_TOKEN)
|
||||||
if (intent.hasExtra(BundleKeys.KEY_EXISTING_PARTICIPANTS)) {
|
if (intent.hasExtra(BundleKeys.KEY_EXISTING_PARTICIPANTS)) {
|
||||||
@ -258,6 +260,7 @@ class ContactsActivity :
|
|||||||
|
|
||||||
private fun selectionDone() {
|
private fun selectionDone() {
|
||||||
if (isAddingParticipantsView) {
|
if (isAddingParticipantsView) {
|
||||||
|
// add participants in the view
|
||||||
addParticipantsToConversation()
|
addParticipantsToConversation()
|
||||||
} else {
|
} else {
|
||||||
// if there is only 1 participant, directly add him while creating room (which can only add 'one')
|
// 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) {
|
override fun onNext(responseBody: ResponseBody) {
|
||||||
|
// getting contacts
|
||||||
val newUserItemList = processAutocompleteUserList(responseBody)
|
val newUserItemList = processAutocompleteUserList(responseBody)
|
||||||
|
|
||||||
userHeaderItems = HashMap()
|
userHeaderItems = HashMap()
|
||||||
|
// getting the contact list from the endpoints.
|
||||||
contactItems!!.addAll(newUserItemList)
|
contactItems!!.addAll(newUserItemList)
|
||||||
|
|
||||||
sortUserItems(newUserItemList)
|
sortUserItems(newUserItemList)
|
||||||
@ -539,7 +544,7 @@ class ContactsActivity :
|
|||||||
}
|
}
|
||||||
val newContactItem = ContactItem(
|
val newContactItem = ContactItem(
|
||||||
participant,
|
participant,
|
||||||
currentUser,
|
currentUser!!,
|
||||||
userHeaderItems[headerTitle],
|
userHeaderItems[headerTitle],
|
||||||
viewThemeUtils
|
viewThemeUtils
|
||||||
)
|
)
|
||||||
@ -551,6 +556,7 @@ class ContactsActivity :
|
|||||||
return newUserItemList
|
return newUserItemList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this function displays the title of the contacts activity
|
||||||
private fun getHeaderTitle(participant: Participant): String {
|
private fun getHeaderTitle(participant: Participant): String {
|
||||||
return when {
|
return when {
|
||||||
participant.calculatedActorType == Participant.ActorType.GROUPS -> {
|
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,65 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
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"] = 50
|
||||||
|
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.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
||||||
import com.nextcloud.talk.chat.ChatActivity
|
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.conversationlist.viewmodels.ConversationsListViewModel
|
||||||
import com.nextcloud.talk.data.user.model.User
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.databinding.ActivityConversationsBinding
|
import com.nextcloud.talk.databinding.ActivityConversationsBinding
|
||||||
@ -1072,7 +1072,7 @@ class ConversationsListActivity :
|
|||||||
conversation.type === Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
conversation.type === Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
|
||||||
|
|
||||||
private fun showNewConversationsScreen() {
|
private fun showNewConversationsScreen() {
|
||||||
val intent = Intent(context, ContactsActivity::class.java)
|
val intent = Intent(context, ContactsActivityCompose::class.java)
|
||||||
intent.putExtra(KEY_NEW_CONVERSATION, true)
|
intent.putExtra(KEY_NEW_CONVERSATION, true)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package com.nextcloud.talk.dagger.modules;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
@ -10,8 +10,11 @@
|
|||||||
package com.nextcloud.talk.dagger.modules
|
package com.nextcloud.talk.dagger.modules
|
||||||
|
|
||||||
import com.nextcloud.talk.api.NcApi
|
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.ChatRepository
|
||||||
import com.nextcloud.talk.chat.data.network.NetworkChatRepositoryImpl
|
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.ConversationRepository
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
||||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
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.shareditems.repositories.SharedItemsRepositoryImpl
|
||||||
import com.nextcloud.talk.translate.repositories.TranslateRepository
|
import com.nextcloud.talk.translate.repositories.TranslateRepository
|
||||||
import com.nextcloud.talk.translate.repositories.TranslateRepositoryImpl
|
import com.nextcloud.talk.translate.repositories.TranslateRepositoryImpl
|
||||||
|
import com.nextcloud.talk.users.UserManager
|
||||||
import com.nextcloud.talk.utils.DateUtils
|
import com.nextcloud.talk.utils.DateUtils
|
||||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
@ -150,4 +154,9 @@ class RepositoryModule {
|
|||||||
fun provideInvitationsRepository(ncApi: NcApi): InvitationsRepository {
|
fun provideInvitationsRepository(ncApi: NcApi): InvitationsRepository {
|
||||||
return InvitationsRepositoryImpl(ncApi)
|
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.BuildConfig;
|
||||||
import com.nextcloud.talk.R;
|
import com.nextcloud.talk.R;
|
||||||
import com.nextcloud.talk.api.NcApi;
|
import com.nextcloud.talk.api.NcApi;
|
||||||
|
import com.nextcloud.talk.api.NcApiCoroutines;
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||||
import com.nextcloud.talk.users.UserManager;
|
import com.nextcloud.talk.users.UserManager;
|
||||||
import com.nextcloud.talk.utils.ApiUtils;
|
import com.nextcloud.talk.utils.ApiUtils;
|
||||||
@ -35,6 +36,7 @@ import java.security.UnrecoverableKeyException;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.X509KeyManager;
|
import javax.net.ssl.X509KeyManager;
|
||||||
@ -58,6 +60,7 @@ import okhttp3.internal.tls.OkHostnameVerifier;
|
|||||||
import okhttp3.logging.HttpLoggingInterceptor;
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
@Module(includes = DatabaseModule.class)
|
@Module(includes = DatabaseModule.class)
|
||||||
public class RestModule {
|
public class RestModule {
|
||||||
@ -75,6 +78,13 @@ public class RestModule {
|
|||||||
return retrofit.create(NcApi.class);
|
return retrofit.create(NcApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
NcApiCoroutines provideNcApiCoroutines(Retrofit retrofit) {
|
||||||
|
return retrofit.create(NcApiCoroutines.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
Proxy provideProxy(AppPreferences appPreferences) {
|
Proxy provideProxy(AppPreferences appPreferences) {
|
||||||
|
@ -10,6 +10,7 @@ package com.nextcloud.talk.dagger.modules
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||||
|
import com.nextcloud.talk.contacts.ContactsViewModel
|
||||||
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
||||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
||||||
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
||||||
@ -150,4 +151,9 @@ abstract class ViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(InvitationsViewModel::class)
|
@ViewModelKey(InvitationsViewModel::class)
|
||||||
abstract fun invitationsViewModel(viewModel: InvitationsViewModel): ViewModel
|
abstract fun invitationsViewModel(viewModel: InvitationsViewModel): ViewModel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ViewModelKey(ContactsViewModel::class)
|
||||||
|
abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
<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_mark_as_unread">Mark as unread</string>
|
||||||
<string name="nc_add_to_favorites">Add to favorites</string>
|
<string name="nc_add_to_favorites">Add to favorites</string>
|
||||||
<string name="nc_remove_from_favorites">Remove from 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="added_to_favorites">Added conversation %1$s to favorites</string>
|
||||||
<string name="removed_from_favorites">Removed conversation %1$s from 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_select_participants">Select participants</string>
|
||||||
<string name="nc_add_participants">Add participants</string>
|
<string name="nc_add_participants">Add participants</string>
|
||||||
<string name="nc_contacts_done">Done</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 -->
|
<!-- Permissions -->
|
||||||
<string name="nc_permissions_rationale_dialog_title">Please allow permissions</string>
|
<string name="nc_permissions_rationale_dialog_title">Please allow permissions</string>
|
||||||
<string name="nc_permissions_denied">Some permissions were denied.</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="openConversations">Open conversations</string>
|
||||||
<string name="error_loading_chats">There was a problem loading your chats</string>
|
<string name="error_loading_chats">There was a problem loading your chats</string>
|
||||||
<string name="close">Close</string>
|
<string name="close">Close</string>
|
||||||
|
<string name="close_icon">Close Icon</string>
|
||||||
<string name="nc_refresh">Refresh</string>
|
<string name="nc_refresh">Refresh</string>
|
||||||
<string name="nc_check_your_internet">Please check your internet connection</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_hint">Search …</string>
|
||||||
<string name="message_search_begin_typing">Start typing to 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="message_search_begin_empty">No search results</string>
|
||||||
|
<string name="search_icon">Search Icon</string>
|
||||||
|
|
||||||
<!-- Polls -->
|
<!-- Polls -->
|
||||||
<string name="message_poll_tap_to_open">Tap to open poll</string>
|
<string name="message_poll_tap_to_open">Tap to open poll</string>
|
||||||
|
@ -10,9 +10,12 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
kotlinVersion = '1.9.23'
|
||||||
|
hilt_version = '2.44'
|
||||||
kotlinVersion = '2.0.0'
|
kotlinVersion = '2.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
@ -25,7 +28,8 @@ buildscript {
|
|||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}"
|
||||||
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.19'
|
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.19'
|
||||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6"
|
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
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// 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 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=".*-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 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>
|
</trusted-artifacts>
|
||||||
<ignored-keys>
|
<ignored-keys>
|
||||||
<ignored-key id="23778689FBFBE047" reason="Key couldn't be downloaded from any key server"/>
|
<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.sqlite"/>
|
||||||
<trusting group="androidx.webkit"/>
|
<trusting group="androidx.webkit"/>
|
||||||
<trusting group="androidx.work"/>
|
<trusting group="androidx.work"/>
|
||||||
|
<trusting group="androidx.compose.foundation"/>
|
||||||
</trusted-key>
|
</trusted-key>
|
||||||
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
||||||
<trusting group="org.apache" name="apache"/>
|
<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="E4AC7874F3479A0F1F8ECF9960BB45F36B649F22" group="fr.dudie" name="nominatim-api" version="3.4"/>
|
||||||
<trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
|
<trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
|
||||||
<trusted-key id="E7DC75FC24FB3C8DFE8086AD3D5839A2262CBBFB" group="org.jetbrains.kotlinx"/>
|
<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="E82D2EAF2E83830CE1F7F6BE571A5291E827E1C7" group="net.java" name="jvnet-parent" version="3"/>
|
||||||
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
||||||
<trusted-key id="EAA526B91DD83BA3E1B9636FA730529CA355A63E" group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2.1"/>
|
<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"/>
|
<sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.annotation" name="annotation" version="1.5.0">
|
||||||
<artifact name="annotation-1.5.0.jar">
|
<artifact name="annotation-1.5.0.jar">
|
||||||
<sha256 value="261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<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"/>
|
<sha256 value="fbc64f5c44a7added8b6eab517cf7d70555e25153bf5d44a6ed9b0e5312f7de9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.annotation" name="annotation-experimental" version="1.0.0">
|
||||||
<artifact name="annotation-experimental-1.0.0.pom">
|
<artifact name="annotation-experimental-1.0.0.pom">
|
||||||
<sha256 value="6b73ff6608f4b1d6cbab620b65708a382d0b39901cf4e6b0d16f84a1b04d7732" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<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"/>
|
<sha256 value="9b6974a7dfe26d3c209dd63e16f8ee2461b57a091789160ca1eb492bb1bf3f84" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.appcompat" name="appcompat" version="1.1.0">
|
||||||
<artifact name="appcompat-1.1.0.pom">
|
<artifact name="appcompat-1.1.0.pom">
|
||||||
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user