Merge pull request #1542 from nextcloud/feature/306/directCamUpload

Adding upload from camera action
This commit is contained in:
Andy Scherzinger 2021-11-12 12:19:06 +01:00 committed by GitHub
commit 39d0052fb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 1602 additions and 130 deletions

View file

@ -187,6 +187,12 @@ dependencies {
ktlint "com.pinterest:ktlint:0.43.0"
implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'androidx.camera:camera-core:1.0.2'
implementation 'androidx.camera:camera-camera2:1.0.2'
implementation 'androidx.camera:camera-lifecycle:1.0.2'
implementation 'androidx.camera:camera-view:1.0.0-alpha28'
implementation "androidx.exifinterface:exifinterface:1.3.3"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.biometric:biometric:1.1.0'

View file

@ -160,6 +160,11 @@
android:configChanges="orientation|keyboardHidden|screenSize">
</activity>
<activity
android:name=".activities.TakePhotoActivity"
android:theme="@style/TakePhotoTheme"
android:windowSoftInputMode="stateHidden" />
<receiver android:name=".receivers.PackageReplacedReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />

View file

@ -0,0 +1,409 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Stefan Niedermann
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Stefan Niedermann <info@niedermann.it>
*
* 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.activities;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.OrientationEventListener;
import android.view.ScaleGestureDetector;
import android.view.Surface;
import android.view.View;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import com.nextcloud.talk.R;
import com.nextcloud.talk.databinding.ActivityTakePictureBinding;
import com.nextcloud.talk.models.TakePictureViewModel;
import com.nextcloud.talk.utils.BitmapShrinker;
import com.nextcloud.talk.utils.FileUtils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.exifinterface.media.ExifInterface;
import androidx.lifecycle.ViewModelProvider;
public class TakePhotoActivity extends AppCompatActivity {
private static final String TAG = TakePhotoActivity.class.getSimpleName();
private static final float MAX_SCALE = 6.0f;
private static final float MEDIUM_SCALE = 2.45f;
private ActivityTakePictureBinding binding;
private TakePictureViewModel viewModel;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private OrientationEventListener orientationEventListener;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.ROOT);
private Camera camera;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityTakePictureBinding.inflate(getLayoutInflater());
viewModel = new ViewModelProvider(this).get(TakePictureViewModel.class);
setContentView(binding.getRoot());
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
final ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
camera = cameraProvider.bindToLifecycle(
this,
viewModel.getCameraSelector(),
getImageCapture(
viewModel.isCropEnabled().getValue(), viewModel.isLowResolutionEnabled().getValue()),
getPreview(viewModel.isCropEnabled().getValue()));
viewModel.getTorchToggleButtonImageResource()
.observe(
this,
res -> binding.toggleTorch.setIcon(ContextCompat.getDrawable(this, res)));
viewModel.isTorchEnabled()
.observe(
this,
enabled -> camera.getCameraControl().enableTorch(viewModel.isTorchEnabled().getValue()));
binding.toggleTorch.setOnClickListener((v) -> viewModel.toggleTorchEnabled());
viewModel.getCropToggleButtonImageResource()
.observe(
this,
res -> binding.toggleCrop.setIcon(ContextCompat.getDrawable(this, res)));
viewModel.isCropEnabled()
.observe(
this,
enabled -> {
cameraProvider.unbindAll();
camera = cameraProvider.bindToLifecycle(
this,
viewModel.getCameraSelector(),
getImageCapture(
viewModel.isCropEnabled().getValue(), viewModel.isLowResolutionEnabled().getValue()),
getPreview(viewModel.isCropEnabled().getValue()));
camera.getCameraControl().enableTorch(viewModel.isTorchEnabled().getValue());
});
binding.toggleCrop.setOnClickListener((v) -> viewModel.toggleCropEnabled());
viewModel.getLowResolutionToggleButtonImageResource()
.observe(
this,
res -> binding.toggleLowres.setIcon(ContextCompat.getDrawable(this, res)));
viewModel.isLowResolutionEnabled()
.observe(
this,
enabled -> {
cameraProvider.unbindAll();
camera = cameraProvider.bindToLifecycle(
this,
viewModel.getCameraSelector(),
getImageCapture(
viewModel.isCropEnabled().getValue(), viewModel.isLowResolutionEnabled().getValue()),
getPreview(viewModel.isCropEnabled().getValue()));
camera.getCameraControl().enableTorch(viewModel.isTorchEnabled().getValue());
});
binding.toggleLowres.setOnClickListener((v) -> viewModel.toggleLowResolutionEnabled());
binding.switchCamera.setOnClickListener((v) -> {
viewModel.toggleCameraSelector();
cameraProvider.unbindAll();
camera = cameraProvider.bindToLifecycle(
this,
viewModel.getCameraSelector(),
getImageCapture(
viewModel.isCropEnabled().getValue(), viewModel.isLowResolutionEnabled().getValue()),
getPreview(viewModel.isCropEnabled().getValue()));
});
binding.retake.setOnClickListener((v) -> {
Uri uri = (Uri) binding.photoPreview.getTag();
File photoFile = new File(uri.getPath());
if (!photoFile.delete()) {
Log.w(TAG, "Error deleting temp camera image");
}
binding.takePhoto.setEnabled(true);
binding.photoPreview.setTag(null);
showCameraElements();
});
binding.send.setOnClickListener((v) -> {
Uri uri = (Uri) binding.photoPreview.getTag();
setResult(RESULT_OK, new Intent().setDataAndType(uri, "image/jpeg"));
binding.photoPreview.setTag(null);
finish();
});
ScaleGestureDetector mDetector =
new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener(){
@Override
public boolean onScale(ScaleGestureDetector detector){
float ratio = camera.getCameraInfo().getZoomState().getValue().getZoomRatio();
float delta = detector.getScaleFactor();
camera.getCameraControl().setZoomRatio(ratio * delta);
return true;
}
});
binding.preview.setOnTouchListener((v, event) -> {
v.performClick();
mDetector.onTouchEvent(event);
return true;
});
// Enable enlarging the image more than default 3x maximumScale.
// Medium scale adapted to make double-tap behaviour more consistent.
binding.photoPreview.setMaximumScale(MAX_SCALE);
binding.photoPreview.setMediumScale(MEDIUM_SCALE);
} catch (IllegalArgumentException | ExecutionException | InterruptedException e) {
Log.e(TAG, "Error taking picture", e);
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
finish();
}
}, ContextCompat.getMainExecutor(this));
}
@Override
public void onBackPressed() {
Uri uri = (Uri) binding.photoPreview.getTag();
if (uri != null) {
File photoFile = new File(uri.getPath());
if (!photoFile.delete()) {
Log.w(TAG, "Error deleting temp camera image");
}
binding.photoPreview.setTag(null);
}
super.onBackPressed();
}
private void showCameraElements() {
binding.send.setVisibility(View.GONE);
binding.retake.setVisibility(View.GONE);
binding.photoPreview.setVisibility(View.INVISIBLE);
binding.preview.setVisibility(View.VISIBLE);
binding.takePhoto.setVisibility(View.VISIBLE);
binding.switchCamera.setVisibility(View.VISIBLE);
binding.toggleTorch.setVisibility(View.VISIBLE);
binding.toggleCrop.setVisibility(View.VISIBLE);
binding.toggleLowres.setVisibility(View.VISIBLE);
}
private void showPictureProcessingElements() {
binding.preview.setVisibility(View.INVISIBLE);
binding.takePhoto.setVisibility(View.GONE);
binding.switchCamera.setVisibility(View.GONE);
binding.toggleTorch.setVisibility(View.GONE);
binding.toggleCrop.setVisibility(View.GONE);
binding.toggleLowres.setVisibility(View.GONE);
binding.send.setVisibility(View.VISIBLE);
binding.retake.setVisibility(View.VISIBLE);
binding.photoPreview.setVisibility(View.VISIBLE);
}
private ImageCapture getImageCapture(Boolean crop, Boolean lowres) {
final ImageCapture imageCapture;
if (lowres) imageCapture = new ImageCapture.Builder()
.setTargetResolution(new Size(crop ? 1080 : 1440, 1920)).build();
else imageCapture = new ImageCapture.Builder()
.setTargetAspectRatio(crop ? AspectRatio.RATIO_16_9 : AspectRatio.RATIO_4_3).build();
orientationEventListener = new OrientationEventListener(this) {
@Override
public void onOrientationChanged(int orientation) {
int rotation;
// Monitors orientation values to determine the target rotation value
if (orientation >= 45 && orientation < 135) {
rotation = Surface.ROTATION_270;
} else if (orientation >= 135 && orientation < 225) {
rotation = Surface.ROTATION_180;
} else if (orientation >= 225 && orientation < 315) {
rotation = Surface.ROTATION_90;
} else {
rotation = Surface.ROTATION_0;
}
imageCapture.setTargetRotation(rotation);
}
};
orientationEventListener.enable();
binding.takePhoto.setOnClickListener((v) -> {
binding.takePhoto.setEnabled(false);
final String photoFileName = dateFormat.format(new Date()) + ".jpg";
try {
final File photoFile = FileUtils.getTempCacheFile(this, "photos/" + photoFileName);
final ImageCapture.OutputFileOptions options =
new ImageCapture.OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(
options,
ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
setPreviewImage(photoFile);
showPictureProcessingElements();
}
@Override
public void onError(@NonNull ImageCaptureException e) {
Log.e(TAG, "Error", e);
if (!photoFile.delete()) {
Log.w(TAG, "Deleting picture failed");
}
binding.takePhoto.setEnabled(true);
}
});
} catch (Exception e) {
Toast.makeText(this, R.string.take_photo_error_deleting_picture, Toast.LENGTH_SHORT).show();
}
});
return imageCapture;
}
private void setPreviewImage(File photoFile) {
final Uri savedUri = Uri.fromFile(photoFile);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int doubleScreenWidth = displayMetrics.widthPixels * 2;
int doubleScreenHeight = displayMetrics.heightPixels * 2;
Bitmap bitmap = BitmapShrinker.shrinkBitmap(photoFile.getAbsolutePath(),
doubleScreenWidth,
doubleScreenHeight);
binding.photoPreview.setImageBitmap(bitmap);
binding.photoPreview.setTag(savedUri);
viewModel.disableTorchIfEnabled();
}
public int getImageOrientation(File imageFile) {
int rotate = 0;
try {
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
default:
rotate = 0;
break;
}
Log.i(TAG, "ImageOrientation - Exif orientation: " + orientation + " - " + "Rotate value: " + rotate);
} catch (Exception e) {
Log.w(TAG, "Error calculation rotation value");
}
return rotate;
}
private Preview getPreview(Boolean crop) {
Preview preview = new Preview.Builder()
.setTargetAspectRatio(crop ? AspectRatio.RATIO_16_9 : AspectRatio.RATIO_4_3).build();
preview.setSurfaceProvider(binding.preview.getSurfaceProvider());
return preview;
}
@Override
protected void onPause() {
if (this.orientationEventListener != null) {
this.orientationEventListener.disable();
}
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
if (this.orientationEventListener != null) {
this.orientationEventListener.enable();
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
if (binding.photoPreview.getTag() != null) {
savedInstanceState.putString("Uri", ((Uri) binding.photoPreview.getTag()).getPath());
}
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String uri = savedInstanceState.getString("Uri", null);
if (uri != null) {
File photoFile = new File(uri);
setPreviewImage(photoFile);
showPictureProcessingElements();
}
}
public static Intent createIntent(@NonNull Context context) {
return new Intent(context, TakePhotoActivity.class).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
}
}

View file

@ -281,7 +281,7 @@ public class AccountVerificationController extends BaseController {
private void storeProfile(String displayName, String userId) {
userUtils.createOrUpdateUser(username, token,
baseUrl, displayName, null, true,
baseUrl, displayName, null, Boolean.TRUE,
userId, null, null,
appPreferences.getTemporaryClientCertAlias(), null)
.subscribeOn(Schedulers.io())

View file

@ -96,6 +96,7 @@ import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.activities.TakePhotoActivity
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingVoiceMessageViewHolder
@ -990,6 +991,17 @@ class ChatController(args: Bundle) :
}
}
private fun isCameraPermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return PermissionChecker.checkSelfPermission(
context!!,
Manifest.permission.CAMERA
) == PermissionChecker.PERMISSION_GRANTED
} else {
true
}
}
private fun startAudioRecording(file: String) {
binding.messageInputView.audioRecordDuration.base = SystemClock.elapsedRealtime()
binding.messageInputView.audioRecordDuration.start()
@ -1079,6 +1091,15 @@ class ChatController(args: Bundle) :
)
}
private fun requestCameraPermissions() {
requestPermissions(
arrayOf(
Manifest.permission.CAMERA
),
REQUEST_CAMERA_PERMISSION
)
}
private fun checkReadOnlyState() {
if (currentConversation != null && isAlive()) {
if (currentConversation?.shouldShowLobby(conversationUser) ?: false ||
@ -1221,6 +1242,34 @@ class ChatController(args: Bundle) :
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
} else if (requestCode == REQUEST_CODE_PICK_CAMERA) {
if (resultCode == RESULT_OK) {
try {
checkNotNull(intent)
filesToUpload.clear()
run {
checkNotNull(intent.data)
intent.data.let {
filesToUpload.add(intent.data.toString())
}
}
require(filesToUpload.isNotEmpty())
if (UploadAndShareFilesWorker.isStoragePermissionGranted(context!!)) {
uploadFiles(filesToUpload, false)
} else {
UploadAndShareFilesWorker.requestStoragePermission(this)
}
} catch (e: IllegalStateException) {
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
}
}
@ -1246,6 +1295,15 @@ class ChatController(args: Bundle) :
Toast.LENGTH_LONG
).show()
}
} else if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "launch cam activity since permission for cam has been granted")
startActivityForResult(TakePhotoActivity.createIntent(context!!), REQUEST_CODE_PICK_CAMERA)
} else {
Toast
.makeText(context, context?.getString(R.string.take_photo_permission), Toast.LENGTH_LONG)
.show()
}
}
}
@ -2542,6 +2600,14 @@ class ChatController(args: Bundle) :
}
}
fun sendPictureFromCamIntent() {
if (!isCameraPermissionGranted()) {
requestCameraPermissions()
} else {
startActivityForResult(TakePhotoActivity.createIntent(context!!), REQUEST_CODE_PICK_CAMERA)
}
}
companion object {
private const val TAG = "ChatController"
private const val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1
@ -2556,6 +2622,8 @@ class ChatController(args: Bundle) :
private const val AGE_THREHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000)
private const val REQUEST_CODE_CHOOSE_FILE: Int = 555
private const val REQUEST_RECORD_AUDIO_PERMISSION = 222
private const val REQUEST_CAMERA_PERMISSION = 223
private const val REQUEST_CODE_PICK_CAMERA: Int = 333
private const val OBJECT_MESSAGE: String = "{object}"
private const val MINIMUM_VOICE_RECORD_DURATION: Int = 1000
private const val VOICE_RECORD_CANCEL_SLIDER_X: Int = -50

View file

@ -77,7 +77,6 @@ import com.nextcloud.talk.models.json.generic.GenericOverall;
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.DoNotDisturbUtils;
import com.nextcloud.talk.utils.LoggingUtils;
import com.nextcloud.talk.utils.SecurityUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys;
@ -132,8 +131,6 @@ public class SettingsController extends BaseController {
private static final int ID_REMOVE_ACCOUNT_WARNING_DIALOG = 0;
@BindView(R.id.settings_screen)
MaterialPreferenceScreen settingsScreen;
@BindView(R.id.settings_proxy_choice)
MaterialChoicePreference proxyChoice;
@BindView(R.id.settings_proxy_port_edit)
MaterialEditTextPreference proxyPortEditText;
@BindView(R.id.settings_licence)

View file

@ -96,7 +96,7 @@ public class SwitchAccountController extends BaseController {
UserEntity userEntity = ((AdvancedUserItem) userItems.get(position)).getEntity();
userUtils.createOrUpdateUser(null,
null, null, null,
null, true, null, userEntity.getId(), null, null, null)
null, Boolean.TRUE, null, userEntity.getId(), null, null, null)
.subscribe(new Observer<UserEntity>() {
@Override
public void onSubscribe(Disposable d) {

View file

@ -398,7 +398,7 @@ public class WebViewLoginController extends BaseController {
if (isPasswordUpdate) {
if (currentUser != null) {
userQueryDisposable = userUtils.createOrUpdateUser(null, loginData.getToken(),
null, null, "", true,
null, null, "", Boolean.TRUE,
null, currentUser.getId(), null, appPreferences.getTemporaryClientCertAlias(), null)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View file

@ -21,21 +21,14 @@
package com.nextcloud.talk.controllers.bottomsheet;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import autodagger.AutoInjector;
import butterknife.BindView;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.kennyc.bottomsheet.adapters.AppAdapter;
@ -55,17 +48,30 @@ import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.ShareUtils;
import com.nextcloud.talk.utils.bundle.BundleKeys;
import com.nextcloud.talk.utils.database.user.UserUtils;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
import org.greenrobot.eventbus.EventBus;
import org.parceler.Parcel;
import org.parceler.Parcels;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import autodagger.AutoInjector;
import butterknife.BindView;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
@AutoInjector(NextcloudTalkApplication.class)
public class CallMenuController extends BaseController implements FlexibleAdapter.OnItemClickListener {
@BindView(R.id.recycler_view)
@ -77,6 +83,9 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
@Inject
UserUtils userUtils;
@Inject
Context context;
private Conversation conversation;
private List<AbstractFlexibleItem> menuItems;
private FlexibleAdapter<AbstractFlexibleItem> adapter;
@ -104,6 +113,7 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
}
@Override
@NonNull
protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
return inflater.inflate(R.layout.controller_call_menu, container, false);
}
@ -153,45 +163,57 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
if (conversation.isFavorite()) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_remove_from_favorites), 97, DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_border_black_24dp, R.color.grey_600)));
} else if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "favorites")) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_add_to_favorites)
, 98, DisplayUtils.getTintedDrawable(getResources(), R.drawable.ic_star_black_24dp, R.color.grey_600)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_add_to_favorites),
98,
DisplayUtils.getTintedDrawable(getResources(),
R.drawable.ic_star_black_24dp,
R.color.grey_600)));
}
if (conversation.isNameEditable(currentUser)) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_rename), 2, getResources().getDrawable(R.drawable
.ic_pencil_grey600_24dp)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_rename),
2,
ContextCompat.getDrawable(context,
R.drawable.ic_pencil_grey600_24dp)));
}
if (conversation.canModerate(currentUser)) {
if (!conversation.isPublic()) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_public), 3, getResources().getDrawable(R.drawable
.ic_link_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_public),
3, ContextCompat.getDrawable(context,
R.drawable.ic_link_grey600_24px)));
} else {
if (conversation.isHasPassword()) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_change_password), 4, getResources().getDrawable(R.drawable
.ic_lock_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_clear_password), 5, getResources().getDrawable(R.drawable
.ic_lock_open_grey600_24dp)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_change_password),
4, ContextCompat.getDrawable(context,
R.drawable.ic_lock_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_clear_password),
5,
ContextCompat.getDrawable(context,
R.drawable.ic_lock_open_grey600_24dp)));
} else {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_set_password), 6, getResources().getDrawable(R.drawable
.ic_lock_plus_grey600_24dp)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_set_password),
6, ContextCompat.getDrawable(context,
R.drawable.ic_lock_plus_grey600_24dp)));
}
}
menuItems.add(new MenuItem(getResources().getString(R.string.nc_delete_call), 9, getResources().getDrawable(R.drawable
.ic_delete_grey600_24dp)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_delete_call),
9, ContextCompat.getDrawable(context,
R.drawable.ic_delete_grey600_24dp)));
}
if (conversation.isPublic()) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_share_link), 7, getResources().getDrawable(R.drawable
.ic_link_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_share_link),
7, ContextCompat.getDrawable(context,
R.drawable.ic_link_grey600_24px)));
if (conversation.canModerate(currentUser)) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_private), 8, getResources().getDrawable(R.drawable
.ic_group_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_make_call_private),
8, ContextCompat.getDrawable(context,
R.drawable.ic_group_grey600_24px)));
}
}
if (conversation.canLeave(currentUser)) {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_leave), 1,
DisplayUtils.getTintedDrawable(getResources(),
@ -202,8 +224,10 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
prepareIntent();
List<AppAdapter.AppInfo> appInfoList = ShareUtils.getShareApps(getActivity(), shareIntent, null,
null);
menuItems.add(new AppItem(getResources().getString(R.string.nc_share_link_via), "", "",
getResources().getDrawable(R.drawable.ic_link_grey600_24px)));
menuItems.add(new AppItem(getResources().getString(R.string.nc_share_link_via),
"",
"",
ContextCompat.getDrawable(context, R.drawable.ic_link_grey600_24px)));
if (appInfoList != null) {
for (AppAdapter.AppInfo appInfo : appInfoList) {
menuItems.add(new AppItem(appInfo.title, appInfo.packageName, appInfo.name, appInfo.drawable));
@ -211,8 +235,12 @@ public class CallMenuController extends BaseController implements FlexibleAdapte
}
} else {
menuItems.add(new MenuItem(getResources().getString(R.string.nc_start_conversation), 0, null));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_new_conversation), 1, getResources().getDrawable(R.drawable.ic_add_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_join_via_link), 2, getResources().getDrawable(R.drawable.ic_link_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_new_conversation),
1, ContextCompat.getDrawable(context,
R.drawable.ic_add_grey600_24px)));
menuItems.add(new MenuItem(getResources().getString(R.string.nc_join_via_link),
2, ContextCompat.getDrawable(context,
R.drawable.ic_link_grey600_24px)));
}
}

View file

@ -0,0 +1,117 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Stefan Niedermann
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Stefan Niedermann <info@niedermann.it>
*
* 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;
import com.nextcloud.talk.R;
import androidx.annotation.NonNull;
import androidx.camera.core.CameraSelector;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import static androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA;
import static androidx.camera.core.CameraSelector.DEFAULT_FRONT_CAMERA;
public class TakePictureViewModel extends ViewModel {
@NonNull
private CameraSelector cameraSelector = DEFAULT_BACK_CAMERA;
@NonNull
private final MutableLiveData<Boolean> torchEnabled = new MutableLiveData<>(Boolean.FALSE);
@NonNull
private final MutableLiveData<Boolean> lowResolutionEnabled = new MutableLiveData<>(Boolean.FALSE);
@NonNull
private final MutableLiveData<Boolean> cropEnabled = new MutableLiveData<>(Boolean.FALSE);
@NonNull
public CameraSelector getCameraSelector() {
return this.cameraSelector;
}
public void toggleCameraSelector() {
if (this.cameraSelector == DEFAULT_BACK_CAMERA) {
this.cameraSelector = DEFAULT_FRONT_CAMERA;
if (this.torchEnabled.getValue()) {
toggleTorchEnabled();
}
} else {
this.cameraSelector = DEFAULT_BACK_CAMERA;
}
}
public void disableTorchIfEnabled() {
if (this.torchEnabled.getValue()) {
toggleTorchEnabled();
}
}
public void toggleTorchEnabled() {
//noinspection ConstantConditions
this.torchEnabled.postValue(!this.torchEnabled.getValue());
}
public void toggleLowResolutionEnabled() {
//noinspection ConstantConditions
this.lowResolutionEnabled.postValue(!this.lowResolutionEnabled.getValue());
}
public void toggleCropEnabled() {
//noinspection ConstantConditions
this.cropEnabled.postValue(!this.cropEnabled.getValue());
}
public LiveData<Boolean> isTorchEnabled() {
return this.torchEnabled;
}
public LiveData<Boolean> isLowResolutionEnabled() {
return this.lowResolutionEnabled;
}
public LiveData<Boolean> isCropEnabled() {
return this.cropEnabled;
}
public LiveData<Integer> getTorchToggleButtonImageResource() {
return Transformations.map(isTorchEnabled(), enabled -> enabled
? R.drawable.ic_baseline_flash_off_24
: R.drawable.ic_baseline_flash_on_24);
}
public LiveData<Integer> getLowResolutionToggleButtonImageResource() {
return Transformations.map(isLowResolutionEnabled(), enabled -> enabled
? R.drawable.ic_high_quality
: R.drawable.ic_low_quality);
}
public LiveData<Integer> getCropToggleButtonImageResource() {
return Transformations.map(isCropEnabled(), enabled -> enabled
? R.drawable.ic_crop_4_3
: R.drawable.ic_crop_16_9);
}
}

View file

@ -2,7 +2,9 @@
* Nextcloud Talk application
*
* @author Marcel Hibbe
* @author Andy Scherzinger
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
*
* 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
@ -24,47 +26,26 @@ import android.app.Activity
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.appcompat.widget.AppCompatTextView
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.Unbinder
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.nextcloud.talk.R
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
import com.nextcloud.talk.controllers.ChatController
import com.nextcloud.talk.databinding.DialogAttachmentBinding
import com.nextcloud.talk.models.database.CapabilitiesUtil
class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) {
@BindView(R.id.menu_share_location)
@JvmField
var shareLocationItem: LinearLayout? = null
@BindView(R.id.txt_share_location)
@JvmField
var shareLocation: AppCompatTextView? = null
@BindView(R.id.txt_attach_file_from_local)
@JvmField
var attachFromLocal: AppCompatTextView? = null
@BindView(R.id.txt_attach_file_from_cloud)
@JvmField
var attachFromCloud: AppCompatTextView? = null
private var unbinder: Unbinder? = null
private lateinit var dialogAttachmentBinding: DialogAttachmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = layoutInflater.inflate(R.layout.dialog_attachment, null)
setContentView(view)
dialogAttachmentBinding = DialogAttachmentBinding.inflate(layoutInflater)
setContentView(dialogAttachmentBinding.root)
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
unbinder = ButterKnife.bind(this, view)
var serverName = CapabilitiesUtil.getServerName(chatController.conversationUser)
attachFromCloud?.text = chatController.resources?.let {
dialogAttachmentBinding.txtAttachFileFromCloud.text = chatController.resources?.let {
if (serverName.isNullOrEmpty()) {
serverName = it.getString(R.string.nc_server_product_name)
}
@ -76,19 +57,25 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle
"geo-location-sharing"
)
) {
shareLocationItem?.visibility = View.GONE
dialogAttachmentBinding.menuShareLocation.visibility = View.GONE
}
shareLocation?.setOnClickListener {
dialogAttachmentBinding.menuShareLocation.setOnClickListener {
chatController.showShareLocationScreen()
dismiss()
}
attachFromLocal?.setOnClickListener {
dialogAttachmentBinding.menuAttachFileFromLocal.setOnClickListener {
chatController.sendSelectLocalFileIntent()
dismiss()
}
attachFromCloud?.setOnClickListener {
dialogAttachmentBinding.menuAttachPictureFromCam.setOnClickListener {
chatController.sendPictureFromCamIntent()
dismiss()
}
dialogAttachmentBinding.menuAttachFileFromCloud.setOnClickListener {
chatController.showBrowserScreen(BrowserController.BrowserType.DAV_BROWSER)
dismiss()
}

View file

@ -206,7 +206,7 @@ public class ChooseAccountDialogFragment extends DialogFragment {
null,
null,
null,
true,
Boolean.TRUE,
null, userEntity.getId(),
null,
null,

View file

@ -2,7 +2,9 @@
* Nextcloud Talk application
*
* @author Tobias Kaminsky
* @author Andy Scherzinger
* Copyright (C) 2021 Tobias Kaminsky <tobias.kaminsky@nextcloud.com>
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
*
* 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
@ -24,11 +26,11 @@ 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.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.nextcloud.talk.R
import com.nextcloud.talk.controllers.ProfileController
import com.nextcloud.talk.databinding.DialogScopeBinding
import com.nextcloud.talk.models.json.userprofile.Scope
class ScopeDialog(
@ -38,33 +40,36 @@ class ScopeDialog(
private val position: Int
) :
BottomSheetDialog(con) {
private lateinit var dialogScopeBinding: DialogScopeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = layoutInflater.inflate(R.layout.dialog_scope, null)
setContentView(view)
dialogScopeBinding = DialogScopeBinding.inflate(layoutInflater)
setContentView(dialogScopeBinding.root)
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
dialogScopeBinding.scopePrivate.visibility = View.GONE
}
findViewById<LinearLayout>(R.id.scope_private)?.setOnClickListener {
dialogScopeBinding.scopePrivate.setOnClickListener {
userInfoAdapter.updateScope(position, Scope.PRIVATE)
dismiss()
}
findViewById<LinearLayout>(R.id.scope_local)?.setOnClickListener {
dialogScopeBinding.scopeLocal.setOnClickListener {
userInfoAdapter.updateScope(position, Scope.LOCAL)
dismiss()
}
findViewById<LinearLayout>(R.id.scope_federated)?.setOnClickListener {
dialogScopeBinding.scopeFederated.setOnClickListener {
userInfoAdapter.updateScope(position, Scope.FEDERATED)
dismiss()
}
findViewById<LinearLayout>(R.id.scope_published)?.setOnClickListener {
dialogScopeBinding.scopePublished.setOnClickListener {
userInfoAdapter.updateScope(position, Scope.PUBLISHED)
dismiss()
}

View file

@ -34,6 +34,7 @@ object BitmapShrinker {
private const val DEGREES_180 = 180f
private const val DEGREES_270 = 270f
@JvmStatic
fun shrinkBitmap(
path: String,
reqWidth: Int,

View file

@ -46,16 +46,13 @@ public class FileSortOrderBySize extends FileSortOrder {
Collections.sort(files, (o1, o2) -> {
if (!o1.getModel().isFile() && !o2.getModel().isFile()) {
Long obj1 = o1.getModel().size;
return multiplier * obj1.compareTo(o2.getModel().getSize());
return multiplier * Long.compare(o1.getModel().getSize(), o2.getModel().getSize());
} else if (!o1.getModel().isFile()) {
return -1;
} else if (!o2.getModel().isFile()) {
return 1;
} else {
Long obj1 = o1.getModel().getSize();
return multiplier * obj1.compareTo(o2.getModel().getSize());
return multiplier * Long.compare(o1.getModel().getSize(), o2.getModel().getSize());
}
});

View file

@ -0,0 +1,68 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Stefan Niedermann
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Stefan Niedermann <info@niedermann.it>
*
* 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.utils;
import android.content.Context;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import androidx.annotation.NonNull;
public class FileUtils {
private static final String TAG = FileUtils.class.getSimpleName();
/**
* Creates a new {@link File}
*/
public static File getTempCacheFile(@NonNull Context context, String fileName) throws IOException {
File cacheFile = new File(context.getApplicationContext().getFilesDir().getAbsolutePath() + "/" + fileName);
Log.v(TAG, "Full path for new cache file:" + cacheFile.getAbsolutePath());
final File tempDir = cacheFile.getParentFile();
if (tempDir == null) {
throw new FileNotFoundException("could not cacheFile.getParentFile()");
}
if (!tempDir.exists()) {
Log.v(TAG,
"The folder in which the new file should be created does not exist yet. Trying to create it…");
if (tempDir.mkdirs()) {
Log.v(TAG, "Creation successful");
} else {
throw new IOException("Directory for temporary file does not exist and could not be created.");
}
}
Log.v(TAG, "- Try to create actual cache file");
if (cacheFile.createNewFile()) {
Log.v(TAG, "Successfully created cache file");
} else {
throw new IOException("Failed to create cacheFile");
}
return cacheFile;
}
}

View file

@ -60,7 +60,7 @@ import autodagger.AutoInjector;
@AutoInjector(NextcloudTalkApplication.class)
public class MagicPeerConnectionWrapper {
private static String TAG = "MagicPeerConnectionWrapper";
private static final String TAG = "MagicPeerConWrapper";
private List<IceCandidate> iceCandidates = new ArrayList<>();
private PeerConnection peerConnection;
@ -129,7 +129,6 @@ public class MagicPeerConnectionWrapper {
EventBus.getDefault().post(new WebSocketCommunicationEvent("peerReadyForRequestingOffer", hashMap));
} else if (!hasMCU && hasInitiated) {
peerConnection.createOffer(magicSdpObserver, sdpConstraints);
}
}
}
@ -178,7 +177,6 @@ public class MagicPeerConnectionWrapper {
}
}
public void sendNickChannelData(DataChannelMessageNick dataChannelMessage) {
ByteBuffer buffer;
if (magicDataChannel != null) {
@ -186,7 +184,7 @@ public class MagicPeerConnectionWrapper {
buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes());
magicDataChannel.send(new DataChannel.Buffer(buffer, false));
} catch (IOException e) {
Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage.toString());
Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage);
}
}
}
@ -198,7 +196,7 @@ public class MagicPeerConnectionWrapper {
buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes());
magicDataChannel.send(new DataChannel.Buffer(buffer, false));
} catch (IOException e) {
Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage.toString());
Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage);
}
}
}

View file

@ -18,8 +18,7 @@
~ * You should have received a copy of the GNU General Public License
~ * along with this program. If not, see <http://www.gnu.org/licenses/>.
~ */
-->
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"

View file

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#0082C9"/>
<stroke android:color="#FFFFFF" android:width="2dp"/>
<size android:width="15dp" android:height="15dp"/>
</shape>
</item>
</selector>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#0082C9" />
<stroke android:width="2dp" android:color="#FFFFFF" />
<size android:width="15dp" android:height="15dp" />
</shape>
</item>
</selector>

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -0,0 +1,21 @@
<!--
@author Google LLC
Copyright (C) 2021 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 android:height="24dp" android:tint="#757575"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3.27,3L2,4.27l5,5V13h3v9l3.58,-6.14L17.73,20 19,18.73 3.27,3zM17,10h-4l4,-8H7v2.18l8.46,8.46L17,10z"/>
</vector>

View file

@ -0,0 +1,21 @@
<!--
@author Google LLC
Copyright (C) 2021 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 android:height="24dp" android:tint="#757575"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
</vector>

View file

@ -0,0 +1,32 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9,12c0,1.66 1.34,3 3,3s3,-1.34 3,-3s-1.34,-3 -3,-3S9,10.34 9,12z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M8,10V8H5.09C6.47,5.61 9.05,4 12,4c3.72,0 6.85,2.56 7.74,6h2.06c-0.93,-4.56 -4.96,-8 -9.8,-8C8.73,2 5.82,3.58 4,6.01V4H2v6H8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M16,14v2h2.91c-1.38,2.39 -3.96,4 -6.91,4c-3.72,0 -6.85,-2.56 -7.74,-6H2.2c0.93,4.56 4.96,8 9.8,8c3.27,0 6.18,-1.58 8,-4.01V20h2v-6H16z"/>
</vector>

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -0,0 +1,22 @@
<!--
@author Google LLC
Copyright (C) 2021 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 android:autoMirrored="true" android:height="24dp"
android:tint="#757575" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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 android:autoMirrored="true" android:height="24dp"
android:tint="@color/medium_emphasis_text" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">

View file

@ -0,0 +1,29 @@
<!--
~ Nextcloud Talk application
~
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~
~ 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/>.
-->
<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="#FF000000"
android:fillType="nonZero"
android:pathData="M21,16L21,17C21,18.1 20.1,19 19,19L5,19C3.9,19 3,18.1 3,17L3,16L5,16L5,17L19,17L19,16L21,16ZM3,8L3,7C3,5.9 3.9,5 5,5L19,5C20.1,5 21,5.9 21,7L21,8L19,8L19,7L5,7L5,8L3,8ZM21,14.014C21,14.564 20.55,15.014 20,15.014L16.5,15.014L16.5,13.514L19.5,13.514L19.5,12.514L17.5,12.514C16.95,12.514 16.5,12.064 16.5,11.514L16.5,10.014C16.5,9.464 16.95,9.014 17.5,9.014L20,9.014C20.55,9.014 21,9.464 21,10.014L21,14.014ZM6,15L4.5,15L4.5,10.5L3,10.5L3,9L6,9L6,15ZM8,15C7.45,15 7,14.55 7,14L7,10C7,9.45 7.45,9 8,9L11.5,9L11.5,10.5L8.5,10.5L8.5,11.5L10.5,11.5C11.05,11.5 11.5,11.95 11.5,12.5L11.5,14C11.5,14.55 11.05,15 10.5,15L8,15ZM14.75,12.722L13.25,12.722L13.25,14.223L14.75,14.223L14.75,12.722ZM9.995,12.456L8.495,12.456L8.495,13.956L9.995,13.956L9.995,12.456ZM19.508,9.984L18.008,9.984L18.008,11.484L19.508,11.484L19.508,9.984ZM14.75,9.722L13.25,9.722L13.25,11.223L14.75,11.223L14.75,9.722Z" />
</vector>

View file

@ -0,0 +1,29 @@
<!--
~ Nextcloud Talk application
~
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~
~ 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/>.
-->
<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="#FF000000"
android:fillType="nonZero"
android:pathData="M21,16L21,17C21,18.1 20.1,19 19,19L5,19C3.9,19 3,18.1 3,17L3,16L5,16L5,17L19,17L19,16L21,16ZM19,14L19,10C19,9.45 18.55,9 18,9L14.5,9L14.5,10.5L17.5,10.5L17.5,11.5L15.5,11.5L15.5,12.5L17.5,12.5L17.5,13.5L14.5,13.5L14.5,15L18,15C18.55,15 19,14.55 19,14ZM9.5,9L9.5,15L8,15L8,13.5L5,13.5L5,9L6.5,9L6.5,12L8,12L8,9L9.5,9ZM12.75,12.723L12.75,14.223L11.25,14.223L11.25,12.723L12.75,12.723ZM12.75,9.723L12.75,11.223L11.25,11.223L11.25,9.723L12.75,9.723ZM3,8L3,7C3,5.9 3.9,5 5,5L19,5C20.1,5 21,5.9 21,7L21,8L19,8L19,7L5,7L5,8L3,8Z" />
</vector>

View file

@ -0,0 +1,26 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM11,15L9.5,15v-2h-2v2L6,15L6,9h1.5v2.5h2L9.5,9L11,9v6zM18,14c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5L14.75,15L14,15c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zM14.5,13.5h2v-3h-2v3z"/>
</vector>

View file

@ -0,0 +1,29 @@
<!--
~ Nextcloud Talk application
~
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~
~ 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/>.
-->
<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="#FF000000"
android:pathData="M19,4L5,4C3.89,4 3,4.9 3,6L3,18C3,19.1 3.89,20 5,20L19,20C20.1,20 21,19.1 21,18L21,6C21,4.9 20.1,4 19,4ZM18,14C18,14.55 17.55,15 17,15L16.25,15L16.25,16.5L14.75,16.5L14.75,15L14,15C13.45,15 13,14.55 13,14L13,10C13,9.45 13.45,9 14,9L17,9C17.55,9 18,9.45 18,10L18,14ZM7.5,13.5L10.509,13.496L10.508,14.997L6,15L6,9L7.5,9L7.5,13.5ZM14.5,13.5L16.5,13.5L16.5,10.5L14.5,10.5L14.5,13.5Z"
android:fillType="nonZero"/>
</vector>

View file

@ -1,3 +1,19 @@
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"

View file

@ -0,0 +1,23 @@
<!--
@author Google LLC
Copyright (C) 2021 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="#000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" />
</vector>

View file

@ -1,5 +1,27 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="@color/medium_emphasis_text" 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="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
<!--
@author Google LLC
Copyright (C) 2021 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:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="@color/medium_emphasis_text"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
</vector>

View file

@ -15,9 +15,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#757575" android:pathData="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" />
<path
android:fillColor="#757575"
android:pathData="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" />
</vector>

View file

@ -1,25 +1,26 @@
<!--
Nextcloud Talk application
@author Google LLC
Copyright (C) 2021 Google LLC
Copyright (C) 2020 Nextcloud.
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
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.
http://www.apache.org/licenses/LICENSE-2.0
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
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 android:height="24dp" android:tint="#666666"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#666666"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
</vector>

View file

@ -0,0 +1,236 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Andy Scherzinger
~ @author Stefan Niedermann
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2021 Stefan Niedermann <info@niedermann.it>
~
~ 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/>.
-->
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:fitsSystemWindows="true"
tools:theme="@style/TransparentTheme">
<androidx.camera.view.PreviewView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:scaleType="fitCenter" />
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/photo_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:scaleType="fitCenter"
android:visibility="invisible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/toggle_crop"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="@dimen/min_size_clickable_area"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="0dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/take_photo_toggle_crop"
android:insetLeft="4dp"
android:insetTop="4dp"
android:insetRight="4dp"
android:insetBottom="4dp"
android:tint="@color/white"
app:backgroundTint="@color/camera_bg_tint"
app:cornerRadius="@dimen/button_corner_radius"
app:elevation="0dp"
app:icon="@drawable/ic_crop_16_9"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/takePhoto"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/toggle_lowres"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="@dimen/min_size_clickable_area"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/take_photo_toggle_lowres"
android:insetLeft="4dp"
android:insetTop="4dp"
android:insetRight="4dp"
android:insetBottom="4dp"
android:padding="0dp"
android:tint="@android:color/white"
app:backgroundTint="@color/camera_bg_tint"
app:cornerRadius="@dimen/button_corner_radius"
app:elevation="0dp"
app:icon="@drawable/ic_low_quality"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/takePhoto" />
<com.google.android.material.button.MaterialButton
android:id="@+id/toggle_torch"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="@dimen/min_size_clickable_area"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="0dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/take_photo_toggle_torch"
android:insetLeft="4dp"
android:insetTop="4dp"
android:insetRight="4dp"
android:insetBottom="4dp"
android:tint="@color/white"
app:backgroundTint="@color/camera_bg_tint"
app:cornerRadius="@dimen/button_corner_radius"
app:elevation="0dp"
app:icon="@drawable/ic_baseline_flash_on_24"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="@id/takePhoto"
app:layout_constraintEnd_toStartOf="@id/takePhoto"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/switchCamera"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="@dimen/min_size_clickable_area"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/take_photo_switch_camera"
android:insetLeft="4dp"
android:insetTop="4dp"
android:insetRight="4dp"
android:insetBottom="4dp"
android:padding="0dp"
android:tint="@android:color/white"
app:backgroundTint="@color/camera_bg_tint"
app:cornerRadius="@dimen/button_corner_radius"
app:elevation="0dp"
app:icon="@drawable/ic_baseline_flip_camera_android_24"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="@id/takePhoto"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/takePhoto" />
<com.google.android.material.button.MaterialButton
android:id="@+id/retake"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="@dimen/min_size_clickable_area"
android:layout_height="@dimen/min_size_clickable_area"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="12dp"
android:contentDescription="@string/take_photo_retake_photo"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
android:padding="0dp"
android:tint="@android:color/white"
android:visibility="gone"
app:backgroundTint="@color/camera_bg_tint"
app:cornerRadius="@dimen/button_corner_radius"
app:elevation="0dp"
app:icon="@drawable/ic_refresh"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="@id/takePhoto"
app:layout_constraintEnd_toStartOf="@id/takePhoto"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/takePhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/standard_double_margin"
android:contentDescription="@string/take_photo"
android:tint="@android:color/white"
app:backgroundTint="@color/colorPrimary"
app:fabCustomSize="72dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_baseline_photo_camera_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:contentDescription="@string/take_photo_switch_camera"
android:paddingStart="24dp"
android:paddingTop="16dp"
android:paddingEnd="24dp"
android:paddingBottom="16dp"
android:text="@string/take_photo_send"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="16sp"
android:theme="@style/Button.Primary"
android:tint="@android:color/white"
android:visibility="gone"
app:backgroundTint="@color/colorPrimary"
app:cornerRadius="48dp"
app:elevation="0dp"
app:icon="@drawable/ic_send"
app:iconGravity="textStart"
app:iconPadding="8dp"
app:iconSize="24dp"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="@id/takePhoto"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/takePhoto" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -105,6 +105,39 @@
</LinearLayout>
<LinearLayout
android:id="@+id/menu_attach_picture_from_cam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingLeft="@dimen/standard_padding"
android:paddingTop="@dimen/standard_half_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingBottom="@dimen/standard_half_padding"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/menu_icon_attach_picture_from_cam"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_baseline_photo_camera_24"
app:tint="@color/colorPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_attach_picture_from_cam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="@dimen/standard_margin"
android:text="@string/nc_upload_picture_from_cam"
android:textAlignment="viewStart"
android:textColor="@color/high_emphasis_text"
android:textSize="@dimen/bottom_sheet_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_attach_file_from_cloud"
android:layout_width="match_parent"

View file

@ -89,4 +89,6 @@
<!-- voicemessage -->
<color name="nc_voice_message_outgoing_controls">#606060</color>
<color name="camera_bg_tint">#99121212</color>
</resources>

View file

@ -389,6 +389,7 @@
<!-- Upload -->
<string name="nc_add_file">Add to conversation</string>
<string name="nc_upload_local_file">Upload local file</string>
<string name="nc_upload_picture_from_cam">Take photo</string>
<string name="nc_upload_from_cloud">Share from %1$s</string>
<string name="nc_upload_failed">Sorry, upload failed</string>
<string name="nc_upload_choose_local_files">Choose files</string>
@ -473,4 +474,13 @@
<string name="nc_dialog_invalid_password">Invalid password</string>
<string name="nc_dialog_reauth_or_delete">Do you want to reauthorize or delete this account?</string>
<string name="take_photo">Take a photo</string>
<string name="take_photo_switch_camera">Switch camera</string>
<string name="take_photo_retake_photo">Re-take photo</string>
<string name="take_photo_toggle_torch">Toggle torch</string>
<string name="take_photo_toggle_crop">Crop photo</string>
<string name="take_photo_toggle_lowres">Reduce image size</string>
<string name="take_photo_send">Send</string>
<string name="take_photo_error_deleting_picture">Error taking picture</string>
<string name="take_photo_permission">Taking a photo is not possible without permissions</string>
</resources>

View file

@ -55,6 +55,18 @@
<item name="elevation">1dp</item>
</style>
<style name="TransparentTheme" parent="Theme.MaterialComponents.NoActionBar.Bridge">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/background_dark</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="TakePhotoTheme" parent="TransparentTheme">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="ErrorAppearance" parent="@android:style/TextAppearance">
<item name="android:textColor">@color/nc_darkRed</item>
<item name="android:textSize">12sp</item>
@ -103,6 +115,21 @@
<item name="searchHintIcon">@null</item>
</style>
<style name="Button" parent="Widget.MaterialComponents.Button.UnelevatedButton">
<item name="colorButtonNormal">@color/colorPrimary</item>
<item name="android:textColor">@color/white</item>
<item name="android:textAllCaps">false</item>
<item name="android:typeface">sans</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Button.Primary" parent="Button">
<item name="colorButtonNormal">@color/colorPrimary</item>
<item name="android:textColor">@color/white</item>
<item name="android:typeface">sans</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Widget.AppTheme.Button.IconButton" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:minWidth">0dp</item>
<item name="android:insetLeft">0dp</item>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M21,16L21,17C21,18.1 20.1,19 19,19L5,19C3.9,19 3,18.1 3,17L3,16L5,16L5,17L19,17L19,16L21,16ZM3,8L3,7C3,5.9 3.9,5 5,5L19,5C20.1,5 21,5.9 21,7L21,8L19,8L19,7L5,7L5,8L3,8ZM21,14.014C21,14.564 20.55,15.014 20,15.014L16.5,15.014L16.5,13.514L19.5,13.514L19.5,12.514L17.5,12.514C16.95,12.514 16.5,12.064 16.5,11.514L16.5,10.014C16.5,9.464 16.95,9.014 17.5,9.014L20,9.014C20.55,9.014 21,9.464 21,10.014L21,14.014ZM6,15L4.5,15L4.5,10.5L3,10.5L3,9L6,9L6,15ZM8,15C7.45,15 7,14.55 7,14L7,10C7,9.45 7.45,9 8,9L11.5,9L11.5,10.5L8.5,10.5L8.5,11.5L10.5,11.5C11.05,11.5 11.5,11.95 11.5,12.5L11.5,14C11.5,14.55 11.05,15 10.5,15L8,15ZM14.75,12.722L13.25,12.722L13.25,14.223L14.75,14.223L14.75,12.722ZM9.995,12.456L8.495,12.456L8.495,13.956L9.995,13.956L9.995,12.456ZM19.508,9.984L18.008,9.984L18.008,11.484L19.508,11.484L19.508,9.984ZM14.75,9.722L13.25,9.722L13.25,11.223L14.75,11.223L14.75,9.722Z" style="fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M21,16L21,17C21,18.1 20.1,19 19,19L5,19C3.9,19 3,18.1 3,17L3,16L5,16L5,17L19,17L19,16L21,16ZM19,14L19,10C19,9.45 18.55,9 18,9L14.5,9L14.5,10.5L17.5,10.5L17.5,11.5L15.5,11.5L15.5,12.5L17.5,12.5L17.5,13.5L14.5,13.5L14.5,15L18,15C18.55,15 19,14.55 19,14ZM9.5,9L9.5,15L8,15L8,13.5L5,13.5L5,9L6.5,9L6.5,12L8,12L8,9L9.5,9ZM12.75,12.723L12.75,14.223L11.25,14.223L11.25,12.723L12.75,12.723ZM12.75,9.723L12.75,11.223L11.25,11.223L11.25,9.723L12.75,9.723ZM3,8L3,7C3,5.9 3.9,5 5,5L19,5C20.1,5 21,5.9 21,7L21,8L19,8L19,7L5,7L5,8L3,8Z" style="fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 1,015 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="M19,4L5,4C3.89,4 3,4.9 3,6L3,18C3,19.1 3.89,20 5,20L19,20C20.1,20 21,19.1 21,18L21,6C21,4.9 20.1,4 19,4ZM18,14C18,14.55 17.55,15 17,15L16.25,15L16.25,16.5L14.75,16.5L14.75,15L14,15C13.45,15 13,14.55 13,14L13,10C13,9.45 13.45,9 14,9L17,9C17.55,9 18,9.45 18,10L18,14ZM7.5,13.5L10.509,13.496L10.508,14.997L6,15L6,9L7.5,9L7.5,13.5ZM14.5,13.5L16.5,13.5L16.5,10.5L14.5,10.5L14.5,13.5Z" style="fill-rule:nonzero;"/>
</svg>

After

Width:  |  Height:  |  Size: 872 B

View file

@ -1 +1 @@
568
559

View file

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 1 error and 238 warnings</span>
<span class="mdl-layout-title">Lint Report: 1 error and 222 warnings</span>