mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-25 14:35:40 +03:00
Add user profile, allow to edit it, if server supports it
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
parent
d38eec4a93
commit
4f0923ba0d
44 changed files with 2334 additions and 93 deletions
|
@ -62,6 +62,7 @@ android {
|
|||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
disable 'MissingTranslation'
|
||||
disable 'VectorPath'
|
||||
}
|
||||
|
||||
javaCompileOptions {
|
||||
|
@ -240,6 +241,10 @@ dependencies {
|
|||
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
|
||||
//implementation 'com.github.dhaval2404:imagepicker:1.8'
|
||||
implementation 'com.github.tobiaskaminsky:ImagePicker:extraFile-SNAPSHOT'
|
||||
implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0'
|
||||
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation 'org.mockito:mockito-core:3.0.0'
|
||||
testImplementation 'org.powermock:powermock-core:2.0.2'
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:replace="label, icon, theme, name, allowBackup">
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
*/
|
||||
package com.nextcloud.talk.api;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall;
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverall;
|
||||
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage;
|
||||
|
@ -37,14 +35,18 @@ import com.nextcloud.talk.models.json.push.PushRegistrationOverall;
|
|||
import com.nextcloud.talk.models.json.search.ContactsByNumberOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.SignalingOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import io.reactivex.Observable;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
|
@ -53,8 +55,10 @@ import retrofit2.http.FieldMap;
|
|||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Multipart;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Part;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.QueryMap;
|
||||
import retrofit2.http.Url;
|
||||
|
@ -373,4 +377,21 @@ public interface NcApi {
|
|||
@DELETE
|
||||
Observable<ChatOverallSingleMessage> deleteChatMessage(@Header("Authorization") String authorization,
|
||||
@Url String url);
|
||||
|
||||
@DELETE
|
||||
Observable<GenericOverall> deleteAvatar(@Header("Authorization") String authorization, @Url String url);
|
||||
|
||||
@Multipart
|
||||
@POST
|
||||
Observable<GenericOverall> uploadAvatar(@Header("Authorization") String authorization,
|
||||
@Url String url,
|
||||
@Part MultipartBody.Part attachment);
|
||||
|
||||
@GET
|
||||
Observable<UserProfileFieldsOverall> getEditableUserProfileFields(@Header("Authorization") String authorization,
|
||||
@Url String url);
|
||||
|
||||
@GET
|
||||
Call<ResponseBody> downloadResizedImage(@Header("Authorization") String authorization,
|
||||
@Url String url);
|
||||
}
|
||||
|
|
|
@ -43,15 +43,20 @@ import com.nextcloud.talk.utils.ApiUtils;
|
|||
import com.nextcloud.talk.utils.DateUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.DrawableUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHolder> implements IFilterable<String> {
|
||||
@Inject
|
||||
|
@ -125,6 +130,16 @@ public class BrowserFileItem extends AbstractFlexibleItem<BrowserFileItem.ViewHo
|
|||
holder.fileFavoriteImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (selectionInterface.shouldOnlySelectOneImageFile()) {
|
||||
if (browserFile.isFile && browserFile.mimeType.startsWith("image/")) {
|
||||
holder.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.selectFileCheckbox.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
holder.selectFileCheckbox.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
holder.fileIconImageView.getHierarchy().setPlaceholderImage(context.getDrawable(DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(browserFile.getMimeType())));
|
||||
|
||||
if (browserFile.isHasPreview()) {
|
||||
|
|
|
@ -23,15 +23,13 @@ package com.nextcloud.talk.components.filebrowser.controllers;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.work.Data;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationItemView;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
|
@ -43,27 +41,39 @@ import com.nextcloud.talk.components.filebrowser.operations.DavListing;
|
|||
import com.nextcloud.talk.components.filebrowser.operations.ListingAbstractClass;
|
||||
import com.nextcloud.talk.controllers.base.BaseController;
|
||||
import com.nextcloud.talk.interfaces.SelectionInterface;
|
||||
import com.nextcloud.talk.jobs.ShareOperationWorker;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.parceler.Parcel;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
import eu.davidea.fastscroller.FastScroller;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.parceler.Parcel;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class BrowserController extends BaseController implements ListingInterface,
|
||||
public abstract class BrowserController extends BaseController implements ListingInterface,
|
||||
FlexibleAdapter.OnItemClickListener, SelectionInterface {
|
||||
private final Set<String> selectedPaths;
|
||||
protected final Set<String> selectedPaths;
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
@BindView(R.id.recycler_view)
|
||||
|
@ -88,8 +98,7 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
private ListingAbstractClass listingAbstractClass;
|
||||
private BrowserType browserType;
|
||||
private String currentPath;
|
||||
private UserEntity activeUser;
|
||||
private String roomToken;
|
||||
protected UserEntity activeUser;
|
||||
|
||||
public BrowserController(Bundle args) {
|
||||
super(args);
|
||||
|
@ -97,7 +106,6 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
||||
browserType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_BROWSER_TYPE()));
|
||||
activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()));
|
||||
roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN());
|
||||
|
||||
currentPath = "/";
|
||||
if (BrowserType.DAV_BROWSER.equals(browserType)) {
|
||||
|
@ -109,6 +117,7 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
selectedPaths = Collections.synchronizedSet(new TreeSet<>());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_browser, container, false);
|
||||
|
@ -125,38 +134,10 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
prepareViews();
|
||||
}
|
||||
|
||||
private void onFileSelectionDone() {
|
||||
synchronized (selectedPaths) {
|
||||
Iterator<String> iterator = selectedPaths.iterator();
|
||||
|
||||
List<String> paths = new ArrayList<>();
|
||||
Data data;
|
||||
OneTimeWorkRequest shareWorker;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
String path = iterator.next();
|
||||
paths.add(path);
|
||||
iterator.remove();
|
||||
if (paths.size() == 10 || !iterator.hasNext()) {
|
||||
data = new Data.Builder()
|
||||
.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), activeUser.getId())
|
||||
.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomToken)
|
||||
.putStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS(), paths.toArray(new String[0]))
|
||||
.build();
|
||||
shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class)
|
||||
.setInputData(data)
|
||||
.build();
|
||||
WorkManager.getInstance().enqueue(shareWorker);
|
||||
paths = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRouter().popCurrentController();
|
||||
}
|
||||
abstract void onFileSelectionDone();
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_share_files, menu);
|
||||
filesSelectionDoneMenuItem = menu.findItem(R.id.files_selection_done);
|
||||
|
@ -315,7 +296,7 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void toggleBrowserItemSelection(String path) {
|
||||
public void toggleBrowserItemSelection(@NonNull String path) {
|
||||
if (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path)) {
|
||||
checkAndRemoveAnySelectedParents(path);
|
||||
} else {
|
||||
|
@ -327,10 +308,13 @@ public class BrowserController extends BaseController implements ListingInterfac
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isPathSelected(String path) {
|
||||
public boolean isPathSelected(@NonNull String path) {
|
||||
return (selectedPaths.contains(path) || shouldPathBeSelectedDueToParent(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract public boolean shouldOnlySelectOneImageFile();
|
||||
|
||||
@Parcel
|
||||
public enum BrowserType {
|
||||
FILE_BROWSER,
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.components.filebrowser.controllers;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.nextcloud.talk.controllers.ProfileController;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class BrowserForAvatarController extends BrowserController {
|
||||
private ProfileController controller;
|
||||
|
||||
public BrowserForAvatarController(Bundle args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public BrowserForAvatarController(Bundle args, ProfileController controller) {
|
||||
super(args);
|
||||
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onFileSelectionDone() {
|
||||
controller.handleAvatar(selectedPaths.iterator().next());
|
||||
|
||||
getRouter().popCurrentController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOnlySelectOneImageFile() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.components.filebrowser.controllers;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.nextcloud.talk.jobs.ShareOperationWorker;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.work.Data;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
public class BrowserForSharingController extends BrowserController {
|
||||
private final String roomToken;
|
||||
|
||||
public BrowserForSharingController(Bundle args) {
|
||||
super(args);
|
||||
|
||||
roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN());
|
||||
}
|
||||
|
||||
@Override
|
||||
void onFileSelectionDone() {
|
||||
synchronized (selectedPaths) {
|
||||
Iterator<String> iterator = selectedPaths.iterator();
|
||||
|
||||
List<String> paths = new ArrayList<>();
|
||||
Data data;
|
||||
OneTimeWorkRequest shareWorker;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
String path = iterator.next();
|
||||
paths.add(path);
|
||||
iterator.remove();
|
||||
if (paths.size() == 10 || !iterator.hasNext()) {
|
||||
data = new Data.Builder()
|
||||
.putLong(BundleKeys.INSTANCE.getKEY_INTERNAL_USER_ID(), activeUser.getId())
|
||||
.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), roomToken)
|
||||
.putStringArray(BundleKeys.INSTANCE.getKEY_FILE_PATHS(), paths.toArray(new String[0]))
|
||||
.build();
|
||||
shareWorker = new OneTimeWorkRequest.Builder(ShareOperationWorker.class)
|
||||
.setInputData(data)
|
||||
.build();
|
||||
WorkManager.getInstance().enqueue(shareWorker);
|
||||
paths = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRouter().popCurrentController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOnlySelectOneImageFile() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ import com.nextcloud.talk.api.NcApi
|
|||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
|
||||
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
|
||||
import com.nextcloud.talk.components.filebrowser.controllers.BrowserForSharingController
|
||||
import com.nextcloud.talk.controllers.base.BaseController
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent
|
||||
import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
||||
|
@ -667,7 +668,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
|
|||
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType))
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
router.pushController(RouterTransaction.with(BrowserController(bundle))
|
||||
router.pushController(RouterTransaction.with(BrowserForSharingController(bundle))
|
||||
.pushChangeHandler(VerticalChangeHandler())
|
||||
.popChangeHandler(VerticalChangeHandler()))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,800 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.controllers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController;
|
||||
import com.nextcloud.talk.components.filebrowser.controllers.BrowserForAvatarController;
|
||||
import com.nextcloud.talk.controllers.base.BaseController;
|
||||
import com.nextcloud.talk.models.database.UserEntity;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.Scope;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileData;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall;
|
||||
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
|
||||
import com.nextcloud.talk.ui.dialog.ScopeDialog;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.DisplayUtils;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observer;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class ProfileController extends BaseController {
|
||||
@Inject
|
||||
NcApi ncApi;
|
||||
|
||||
@Inject
|
||||
UserUtils userUtils;
|
||||
private UserEntity currentUser;
|
||||
private boolean edit = false;
|
||||
private RecyclerView recyclerView;
|
||||
private UserProfileData userInfo;
|
||||
private ArrayList<String> editableFields = new ArrayList<>();
|
||||
|
||||
public ProfileController() {
|
||||
super();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected View inflateView(@NotNull LayoutInflater inflater, @NotNull ViewGroup container) {
|
||||
return inflater.inflate(R.layout.controller_profile, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewBound(@NonNull View view) {
|
||||
super.onViewBound(view);
|
||||
|
||||
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
inflater.inflate(R.menu.menu_profile, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
menu.findItem(R.id.edit).setVisible(editableFields.size() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.edit:
|
||||
if (edit) {
|
||||
save();
|
||||
}
|
||||
|
||||
edit = !edit;
|
||||
|
||||
if (edit) {
|
||||
item.setTitle(R.string.save);
|
||||
|
||||
if (currentUser.isAvatarEndpointAvailable()) {
|
||||
// TODO later avatar can also be checked via user fields, for now it is in Talk capability
|
||||
getActivity().findViewById(R.id.avatar_buttons).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
ncApi.getEditableUserProfileFields(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
|
||||
ApiUtils.getUrlForUserFields(currentUser.getBaseUrl()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<UserProfileFieldsOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull UserProfileFieldsOverall userProfileFieldsOverall) {
|
||||
editableFields = userProfileFieldsOverall.getOcs().getData();
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
edit = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
item.setTitle(R.string.edit);
|
||||
getActivity().findViewById(R.id.avatar_buttons).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(@NonNull View view) {
|
||||
super.onAttach(view);
|
||||
|
||||
recyclerView = getActivity().findViewById(R.id.userinfo_list);
|
||||
recyclerView.setAdapter(new UserInfoAdapter(null,
|
||||
getActivity().getResources().getColor(R.color.colorPrimary),
|
||||
this));
|
||||
recyclerView.setItemViewCacheSize(20);
|
||||
|
||||
currentUser = userUtils.getCurrentUser();
|
||||
String credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken());
|
||||
|
||||
getActivity().findViewById(R.id.avatar_upload).setOnClickListener(v -> sendSelectLocalFileIntent());
|
||||
getActivity().findViewById(R.id.avatar_choose).setOnClickListener(v ->
|
||||
showBrowserScreen(BrowserController.BrowserType.DAV_BROWSER));
|
||||
|
||||
getActivity().findViewById(R.id.avatar_delete).setOnClickListener(v ->
|
||||
ncApi.deleteAvatar(credentials, ApiUtils.getUrlForTempAvatar(currentUser.getBaseUrl()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull GenericOverall genericOverall) {
|
||||
DisplayUtils.loadAvatarImage(currentUser,
|
||||
getActivity().findViewById(R.id.avatar_image));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
ViewCompat.setTransitionName(getActivity().findViewById(R.id.avatar_image), "userAvatar.transitionTag");
|
||||
|
||||
ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
|
||||
.retry(3)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<UserProfileOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull UserProfileOverall userProfileOverall) {
|
||||
userInfo = userProfileOverall.getOcs().getData();
|
||||
showUserProfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
setErrorMessageForMultiList(
|
||||
getActivity().getString(R.string.userinfo_no_info_headline),
|
||||
getActivity().getString(R.string.userinfo_error_text),
|
||||
R.drawable.ic_list_empty_error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showUserProfile() {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
((TextView) getActivity()
|
||||
.findViewById(R.id.userinfo_baseurl))
|
||||
.setText(Uri.parse(currentUser.getBaseUrl()).getHost());
|
||||
|
||||
DisplayUtils.loadAvatarImage(currentUser, getActivity().findViewById(R.id.avatar_image));
|
||||
|
||||
if (!TextUtils.isEmpty(userInfo.getDisplayName())) {
|
||||
((TextView) getActivity().findViewById(R.id.userinfo_fullName)).setText(userInfo.getDisplayName());
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(userInfo.getPhone()) &&
|
||||
TextUtils.isEmpty(userInfo.getEmail()) &&
|
||||
TextUtils.isEmpty(userInfo.getAddress()) &&
|
||||
TextUtils.isEmpty(userInfo.getTwitter()) &&
|
||||
TextUtils.isEmpty(userInfo.getWebsite())) {
|
||||
|
||||
getActivity().findViewById(R.id.userinfo_list).setVisibility(View.GONE);
|
||||
getActivity().findViewById(R.id.loading_content).setVisibility(View.GONE);
|
||||
getActivity().findViewById(R.id.emptyList).setVisibility(View.VISIBLE);
|
||||
|
||||
setErrorMessageForMultiList(
|
||||
getActivity().getString(R.string.userinfo_no_info_headline),
|
||||
getActivity().getString(R.string.userinfo_no_info_text), R.drawable.ic_user);
|
||||
} else {
|
||||
getActivity().findViewById(R.id.loading_content).setVisibility(View.VISIBLE);
|
||||
getActivity().findViewById(R.id.emptyList).setVisibility(View.GONE);
|
||||
|
||||
RecyclerView recyclerView = getActivity().findViewById(R.id.userinfo_list);
|
||||
if (recyclerView.getAdapter() instanceof UserInfoAdapter) {
|
||||
UserInfoAdapter adapter = ((UserInfoAdapter) recyclerView.getAdapter());
|
||||
adapter.setData(createUserInfoDetails(userInfo));
|
||||
}
|
||||
|
||||
getActivity().findViewById(R.id.loading_content).setVisibility(View.GONE);
|
||||
getActivity().findViewById(R.id.userinfo_list).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// show edit button
|
||||
if (currentUser.canEditScopes()) {
|
||||
ncApi.getEditableUserProfileFields(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
|
||||
ApiUtils.getUrlForUserFields(currentUser.getBaseUrl()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<UserProfileFieldsOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull UserProfileFieldsOverall userProfileFieldsOverall) {
|
||||
editableFields = userProfileFieldsOverall.getOcs().getData();
|
||||
|
||||
getActivity().invalidateOptionsMenu();
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
edit = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setErrorMessageForMultiList(String headline, String message, @DrawableRes int errorResource) {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
((TextView) getActivity().findViewById(R.id.empty_list_view_headline)).setText(headline);
|
||||
((TextView) getActivity().findViewById(R.id.empty_list_view_text)).setText(message);
|
||||
((ImageView) getActivity().findViewById(R.id.empty_list_icon)).setImageResource(errorResource);
|
||||
|
||||
getActivity().findViewById(R.id.empty_list_icon).setVisibility(View.VISIBLE);
|
||||
getActivity().findViewById(R.id.empty_list_view_text).setVisibility(View.VISIBLE);
|
||||
getActivity().findViewById(R.id.userinfo_list).setVisibility(View.GONE);
|
||||
getActivity().findViewById(R.id.loading_content).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private List<UserInfoDetailsItem> createUserInfoDetails(UserProfileData userInfo) {
|
||||
List<UserInfoDetailsItem> result = new LinkedList<>();
|
||||
|
||||
addToList(result,
|
||||
R.drawable.ic_user,
|
||||
userInfo.getDisplayName(),
|
||||
R.string.user_info_displayname,
|
||||
Field.DISPLAYNAME,
|
||||
userInfo.getDisplayNameScope());
|
||||
addToList(result,
|
||||
R.drawable.ic_phone,
|
||||
userInfo.getPhone(),
|
||||
R.string.user_info_phone,
|
||||
Field.PHONE,
|
||||
userInfo.getPhoneScope());
|
||||
addToList(result, R.drawable.ic_email, userInfo.getEmail(), R.string.user_info_email, Field.EMAIL, userInfo.getEmailScope());
|
||||
addToList(result,
|
||||
R.drawable.ic_map_marker,
|
||||
userInfo.getAddress(),
|
||||
R.string.user_info_address,
|
||||
Field.ADDRESS,
|
||||
userInfo.getAddressScope());
|
||||
addToList(result,
|
||||
R.drawable.ic_web,
|
||||
DisplayUtils.beautifyURL(userInfo.getWebsite()),
|
||||
R.string.user_info_website,
|
||||
Field.WEBSITE,
|
||||
userInfo.getWebsiteScope());
|
||||
addToList(
|
||||
result,
|
||||
R.drawable.ic_twitter,
|
||||
DisplayUtils.beautifyTwitterHandle(userInfo.getTwitter()),
|
||||
R.string.user_info_twitter,
|
||||
Field.TWITTER,
|
||||
userInfo.getTwitterScope());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addToList(List<UserInfoDetailsItem> info,
|
||||
@DrawableRes int icon,
|
||||
String text,
|
||||
@StringRes int contentDescriptionInt,
|
||||
Field field,
|
||||
Scope scope) {
|
||||
info.add(new UserInfoDetailsItem(icon, text, getResources().getString(contentDescriptionInt), field, scope));
|
||||
}
|
||||
|
||||
private void save() {
|
||||
UserInfoAdapter adapter = (UserInfoAdapter) recyclerView.getAdapter();
|
||||
|
||||
for (UserInfoDetailsItem item : adapter.mDisplayList) {
|
||||
// Text
|
||||
if (!item.text.equals(userInfo.getValueByField(item.field))) {
|
||||
String credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken());
|
||||
|
||||
ncApi.setUserData(
|
||||
credentials,
|
||||
ApiUtils.getUrlForUserData(currentUser.getBaseUrl(), currentUser.getUserId()),
|
||||
item.field.fieldName,
|
||||
item.text)
|
||||
.retry(3)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull GenericOverall userProfileOverall) {
|
||||
Log.d("ProfileController", "Successfully saved: " + item.text + " as " + item.field);
|
||||
|
||||
if (item.field == Field.DISPLAYNAME) {
|
||||
((TextView) getActivity().findViewById(R.id.userinfo_fullName)).setText(item.text);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
item.text = userInfo.getValueByField(item.field);
|
||||
Log.e("ProfileController", "Failed to saved: " + item.text + " as " + item.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scope
|
||||
if (item.scope != userInfo.getScopeByField(item.field)) {
|
||||
saveScope(item, userInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendSelectLocalFileIntent() {
|
||||
Intent intent = ImagePicker.Companion.with(getActivity())
|
||||
.galleryOnly()
|
||||
.crop()
|
||||
.cropSquare()
|
||||
.compress(1024)
|
||||
.maxResultSize(1024, 1024)
|
||||
.prepareIntent();
|
||||
|
||||
startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
private void showBrowserScreen(BrowserController.BrowserType browserType) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(
|
||||
BundleKeys.INSTANCE.getKEY_BROWSER_TYPE(),
|
||||
Parcels.wrap(BrowserController.BrowserType.class, browserType));
|
||||
bundle.putParcelable(
|
||||
BundleKeys.INSTANCE.getKEY_USER_ENTITY(),
|
||||
Parcels.wrap(UserEntity.class, currentUser));
|
||||
bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), "123");
|
||||
getRouter().pushController(RouterTransaction.with(new BrowserForAvatarController(bundle, this))
|
||||
.pushChangeHandler(new VerticalChangeHandler())
|
||||
.popChangeHandler(new VerticalChangeHandler()));
|
||||
}
|
||||
|
||||
public void handleAvatar(String remotePath) {
|
||||
String uri = currentUser.getBaseUrl() + "/index.php/apps/files/api/v1/thumbnail/512/512/" +
|
||||
Uri.encode(remotePath, "/");
|
||||
|
||||
Call<ResponseBody> downloadCall = ncApi.downloadResizedImage(
|
||||
ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
|
||||
uri);
|
||||
|
||||
downloadCall.enqueue(new Callback<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
|
||||
saveBitmapAndPassToImagePicker(BitmapFactory.decodeStream(response.body().byteStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveBitmapAndPassToImagePicker(Bitmap bitmap) {
|
||||
File file = null;
|
||||
try {
|
||||
file = File.createTempFile("avatar", "png",
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
|
||||
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (file == null) {
|
||||
// TODO exception
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = ImagePicker.Companion.with(getActivity())
|
||||
.fileOnly()
|
||||
.crop()
|
||||
.cropSquare()
|
||||
.compress(1024)
|
||||
.maxResultSize(1024, 1024)
|
||||
.prepareIntent();
|
||||
|
||||
intent.putExtra(ImagePicker.EXTRA_FILE, file);
|
||||
|
||||
startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
uploadAvatar(ImagePicker.Companion.getFile(data));
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(getActivity(), ImagePicker.Companion.getError(data), Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getActivity(), "Task Cancelled", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadAvatar(File file) {
|
||||
MultipartBody.Builder builder = new MultipartBody.Builder();
|
||||
builder.setType(MultipartBody.FORM);
|
||||
builder.addFormDataPart("files[]", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
|
||||
|
||||
final MultipartBody.Part filePart = MultipartBody.Part.createFormData("files[]", file.getName(),
|
||||
RequestBody.create(MediaType.parse("image/jpg"), file));
|
||||
|
||||
// upload file
|
||||
ncApi.uploadAvatar(
|
||||
ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
|
||||
ApiUtils.getUrlForTempAvatar(currentUser.getBaseUrl()),
|
||||
filePart)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull GenericOverall genericOverall) {
|
||||
DisplayUtils.loadAvatarImage(currentUser, getActivity().findViewById(R.id.avatar_image));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void saveScope(UserInfoDetailsItem item, UserProfileData userInfo) {
|
||||
String credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken());
|
||||
ncApi.setUserData(
|
||||
credentials,
|
||||
ApiUtils.getUrlForUserData(currentUser.getBaseUrl(), currentUser.getUserId()),
|
||||
item.field.getScopeName(),
|
||||
item.scope.getName())
|
||||
.retry(3)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<GenericOverall>() {
|
||||
@Override
|
||||
public void onSubscribe(@NotNull Disposable d) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NotNull GenericOverall userProfileOverall) {
|
||||
Log.d("ProfileController", "Successfully saved: " + item.scope + " as " + item.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NotNull Throwable e) {
|
||||
item.scope = userInfo.getScopeByField(item.field);
|
||||
Log.e("ProfileController", "Failed to saved: " + item.scope + " as " + item.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected static class UserInfoDetailsItem {
|
||||
@DrawableRes
|
||||
public int icon;
|
||||
public String text;
|
||||
public String hint;
|
||||
private Field field;
|
||||
private Scope scope;
|
||||
|
||||
public UserInfoDetailsItem(@DrawableRes int icon, String text, String hint, Field field, Scope scope) {
|
||||
this.icon = icon;
|
||||
this.text = text;
|
||||
this.hint = hint;
|
||||
this.field = field;
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UserInfoAdapter extends RecyclerView.Adapter<UserInfoAdapter.ViewHolder> {
|
||||
protected List<UserInfoDetailsItem> mDisplayList;
|
||||
@ColorInt
|
||||
protected int mTintColor;
|
||||
private final ProfileController controller;
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
@BindView(R.id.user_info_detail_container)
|
||||
protected View container;
|
||||
@BindView(R.id.icon)
|
||||
protected ImageView icon;
|
||||
@BindView(R.id.user_info_edit_text)
|
||||
protected EditText text;
|
||||
@BindView(R.id.scope)
|
||||
protected ImageView scope;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
}
|
||||
}
|
||||
|
||||
public UserInfoAdapter(List<UserInfoDetailsItem> displayList,
|
||||
@ColorInt int tintColor,
|
||||
ProfileController controller) {
|
||||
mDisplayList = displayList == null ? new LinkedList<>() : displayList;
|
||||
mTintColor = tintColor;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public void setData(List<UserInfoDetailsItem> displayList) {
|
||||
mDisplayList = displayList == null ? new LinkedList<>() : displayList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
View view = inflater.inflate(R.layout.user_info_details_table_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
UserInfoDetailsItem item = mDisplayList.get(position);
|
||||
|
||||
if (item.scope == null) {
|
||||
holder.scope.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.scope.setVisibility(View.VISIBLE);
|
||||
|
||||
switch (item.scope) {
|
||||
case PRIVATE:
|
||||
case LOCAL:
|
||||
holder.scope.setImageResource(R.drawable.ic_password);
|
||||
break;
|
||||
case FEDERATED:
|
||||
holder.scope.setImageResource(R.drawable.ic_contacts);
|
||||
break;
|
||||
case PUBLISHED:
|
||||
holder.scope.setImageResource(R.drawable.ic_link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
holder.icon.setImageResource(item.icon);
|
||||
holder.text.setText(item.text);
|
||||
holder.text.setHint(item.hint);
|
||||
holder.text.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
mDisplayList.get(holder.getAdapterPosition()).text = holder.text.getText().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
holder.icon.setContentDescription(item.hint);
|
||||
DrawableCompat.setTint(holder.icon.getDrawable(), mTintColor);
|
||||
|
||||
if (!TextUtils.isEmpty(item.text) || controller.edit) {
|
||||
holder.container.setVisibility(View.VISIBLE);
|
||||
holder.text.setTextColor(Color.BLACK);
|
||||
|
||||
if (controller.edit &&
|
||||
controller.editableFields.contains(item.field.toString().toLowerCase(Locale.ROOT))) {
|
||||
holder.text.setEnabled(true);
|
||||
holder.text.setFocusableInTouchMode(true);
|
||||
holder.text.setEnabled(true);
|
||||
holder.text.setCursorVisible(true);
|
||||
holder.text.setBackgroundTintList(ColorStateList.valueOf(mTintColor));
|
||||
holder.scope.setOnClickListener(v -> new ScopeDialog(
|
||||
controller.getActivity(),
|
||||
this,
|
||||
item.field,
|
||||
holder.getAdapterPosition()).show());
|
||||
holder.scope.setAlpha(1.0f);
|
||||
} else {
|
||||
holder.text.setEnabled(false);
|
||||
holder.text.setFocusableInTouchMode(false);
|
||||
holder.text.setEnabled(false);
|
||||
holder.text.setCursorVisible(false);
|
||||
holder.text.setBackgroundTintList(ColorStateList.valueOf(Color.TRANSPARENT));
|
||||
holder.scope.setOnClickListener(null);
|
||||
holder.scope.setAlpha(0.4f);
|
||||
}
|
||||
} else {
|
||||
holder.container.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mDisplayList.size();
|
||||
}
|
||||
|
||||
public void updateScope(int position, Scope scope) {
|
||||
mDisplayList.get(position).scope = scope;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public enum Field {
|
||||
EMAIL("email", "emailScope"),
|
||||
DISPLAYNAME("displayname", "displaynameScope"),
|
||||
PHONE("phone", "phoneScope"),
|
||||
ADDRESS("address", "addressScope"),
|
||||
WEBSITE("website", "websiteScope"),
|
||||
TWITTER("twitter", "twitterScope");
|
||||
|
||||
private final String fieldName;
|
||||
private final String scopeName;
|
||||
|
||||
Field(String fieldName, String scopeName) {
|
||||
this.fieldName = fieldName;
|
||||
this.scopeName = scopeName;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public String getScopeName() {
|
||||
return scopeName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,26 +46,16 @@ import android.widget.Button;
|
|||
import android.widget.Checkable;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import com.bluelinelabs.conductor.Controller;
|
||||
import com.bluelinelabs.conductor.RouterTransaction;
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler;
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.nextcloud.talk.BuildConfig;
|
||||
import com.nextcloud.talk.R;
|
||||
|
@ -110,6 +100,13 @@ import java.util.Objects;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.emoji.widget.EmojiTextView;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import autodagger.AutoInjector;
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
@ -139,6 +136,8 @@ public class SettingsController extends BaseController {
|
|||
MaterialStandardPreference sourceCodeButton;
|
||||
@BindView(R.id.settings_version)
|
||||
MaterialStandardPreference versionInfo;
|
||||
@BindView(R.id.avatarContainer)
|
||||
RelativeLayout avatarContainer;
|
||||
@BindView(R.id.avatar_image)
|
||||
SimpleDraweeView avatarImageView;
|
||||
@BindView(R.id.display_name_text)
|
||||
|
@ -563,7 +562,7 @@ public class SettingsController extends BaseController {
|
|||
displayNameTextView.setText(currentUser.getDisplayName());
|
||||
}
|
||||
|
||||
loadAvatarImage();
|
||||
DisplayUtils.loadAvatarImage(currentUser, avatarImageView);
|
||||
|
||||
profileQueryDisposable = ncApi.getUserProfile(credentials,
|
||||
ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()))
|
||||
|
@ -661,23 +660,12 @@ public class SettingsController extends BaseController {
|
|||
messageView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAvatarImage() {
|
||||
String avatarId;
|
||||
if (!TextUtils.isEmpty(currentUser.getUserId())) {
|
||||
avatarId = currentUser.getUserId();
|
||||
} else {
|
||||
avatarId = currentUser.getUsername();
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(avatarImageView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(currentUser.getBaseUrl(),
|
||||
avatarId, R.dimen.avatar_size_big), null))
|
||||
.build();
|
||||
avatarImageView.setController(draweeController);
|
||||
avatarContainer.setOnClickListener(v ->
|
||||
getRouter()
|
||||
.pushController((RouterTransaction.with(new ProfileController())
|
||||
.pushChangeHandler(new HorizontalChangeHandler())
|
||||
.popChangeHandler(new HorizontalChangeHandler()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
package com.nextcloud.talk.dagger.modules;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
|
|
@ -24,4 +24,6 @@ interface SelectionInterface {
|
|||
fun toggleBrowserItemSelection(path: String)
|
||||
|
||||
fun isPathSelected(path: String): Boolean
|
||||
|
||||
fun shouldOnlySelectOneImageFile(): Boolean
|
||||
}
|
||||
|
|
|
@ -222,4 +222,37 @@ public interface User extends Parcelable, Persistable, Serializable {
|
|||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// TODO later avatar can also be checked via user fields, for now it is in Talk capability
|
||||
default boolean isAvatarEndpointAvailable() {
|
||||
if (getCapabilities() != null) {
|
||||
Capabilities capabilities;
|
||||
try {
|
||||
capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class);
|
||||
return (capabilities != null &&
|
||||
capabilities.getSpreedCapability() != null &&
|
||||
capabilities.getSpreedCapability().getFeatures() != null &&
|
||||
capabilities.getSpreedCapability().getFeatures().contains("temp-user-avatar-api"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean canEditScopes() {
|
||||
if (getCapabilities() != null) {
|
||||
Capabilities capabilities;
|
||||
try {
|
||||
capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class);
|
||||
return (capabilities != null &&
|
||||
capabilities.getProvisioningCapability() != null &&
|
||||
capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() != null &&
|
||||
capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() > 1);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,14 @@ package com.nextcloud.talk.models.json.capabilities;
|
|||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import lombok.Data;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject
|
||||
|
@ -43,4 +45,7 @@ public class Capabilities {
|
|||
|
||||
@JsonField(name = "external")
|
||||
HashMap<String, List<String>> externalCapability;
|
||||
|
||||
@JsonField(name = "provisioning_api")
|
||||
ProvisioningCapability provisioningCapability;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models.json.capabilities;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject
|
||||
public class ProvisioningCapability {
|
||||
@JsonField(name = "AccountPropertyScopesVersion")
|
||||
Integer accountPropertyScopesVersion;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models.json.converters;
|
||||
|
||||
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
|
||||
import com.nextcloud.talk.models.json.userprofile.Scope;
|
||||
|
||||
public class ScopeConverter extends StringBasedTypeConverter<Scope> {
|
||||
@Override
|
||||
public Scope getFromString(String string) {
|
||||
switch (string) {
|
||||
case "v2-private":
|
||||
return Scope.PRIVATE;
|
||||
case "v2-local":
|
||||
return Scope.LOCAL;
|
||||
case "v2-federated":
|
||||
return Scope.FEDERATED;
|
||||
case "v2-published":
|
||||
return Scope.PUBLISHED;
|
||||
default:
|
||||
return Scope.PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToString(Scope scope) {
|
||||
return scope.getName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.models.json.userprofile;
|
||||
|
||||
public enum Scope {
|
||||
PRIVATE("v2-private"),
|
||||
LOCAL("v2-local"),
|
||||
FEDERATED("v2-federated"),
|
||||
PUBLISHED("v2-published");
|
||||
|
||||
private final String name;
|
||||
|
||||
Scope(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -22,9 +22,13 @@ package com.nextcloud.talk.models.json.userprofile;
|
|||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import lombok.Data;
|
||||
import com.nextcloud.talk.controllers.ProfileController;
|
||||
import com.nextcloud.talk.models.json.converters.ScopeConverter;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject()
|
||||
|
@ -32,6 +36,9 @@ public class UserProfileData {
|
|||
@JsonField(name = "display-name")
|
||||
String displayName;
|
||||
|
||||
@JsonField(name = "displaynameScope", typeConverter = ScopeConverter.class)
|
||||
Scope displayNameScope;
|
||||
|
||||
@JsonField(name = "displayname")
|
||||
String displayNameAlt;
|
||||
|
||||
|
@ -40,4 +47,69 @@ public class UserProfileData {
|
|||
|
||||
@JsonField(name = "phone")
|
||||
String phone;
|
||||
|
||||
@JsonField(name = "phoneScope", typeConverter = ScopeConverter.class)
|
||||
Scope phoneScope;
|
||||
|
||||
@JsonField(name = "email")
|
||||
String email;
|
||||
|
||||
@JsonField(name = "emailScope", typeConverter = ScopeConverter.class)
|
||||
Scope emailScope;
|
||||
|
||||
@JsonField(name = "address")
|
||||
String address;
|
||||
|
||||
@JsonField(name = "addressScope", typeConverter = ScopeConverter.class)
|
||||
Scope addressScope;
|
||||
|
||||
@JsonField(name = "twitter")
|
||||
String twitter;
|
||||
|
||||
@JsonField(name = "twitterScope", typeConverter = ScopeConverter.class)
|
||||
Scope twitterScope;
|
||||
|
||||
@JsonField(name = "website")
|
||||
String website;
|
||||
|
||||
@JsonField(name = "websiteScope", typeConverter = ScopeConverter.class)
|
||||
Scope websiteScope;
|
||||
|
||||
public String getValueByField(ProfileController.Field field) {
|
||||
switch (field) {
|
||||
case EMAIL:
|
||||
return email;
|
||||
case DISPLAYNAME:
|
||||
return displayName;
|
||||
case PHONE:
|
||||
return phone;
|
||||
case ADDRESS:
|
||||
return address;
|
||||
case WEBSITE:
|
||||
return website;
|
||||
case TWITTER:
|
||||
return twitter;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public Scope getScopeByField(ProfileController.Field field) {
|
||||
switch (field) {
|
||||
case EMAIL:
|
||||
return emailScope;
|
||||
case DISPLAYNAME:
|
||||
return displayNameScope;
|
||||
case PHONE:
|
||||
return phoneScope;
|
||||
case ADDRESS:
|
||||
return addressScope;
|
||||
case WEBSITE:
|
||||
return websiteScope;
|
||||
case TWITTER:
|
||||
return twitterScope;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.models.json.userprofile;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.nextcloud.talk.models.json.generic.GenericOCS;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject
|
||||
public class UserProfileFieldsOCS extends GenericOCS {
|
||||
@JsonField(name = "data")
|
||||
ArrayList<String> data;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.models.json.userprofile;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Parcel
|
||||
@Data
|
||||
@JsonObject
|
||||
public class UserProfileFieldsOverall {
|
||||
@JsonField(name = "ocs")
|
||||
UserProfileFieldsOCS ocs;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.controllers.ProfileController
|
||||
import com.nextcloud.talk.models.json.userprofile.Scope
|
||||
|
||||
|
||||
class ScopeDialog(con: Context,
|
||||
private val userInfoAdapter: ProfileController.UserInfoAdapter,
|
||||
private val field: ProfileController.Field,
|
||||
private val position: Int) :
|
||||
BottomSheetDialog(con) {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val view = layoutInflater.inflate(R.layout.dialog_scope, null)
|
||||
setContentView(view)
|
||||
|
||||
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
if (field == ProfileController.Field.DISPLAYNAME || field == ProfileController.Field.EMAIL) {
|
||||
findViewById<LinearLayout>(R.id.scope_private)?.visibility = View.GONE
|
||||
}
|
||||
|
||||
findViewById<LinearLayout>(R.id.scope_private)?.setOnClickListener {
|
||||
userInfoAdapter.updateScope(position, Scope.PRIVATE)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
findViewById<LinearLayout>(R.id.scope_local)?.setOnClickListener {
|
||||
userInfoAdapter.updateScope(position, Scope.LOCAL)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
findViewById<LinearLayout>(R.id.scope_federated)?.setOnClickListener {
|
||||
userInfoAdapter.updateScope(position, Scope.FEDERATED)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
findViewById<LinearLayout>(R.id.scope_published)?.setOnClickListener {
|
||||
userInfoAdapter.updateScope(position, Scope.PUBLISHED)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,9 +22,6 @@ package com.nextcloud.talk.utils;
|
|||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.nextcloud.talk.BuildConfig;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
|
@ -33,6 +30,8 @@ import com.nextcloud.talk.models.RetrofitBucket;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import okhttp3.Credentials;
|
||||
|
||||
public class ApiUtils {
|
||||
|
@ -303,4 +302,12 @@ public class ApiUtils {
|
|||
public static String getUrlForMessageDeletion(String baseUrl, String token, String messageId) {
|
||||
return baseUrl + ocsApiVersion + spreedApiVersion + "/chat/" + token + "/" + messageId;
|
||||
}
|
||||
|
||||
public static String getUrlForTempAvatar(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/apps/spreed/temp-user-avatar";
|
||||
}
|
||||
|
||||
public static String getUrlForUserFields(String baseUrl) {
|
||||
return baseUrl + ocsApiVersion + "/cloud/user/fields";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ import com.facebook.common.references.CloseableReference;
|
|||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.controller.ControllerListener;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.facebook.imagepipeline.common.RotationOptions;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
|
@ -107,6 +108,17 @@ import java.util.Map;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.XmlRes;
|
||||
import androidx.appcompat.widget.AppCompatDrawableManager;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.emoji.text.EmojiCompat;
|
||||
|
||||
public class DisplayUtils {
|
||||
|
||||
private static final String TAG = "DisplayUtils";
|
||||
|
@ -114,6 +126,10 @@ public class DisplayUtils {
|
|||
private static final int INDEX_LUMINATION = 2;
|
||||
private static final double MAX_LIGHTNESS = 0.92;
|
||||
|
||||
private static final String TWITTER_HANDLE_PREFIX = "@";
|
||||
private static final String HTTP_PROTOCOL = "http://";
|
||||
private static final String HTTPS_PROTOCOL = "https://";
|
||||
|
||||
public static void setClickableString(String string, String url, TextView textView) {
|
||||
SpannableString spannableString = new SpannableString(string);
|
||||
spannableString.setSpan(new ClickableSpan() {
|
||||
|
@ -471,4 +487,68 @@ public class DisplayUtils {
|
|||
editText.setTextSize(16);
|
||||
editText.setHintTextColor(context.getResources().getColor(R.color.fontSecondaryAppbar));
|
||||
}
|
||||
|
||||
/**
|
||||
* beautifies a given URL by removing any http/https protocol prefix.
|
||||
*
|
||||
* @param url to be beautified url
|
||||
* @return beautified url
|
||||
*/
|
||||
public static String beautifyURL(@Nullable String url) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (url.length() >= 7 && HTTP_PROTOCOL.equalsIgnoreCase(url.substring(0, 7))) {
|
||||
return url.substring(HTTP_PROTOCOL.length()).trim();
|
||||
}
|
||||
|
||||
if (url.length() >= 8 && HTTPS_PROTOCOL.equalsIgnoreCase(url.substring(0, 8))) {
|
||||
return url.substring(HTTPS_PROTOCOL.length()).trim();
|
||||
}
|
||||
|
||||
return url.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* beautifies a given twitter handle by prefixing it with an @ in case it is missing.
|
||||
*
|
||||
* @param handle to be beautified twitter handle
|
||||
* @return beautified twitter handle
|
||||
*/
|
||||
public static String beautifyTwitterHandle(@Nullable String handle) {
|
||||
if (handle != null) {
|
||||
String trimmedHandle = handle.trim();
|
||||
|
||||
if (TextUtils.isEmpty(trimmedHandle)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (trimmedHandle.startsWith(TWITTER_HANDLE_PREFIX)) {
|
||||
return trimmedHandle;
|
||||
} else {
|
||||
return TWITTER_HANDLE_PREFIX + trimmedHandle;
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadAvatarImage(UserEntity user, SimpleDraweeView avatarImageView) {
|
||||
String avatarId;
|
||||
if (!TextUtils.isEmpty(user.getUserId())) {
|
||||
avatarId = user.getUserId();
|
||||
} else {
|
||||
avatarId = user.getUsername();
|
||||
}
|
||||
|
||||
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setOldController(avatarImageView.getController())
|
||||
.setAutoPlayAnimations(true)
|
||||
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(user.getBaseUrl(),
|
||||
avatarId, R.dimen.avatar_size_big), null))
|
||||
.build();
|
||||
avatarImageView.setController(draweeController);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
9
app/src/main/res/drawable/ic_contacts.xml
Normal file
9
app/src/main/res/drawable/ic_contacts.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m9,1c-1.746,0 -3,1.43 -3,2.8 0,1.4 0.1,2.4 0.8,3.5 0.224,0.29 0.485,0.35 0.7,0.6 0.135,0.5 0.24,1 0.1,1.5 -0.436,0.153 -0.85,0.332 -1.27,0.5 -0.51,-0.273 -1.1,-0.5 -1.61,-0.7 -0.07,-0.28 -0.02,-0.487 0.05,-0.75 0.12,-0.125 0.23,-0.18 0.36,-0.3 0.37,-0.45 0.39,-1.21 0.39,-1.75 0,-0.8 -0.72,-1.4 -1.5,-1.4 -0.87,0 -1.5,0.72 -1.5,1.4h-0.02c0,0.7 0.05,1.2 0.4,1.75 0.1,0.15 0.242,0.175 0.35,0.3 0.0674,0.25 0.121,0.5 0.05,0.75 -0.64,0.223 -1.244,0.5 -1.8,0.8 -0.42,0.3 -0.233,0.182 -0.5,1.15 -0.124,0.5 1.3,0.73 2.32,0.81 -0.05,0.275 -0.12,0.64 -0.32,1.34 -0.32,1.25 4.353,1.7 6,1.7 2.43,0 6.313,-0.456 5.98,-1.7 -0.52,-1.94 -0.208,-1.71 -0.98,-2.3 -1.09,-0.654 -2.452,-1.167 -3.6,-1.6 -0.15,-0.557 -0.04,-0.97 0.1,-1.5 0.235,-0.25 0.5,-0.36 0.72,-0.6 0.69,-0.884 0.78,-2.424 0.78,-3.5 0,-1.586 -1.43,-2.8 -3,-2.8z" />
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_email.xml
Normal file
23
app/src/main/res/drawable/ic_email.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#757575" android:pathData="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_link.xml
Normal file
9
app/src/main/res/drawable/ic_link.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m7.95,0.65c-4.1,0 -7.4,3.3 -7.4,7.4s3.3,7.4 7.4,7.4 7.4,-3.3 7.4,-7.4 -3.3,-7.4 -7.4,-7.4zM8.75,1.55c1.3,0 2.4,0.8 3.5,1.3l1.8,2.5 -0.3,1.1 0.6,0.3v2.4c-0.2,0.7 -0.6,1.3 -0.9,2 -0.2,0.1 0,-0.8 -0.1,-1 0,-0.6 -0.5,-0.6 -0.9,-0.2 -0.4,0.3 -1.4,0.3 -1.5,-0.4 -0.3,-0.8 0,-1.7 0.3,-2.5l-0.6,-0.7 0.2,-1.8 -0.8,-0.9 0.2,-1 -1,-0.6c-0.2,-0.2 -0.6,-0.2 -0.7,-0.4 0.1,0 0.2,-0.1 0.2,-0.1zM6.15,1.65s0.1,0 0.1,0.1c0.4,0.2 -0.1,0.4 -0.2,0.6 -0.5,0.3 0.3,0.7 0.5,1 0.4,-0.1 0.8,-0.7 1.4,-0.5 0.7,-0.2 0.6,0.6 1.1,1 0.1,0.2 0.9,0.8 0.4,0.6 -0.5,-0.4 -1,-0.4 -1.3,0.1 -0.8,0.5 -0.3,-0.9 -0.7,-1.2 -0.6,-0.7 -0.4,0.5 -0.4,0.9 -0.4,0 -1.1,-0.3 -1.5,0.2l0.4,0.6 0.5,-0.7c0,-0.3 0.1,0.2 0.3,0.3 0.1,0.2 0.8,0.7 0.3,0.9 -0.8,0.4 -1.4,1.1 -2.1,1.7 -0.2,0.5 -0.7,0.4 -1,0 -0.7,-0.4 -0.7,0.7 -0.6,1.1l0.6,-0.4v1.1c-0.4,0.4 -0.9,-0.7 -1.3,-0.9v-1.6c0,-0.4 -0.1,-0.9 0,-1.3 0.8,-0.9 1.7,-1.9 2.2,-3h0.8c0.6,0.2 0.3,-0.7 0.5,-0.6zM4.95,9.85c0.1,0 0.2,0 0.3,0.1 0.8,0.1 1.4,0.7 2,1.1 0.5,0.5 1.6,0.3 1.7,1.2 -0.2,0.9 -1.1,1.4 -1.8,1.7 -0.2,0.1 -0.4,0.2 -0.6,0.2 -0.7,0.2 -1,-0.6 -1.2,-1.1 -0.3,-0.7 -1.1,-1.2 -1,-2.1 0,-0.4 0.2,-1 0.6,-1.1z" />
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_list_empty_error.xml
Normal file
5
app/src/main/res/drawable/ic_list_empty_error.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:alpha="0.5" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF969696" android:pathData="M13,13H11V7H13M12,17.3A1.3,1.3 0,0 1,10.7 16A1.3,1.3 0,0 1,12 14.7A1.3,1.3 0,0 1,13.3 16A1.3,1.3 0,0 1,12 17.3M15.73,3H8.27L3,8.27V15.73L8.27,21H15.73L21,15.73V8.27L15.73,3Z"/>
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_map_marker.xml
Normal file
23
app/src/main/res/drawable/ic_map_marker.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#757575" android:pathData="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_password.xml
Normal file
9
app/src/main/res/drawable/ic_password.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m8,1c-2.319,0 -3.967,1.8644 -4,4v2.5h-1.5v7.5h11v-7.5h-1.5v-2.5c0,-2.27 -1.8,-3.9735 -4,-4zM8,3c1.25,0 2,0.963 2,2v2.5h-4v-2.5c0,-1.174 0.747,-2 2,-2z" />
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_phone.xml
Normal file
23
app/src/main/res/drawable/ic_phone.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#757575" android:pathData="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z" />
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_twitter.xml
Normal file
23
app/src/main/res/drawable/ic_twitter.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#757575" android:pathData="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
|
||||
</vector>
|
25
app/src/main/res/drawable/ic_user.xml
Normal file
25
app/src/main/res/drawable/ic_user.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2020 Nextcloud.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Icon provided by Android Material Library in Apache License 2.0
|
||||
-->
|
||||
<vector android:height="24dp" android:tint="#666666"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_web.xml
Normal file
23
app/src/main/res/drawable/ic_web.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2018 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#757575" android:pathData="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</vector>
|
5
app/src/main/res/drawable/round_corner.xml
Normal file
5
app/src/main/res/drawable/round_corner.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#ededed" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
25
app/src/main/res/drawable/trashbin.xml
Normal file
25
app/src/main/res/drawable/trashbin.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2020 Nextcloud.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Icon provided by Android Material Library in Apache License 2.0
|
||||
-->
|
||||
<vector android:height="24dp" android:tint="#757575"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
12
app/src/main/res/drawable/upload.xml
Normal file
12
app/src/main/res/drawable/upload.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#757575"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M 9,16 V 10 H 5 l 7,-7 7,7 h -4 v 6 H 9 m -4,4 v -2 h 14 v 2 z"/>
|
||||
</vector>
|
219
app/src/main/res/layout/controller_profile.xml
Normal file
219
app/src/main/res/layout/controller_profile.xml
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!-- Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2017 Andy Scherzinger
|
||||
Copyright (C) 2017 Nextcloud
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/bg_default"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/avatarContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:transitionName="userAvatar.transitionTag"
|
||||
app:roundAsCircle="true" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/userinfo_fullName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/avatar_image"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/margin_between_elements"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/nc_incoming_text_default"
|
||||
tools:text="John Doe" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/userinfo_baseurl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/userinfo_fullName"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_margin="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
android:textColor="@color/nc_incoming_text_default"
|
||||
tools:text="john@nextcloud.com" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/avatar_buttons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/userinfo_baseurl"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/avatar_upload"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="@dimen/standard_half_margin"
|
||||
android:layout_marginRight="@dimen/standard_half_margin"
|
||||
android:tint="@color/black"
|
||||
android:src="@drawable/upload"
|
||||
android:background="@drawable/round_corner"
|
||||
android:contentDescription="@string/upload_new_avatar_from_device" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/avatar_choose"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="@dimen/standard_half_margin"
|
||||
android:layout_marginRight="@dimen/standard_half_margin"
|
||||
android:src="@drawable/ic_mimetype_folder"
|
||||
android:background="@drawable/round_corner"
|
||||
android:tint="@color/colorPrimary"
|
||||
android:contentDescription="@string/choose_avatar_from_cloud" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/avatar_delete"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginLeft="@dimen/standard_half_margin"
|
||||
android:layout_marginRight="@dimen/standard_half_margin"
|
||||
android:src="@drawable/trashbin"
|
||||
android:tint="@color/black"
|
||||
android:background="@drawable/round_corner"
|
||||
android:contentDescription="@string/delete_avatar" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/userinfo_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/user_info_details_table_item"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emptyList"
|
||||
layout="@layout/empty_list" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/loading_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/iconized_single_line_item_layout_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
app:corners="100" />
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/iconized_single_line_item_layout_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
app:corners="100" />
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/iconized_single_line_item_layout_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
app:corners="100" />
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/iconized_single_line_item_layout_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
app:corners="100" />
|
||||
|
||||
<com.elyeproj.loaderviewlibrary.LoaderTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -43,6 +43,7 @@
|
|||
android:animateLayoutChanges="true">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/avatarContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
|
184
app/src/main/res/layout/dialog_scope.xml
Normal file
184
app/src/main/res/layout/dialog_scope.xml
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Tobias Kaminsky
|
||||
~ Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scope_private"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/lock_symbol"
|
||||
app:srcCompat="@drawable/ic_password" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_private_title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_private_description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scope_local"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/lock_symbol"
|
||||
app:srcCompat="@drawable/ic_password" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_local_title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_local_description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scope_federated"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/lock_symbol"
|
||||
app:srcCompat="@drawable/ic_contacts" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_federated_title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_federated_description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scope_published"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/lock_symbol"
|
||||
app:srcCompat="@drawable/ic_link" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_published_title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_half_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding"
|
||||
android:text="@string/scope_published_description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
64
app/src/main/res/layout/empty_list.xml
Normal file
64
app/src/main/res/layout/empty_list.xml
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2016 Andy Scherzinger
|
||||
Copyright (C) 2016 Nextcloud.
|
||||
Copyright (C) 2021 Tobias Kaminsky
|
||||
Copyright (C) 2021 Nextcloud.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/empty_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="@dimen/standard_margin"
|
||||
android:gravity="center_vertical|center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/standard_double_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/empty_list_icon"
|
||||
android:layout_width="@dimen/empty_list_icon_layout_width"
|
||||
android:layout_height="@dimen/empty_list_icon_layout_height"
|
||||
android:contentDescription="@string/file_list_folder"
|
||||
android:src="@drawable/ic_user"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_list_view_headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="2"
|
||||
android:paddingTop="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:text="@string/file_list_loading"
|
||||
android:textSize="26sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/empty_list_view_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:paddingTop="@dimen/standard_half_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding"
|
||||
android:text=""
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
70
app/src/main/res/layout/user_info_details_table_item.xml
Normal file
70
app/src/main/res/layout/user_info_details_table_item.xml
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2018 Andy Scherzinger
|
||||
Copyright (C) 2018 Nextcloud
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/user_info_detail_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/iconized_single_line_item_layout_height"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_height="@dimen/iconized_single_line_item_icon_size"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:contentDescription="@string/account_icon"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_phone" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/user_info_edit_text"
|
||||
android:layout_width="259dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:autofillHints="none"
|
||||
android:ellipsize="end"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/scope"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="+49 123 456 789 12"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/scope"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="@dimen/user_info_icon_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/standard_double_margin"
|
||||
android:contentDescription="@string/scope_toggle"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/user_info_edit_text"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_link" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
28
app/src/main/res/menu/menu_profile.xml
Normal file
28
app/src/main/res/menu/menu_profile.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Nextcloud Android Talk application
|
||||
|
||||
@author Tobias Kaminsky
|
||||
Copyright (C) 2021 Tobias Kaminsky
|
||||
Copyright (C) 2021 Nextcloud GmbH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/edit"
|
||||
android:title="@string/edit"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
|
@ -44,4 +44,16 @@
|
|||
<dimen name="large_preview_dimension">80dp</dimen>
|
||||
<dimen name="corner_radius">16dp</dimen>
|
||||
<dimen name="button_corner_radius">24dp</dimen>
|
||||
<dimen name="standard_margin">16dp</dimen>
|
||||
<dimen name="two_line_primary_text_size">16sp</dimen>
|
||||
<dimen name="drawer_header_subtext">12sp</dimen>
|
||||
<dimen name="iconized_single_line_item_layout_height">56dp</dimen>
|
||||
<dimen name="iconized_single_line_item_icon_size">24dp</dimen>
|
||||
<dimen name="user_info_icon_horizontal_margin">24dp</dimen>
|
||||
<dimen name="standard_double_margin">32dp</dimen>
|
||||
<dimen name="empty_list_icon_layout_width">72dp</dimen>
|
||||
<dimen name="empty_list_icon_layout_height">72dp</dimen>
|
||||
<dimen name="standard_padding">16dp</dimen>
|
||||
<dimen name="standard_half_padding">8dp</dimen>
|
||||
<dimen name="standard_half_margin">8dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -356,7 +356,36 @@
|
|||
<string name="no_phone_book_integration_due_to_permissions">No phone book integration due to missing permissions</string>
|
||||
<string name="nc_phone_book_integration_chat_via">Chat via %s</string>
|
||||
<string name="nc_phone_book_integration_account_not_found">Account not found</string>
|
||||
|
||||
|
||||
<string name="avatar">Avatar</string>
|
||||
<string name="account_icon">Account icon</string>
|
||||
<string name="userinfo_no_info_headline">No personal info set</string>
|
||||
<string name="userinfo_no_info_text">Add name, picture and contact details on your profile page.</string>
|
||||
<string name="userinfo_error_text">Failed to retrieve personal user information.</string>
|
||||
<string name="user_info_phone">Phone number</string>
|
||||
<string name="user_info_email">E-mail</string>
|
||||
<string name="user_info_address">Address</string>
|
||||
<string name="user_info_website">Website</string>
|
||||
<string name="user_info_twitter">Twitter</string>
|
||||
<string name="user_info_displayname">Full name</string>
|
||||
<string name="file_list_folder">folder</string>
|
||||
<string name="file_list_loading">Loading…</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="upload_new_avatar_from_device">Upload new avatar from device</string>
|
||||
<string name="choose_avatar_from_cloud">Choose avatar from cloud</string>
|
||||
<string name="delete_avatar">Delete avatar</string>
|
||||
<string name="scope_private_title">Private</string>
|
||||
<string name="scope_private_description">Don\'t show via public link</string>
|
||||
<string name="lock_symbol">Lock symbol</string>
|
||||
<string name="scope_local_title">Local</string>
|
||||
<string name="scope_local_description">Don\'t synchronize to servers</string>
|
||||
<string name="scope_federated_title">Federated</string>
|
||||
<string name="scope_federated_description">Only synchronize to trusted servers</string>
|
||||
<string name="scope_published_title">Published</string>
|
||||
<string name="scope_published_description">Synchronize to trusted serves and the global and public address book</string>
|
||||
<string name="scope_toggle">Scope toggle</string>
|
||||
|
||||
<!-- App Bar -->
|
||||
<string name="appbar_search_in">Search in %s</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue