Ver Fonte

Version 4.5-rc2

Threema há 5 anos atrás
pai
commit
f6e9f962c4
43 ficheiros alterados com 368 adições e 337 exclusões
  1. 8 8
      app/build.gradle
  2. 1 1
      app/src/main/AndroidManifest.xml
  3. 67 79
      app/src/main/java/ch/threema/app/activities/RecipientListBaseActivity.java
  4. 3 3
      app/src/main/java/ch/threema/app/activities/SendMediaActivity.java
  5. 1 1
      app/src/main/java/ch/threema/app/activities/wizard/WizardBaseActivity.java
  6. 2 2
      app/src/main/java/ch/threema/app/adapters/decorators/FileChatAdapterDecorator.java
  7. 7 0
      app/src/main/java/ch/threema/app/adapters/decorators/TextChatAdapterDecorator.java
  8. 1 1
      app/src/main/java/ch/threema/app/camera/CameraFragment.java
  9. 0 3
      app/src/main/java/ch/threema/app/camera/CameraXModule.java
  10. 6 4
      app/src/main/java/ch/threema/app/fragments/ComposeMessageFragment.java
  11. 15 4
      app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment1.java
  12. 15 4
      app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment2.java
  13. 16 4
      app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment3.java
  14. 3 5
      app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment4.java
  15. 0 8
      app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment5.java
  16. 25 16
      app/src/main/java/ch/threema/app/mediaattacher/MediaRepository.java
  17. 12 11
      app/src/main/java/ch/threema/app/mediaattacher/MediaSelectionBaseActivity.java
  18. 33 44
      app/src/main/java/ch/threema/app/routines/SynchronizeContactsRoutine.java
  19. 9 6
      app/src/main/java/ch/threema/app/services/MessageServiceImpl.java
  20. 6 0
      app/src/main/java/ch/threema/app/ui/AvatarEditView.java
  21. 5 8
      app/src/main/java/ch/threema/app/utils/ConfigUtils.java
  22. 25 13
      app/src/main/java/ch/threema/app/voip/activities/CallActivity.java
  23. 6 1
      app/src/main/java/ch/threema/app/voip/services/VoipStateService.java
  24. 7 4
      app/src/main/java/ch/threema/app/voip/util/VideoCapturerUtil.java
  25. 7 1
      app/src/main/java/ch/threema/app/webclient/webrtc/PeerConnectionWrapper.java
  26. 6 4
      app/src/main/res/layout/activity_call.xml
  27. 2 1
      app/src/main/res/layout/activity_send_media.xml
  28. 2 12
      app/src/main/res/layout/activity_wizard.xml
  29. 0 1
      app/src/main/res/layout/conversation_list_item_quote.xml
  30. 1 13
      app/src/main/res/layout/conversation_list_item_quote_recv.xml
  31. 1 13
      app/src/main/res/layout/conversation_list_item_quote_send.xml
  32. 16 13
      app/src/main/res/layout/conversation_list_item_recv.xml
  33. 3 3
      app/src/main/res/layout/fragment_wizard.xml
  34. 0 1
      app/src/main/res/layout/fragment_wizard0.xml
  35. 2 5
      app/src/main/res/layout/fragment_wizard1.xml
  36. 1 3
      app/src/main/res/layout/fragment_wizard3.xml
  37. 1 1
      app/src/main/res/values-de/strings.xml
  38. 0 12
      app/src/main/res/values-h533dp/dimens.xml
  39. 24 0
      app/src/main/res/values-h640dp/dimens.xml
  40. 15 9
      app/src/main/res/values/dimens.xml
  41. 2 2
      app/src/main/res/values/strings.xml
  42. 11 12
      app/src/main/res/values/styles.xml
  43. 1 1
      app/src/main/res/values/themes.xml

+ 8 - 8
app/build.gradle

@@ -75,8 +75,8 @@ android {
         vectorDrawables.useSupportLibrary = true
         applicationId "ch.threema.app"
         testApplicationId 'ch.threema.app.test'
-        versionCode 661
-        versionName "4.5-rc1"
+        versionCode 662
+        versionName "4.5-rc2"
         resValue "string", "version_name_suffix", ""
         resValue "string", "app_name", "Threema"
         resValue "string", "uri_scheme", "threema"
@@ -141,7 +141,7 @@ android {
         }
         store_threema { }
         store_google_work {
-            versionName "4.5k-rc1"
+            versionName "4.5k-rc2"
             applicationId "ch.threema.app.work"
             testApplicationId 'ch.threema.app.work.test'
             resValue "string", "package_name", applicationId
@@ -178,7 +178,7 @@ android {
             buildConfigField "byte[]", "SERVER_PUBKEY_ALT", "new byte[] {(byte) 0x5a, (byte) 0x98, (byte) 0xf2, (byte) 0x3d, (byte) 0xe6, (byte) 0x56, (byte) 0x05, (byte) 0xd0, (byte) 0x50, (byte) 0xdc, (byte) 0x00, (byte) 0x64, (byte) 0xbe, (byte) 0x07, (byte) 0xdd, (byte) 0xdd, (byte) 0x81, (byte) 0x1d, (byte) 0xa1, (byte) 0x16, (byte) 0xa5, (byte) 0x43, (byte) 0xce, (byte) 0x43, (byte) 0xaa, (byte) 0x26, (byte) 0x87, (byte) 0xd1, (byte) 0x9f, (byte) 0x20, (byte) 0xaf, (byte) 0x3c }"
         }
         sandbox_work {
-            versionName "4.5k-rc1"
+            versionName "4.5k-rc2"
             applicationId "ch.threema.app.sandbox.work"
             testApplicationId 'ch.threema.app.sandbox.work.test'
 
@@ -208,7 +208,7 @@ android {
             ]
         }
         red { // Essentially like sandbox work, but with a different icon and accent color, used for internal testing
-            versionName "4.5r-rc1"
+            versionName "4.5r-rc2"
             applicationId "ch.threema.app.red"
             testApplicationId 'ch.threema.app.red.test'
 
@@ -453,9 +453,9 @@ dependencies {
     implementation 'androidx.activity:activity:1.1.0'
     implementation 'androidx.sqlite:sqlite:2.1.0'
     implementation "androidx.concurrent:concurrent-futures:1.1.0"
-    implementation "androidx.camera:camera-camera2:1.0.0-rc01"
-    implementation "androidx.camera:camera-lifecycle:1.0.0-rc01"
-    implementation "androidx.camera:camera-view:1.0.0-alpha20"
+    implementation "androidx.camera:camera-camera2:1.0.0-rc02"
+    implementation "androidx.camera:camera-lifecycle:1.0.0-rc02"
+    implementation "androidx.camera:camera-view:1.0.0-alpha21"
     implementation 'androidx.multidex:multidex:2.0.1'
     implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
     implementation "androidx.lifecycle:lifecycle-livedata:2.2.0"

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -590,7 +590,7 @@
 			android:name=".activities.wizard.WizardBaseActivity"
 			android:screenOrientation="sensorPortrait"
 			android:theme="@style/Theme.Threema.Wizard"
-			android:windowSoftInputMode="adjustPan"/>
+			android:windowSoftInputMode="adjustResize"/>
 		<activity
 			android:name=".activities.wizard.WizardFingerPrintActivity"
 			android:screenOrientation="sensorPortrait"

+ 67 - 79
app/src/main/java/ch/threema/app/activities/RecipientListBaseActivity.java

@@ -769,14 +769,6 @@ public class RecipientListBaseActivity extends ThreemaToolbarActivity implements
 		}
 	}
 
-	/**
-	 * Copy shared files to a private directory as permissions for content URIs may not persist across activities
-	 */
-	@WorkerThread
-	private void copySelectedFiles() {
-
-	}
-
 	private void sendSharedMedia(final MessageReceiver[] messageReceivers, final Intent intent) {
 		if (messageReceivers.length == 1 && mediaItems.size() == 1 && TYPE_TEXT == mediaItems.get(0).getType()) {
 			intent.putExtra(ThreemaApplication.INTENT_DATA_TEXT, mediaItems.get(0).getCaption());
@@ -789,85 +781,81 @@ public class RecipientListBaseActivity extends ThreemaToolbarActivity implements
 		}
 	}
 
-	@UiThread
-	private void forwardMessages(final MessageReceiver[] messageReceivers, final Intent intent) {
-		final int[] errors = {0};
-		CancelableHorizontalProgressDialog.newInstance(R.string.sending_messages, 0, 0, originalMessageModels.size()).show(getSupportFragmentManager(), DIALOG_TAG_MULTISEND);
-		for (int i = 0; i < originalMessageModels.size(); i++) {
-			final AbstractMessageModel messageModel = originalMessageModels.get(i);
-			int progress = i;
-			fileService.loadDecryptedMessageFile(messageModel, new FileService.OnDecryptedFileComplete() {
-				@Override
-				public void complete(File decryptedFile) {
-					RuntimeUtil.runOnUiThread(new Runnable() {
-						@Override
-						public void run() {
-							DialogUtil.updateProgress(getSupportFragmentManager(), DIALOG_TAG_MULTISEND, progress);
-						}
-					});
-					if (messageModel.isAvailable()) {
-						Uri uri = null;
-						if (decryptedFile != null) {
-							uri = Uri.fromFile(decryptedFile);
-						}
-						switch (messageModel.getType()) {
-							case IMAGE:
-								sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_IMAGE, null, FileData.RENDERING_MEDIA, null);
-								break;
-							case VIDEO:
-								sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_VIDEO, null, FileData.RENDERING_MEDIA, null);
-								break;
-							case VOICEMESSAGE:
-								// voice messages should always be forwarded as files in order not to appear to be recorded by the forwarder
-								sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_FILE, MimeUtil.MIME_TYPE_AUDIO_AAC, FileData.RENDERING_DEFAULT, null);
-								break;
-							case FILE:
-								int mediaType = MediaItem.TYPE_FILE;
-								String mimeType = messageModel.getFileData().getMimeType();
-								int renderingType = messageModel.getFileData().getRenderingType();
-
-								if (messageModel.getFileData().getRenderingType() != FileData.RENDERING_DEFAULT) {
-									if (MimeUtil.isVideoFile(mimeType)) {
-										mediaType = MediaItem.TYPE_VIDEO;
-									} else if (MimeUtil.isImageFile(mimeType)) {
-										if (MimeUtil.isGifFile(mimeType)) {
-											mediaType = MediaItem.TYPE_GIF;
-										} else {
-											mediaType = MediaItem.TYPE_IMAGE;
-										}
+	void forwardSingleMessage(final MessageReceiver[] messageReceivers, final int i, final Intent intent) {
+		final AbstractMessageModel messageModel = originalMessageModels.get(i);
+		fileService.loadDecryptedMessageFile(messageModel, new FileService.OnDecryptedFileComplete() {
+			@Override
+			public void complete(File decryptedFile) {
+				RuntimeUtil.runOnUiThread(() -> DialogUtil.updateProgress(getSupportFragmentManager(), DIALOG_TAG_MULTISEND, i));
+
+				if (messageModel.isAvailable()) {
+					Uri uri = null;
+					if (decryptedFile != null) {
+						uri = Uri.fromFile(decryptedFile);
+					}
+					switch (messageModel.getType()) {
+						case IMAGE:
+							sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_IMAGE, null, FileData.RENDERING_MEDIA, null);
+							break;
+						case VIDEO:
+							sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_VIDEO, null, FileData.RENDERING_MEDIA, null);
+							break;
+						case VOICEMESSAGE:
+							// voice messages should always be forwarded as files in order not to appear to be recorded by the forwarder
+							sendForwardedMedia(messageReceivers, uri, captionText, MediaItem.TYPE_FILE, MimeUtil.MIME_TYPE_AUDIO_AAC, FileData.RENDERING_DEFAULT, null);
+							break;
+						case FILE:
+							int mediaType = MediaItem.TYPE_FILE;
+							String mimeType = messageModel.getFileData().getMimeType();
+							int renderingType = messageModel.getFileData().getRenderingType();
+
+							if (messageModel.getFileData().getRenderingType() != FileData.RENDERING_DEFAULT) {
+								if (MimeUtil.isVideoFile(mimeType)) {
+									mediaType = MediaItem.TYPE_VIDEO;
+								} else if (MimeUtil.isImageFile(mimeType)) {
+									if (MimeUtil.isGifFile(mimeType)) {
+										mediaType = MediaItem.TYPE_GIF;
 									} else {
-										// voice messages should always be forwarded as files in order not to appear to be recorded by the forwarder
-										renderingType = FileData.RENDERING_DEFAULT;
+										mediaType = MediaItem.TYPE_IMAGE;
 									}
+								} else {
+									// voice messages should always be forwarded as files in order not to appear to be recorded by the forwarder
+									renderingType = FileData.RENDERING_DEFAULT;
 								}
-								sendForwardedMedia(messageReceivers, uri, captionText, mediaType, mimeType, renderingType, messageModel.getFileData().getFileName());
-								break;
-							case LOCATION:
-								sendLocationMessage(messageReceivers, messageModel.getLocationData());
-								break;
-							case TEXT:
-								sendTextMessage(messageReceivers, messageModel.getBody());
-								break;
-							default:
-								// unsupported message type
-								break;
-						}
+							}
+							sendForwardedMedia(messageReceivers, uri, captionText, mediaType, mimeType, renderingType, messageModel.getFileData().getFileName());
+							break;
+						case LOCATION:
+							sendLocationMessage(messageReceivers, messageModel.getLocationData());
+							break;
+						case TEXT:
+							sendTextMessage(messageReceivers, messageModel.getBody());
+							break;
+						default:
+							// unsupported message type
+							break;
 					}
 				}
-
-				@Override
-				public void error(String message) {
-					errors[0]++;
+				if (i < originalMessageModels.size() - 1) {
+					forwardSingleMessage(messageReceivers, i+1, intent);
+				} else {
+					DialogUtil.dismissDialog(getSupportFragmentManager(), DIALOG_TAG_MULTISEND, true);
+					startComposeActivity(intent);
 				}
-			});
-		}
+			}
 
-		DialogUtil.dismissDialog(getSupportFragmentManager(), DIALOG_TAG_MULTISEND, true);
+			@Override
+			public void error(String message) {
+				RuntimeUtil.runOnUiThread(() -> SingleToast.getInstance().showLongText(getString(R.string.an_error_occurred_during_send)));
+			}
+		});
+	}
 
-		if (errors[0] > 0) {
-			SingleToast.getInstance().showLongText(getString(R.string.an_error_occurred_during_send));
-		}
-		startComposeActivity(intent);
+	@UiThread
+	private void forwardMessages(final MessageReceiver[] messageReceivers, final Intent intent) {
+		CancelableHorizontalProgressDialog.newInstance(R.string.sending_messages, 0, 0, originalMessageModels.size()).show(getSupportFragmentManager(), DIALOG_TAG_MULTISEND);
+
+		forwardSingleMessage(messageReceivers, 0, intent);
 	}
 
 	private void startComposeActivityAsync(final Intent intent) {

+ 3 - 3
app/src/main/java/ch/threema/app/activities/SendMediaActivity.java

@@ -60,6 +60,7 @@ import android.widget.TextView;
 import android.widget.Toast;
 
 import com.google.android.material.snackbar.Snackbar;
+import com.mapbox.mapboxsdk.style.layers.Property;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -657,14 +658,13 @@ public class SendMediaActivity extends ThreemaToolbarActivity implements
 	@UiThread
 	public void maybeShowImageResolutionTooltip() {
 		editPanel.postDelayed(() -> {
-			if (settingsItem.getVisibility() == View.VISIBLE && !preferenceService.getIsImageResolutionTooltipShown()) {
+			if (editPanel.getVisibility() == View.VISIBLE && settingsItem.getVisibility() == View.VISIBLE && !preferenceService.getIsImageResolutionTooltipShown()) {
 				int[] location = new int[2];
 				settingsItem.getLocationOnScreen(location);
 				location[1] -= (settingsItem.getHeight() / 5);
 
-				final TooltipPopup resolutionTooltipPopup = new TooltipPopup(SendMediaActivity.this, R.string.preferences__image_resolution_tooltip_shown, R.layout.popup_tooltip_top_right, SendMediaActivity.this);
+				final TooltipPopup resolutionTooltipPopup = new TooltipPopup(SendMediaActivity.this, 0, R.layout.popup_tooltip_top_right, SendMediaActivity.this);
 				resolutionTooltipPopup.show(this, settingsItem, getString(R.string.tooltip_image_resolution_hint), TooltipPopup.ALIGN_BELOW_ANCHOR_ARROW_RIGHT, location, 6000);
-
 				preferenceService.setIsImageResolutionTooltipShown(true);
 			}
 		}, 2000);

+ 1 - 1
app/src/main/java/ch/threema/app/activities/wizard/WizardBaseActivity.java

@@ -625,7 +625,7 @@ public class WizardBaseActivity extends ThreemaAppCompatActivity implements View
 
 	private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
 		public ScreenSlidePagerAdapter(FragmentManager fm) {
-			super(fm);
+			super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
 		}
 
 		@Override

+ 2 - 2
app/src/main/java/ch/threema/app/adapters/decorators/FileChatAdapterDecorator.java

@@ -308,8 +308,8 @@ public class FileChatAdapterDecorator extends ChatAdapterDecorator {
 			}
 
 			if (thumbnail != null) {
-				if (holder.controller.getStatus() == ControllerView.STATUS_NONE) {
-						holder.controller.setBackgroundImage(thumbnail);
+				if (holder.controller != null) {
+					holder.controller.setBackgroundImage(thumbnail);
 				}
 			}
 

+ 7 - 0
app/src/main/java/ch/threema/app/adapters/decorators/TextChatAdapterDecorator.java

@@ -31,6 +31,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import androidx.annotation.Nullable;
+import ch.threema.app.R;
 import ch.threema.app.activities.TextChatBubbleActivity;
 import ch.threema.app.emojis.EmojiConversationTextView;
 import ch.threema.app.fragments.ComposeMessageFragment;
@@ -78,6 +79,12 @@ public class TextChatAdapterDecorator extends ChatAdapterDecorator {
 			if (holder.readOnContainer != null) {
 				if (messageText != null && messageText.length() > helper.getMaxBubbleTextLength()) {
 					holder.readOnContainer.setVisibility(View.VISIBLE);
+					if (quoteType != QuoteUtil.QUOTE_TYPE_NONE) {
+						holder.readOnContainer.setBackgroundResource(ConfigUtils.getResourceFromAttribute(getContext(),
+							this.getMessageModel().isOutbox() ?
+								R.attr.chat_bubble_fade_send :
+								R.attr.chat_bubble_fade_recv));
+					}
 					holder.readOnButton.setOnClickListener(view -> {
 						Intent intent = new Intent(helper.getFragment().getContext(), TextChatBubbleActivity.class);
 						IntentDataUtil.append(this.getMessageModel(), intent);

+ 1 - 1
app/src/main/java/ch/threema/app/camera/CameraFragment.java

@@ -167,7 +167,7 @@ public class CameraFragment extends Fragment {
 	/**
 	 * Define callback that will be triggered after a photo has been taken
 	 */
-	private ImageCapture.OnImageCapturedCallback imageCapturedCallback = new ImageCapture.OnImageCapturedCallback() {
+	private final ImageCapture.OnImageCapturedCallback imageCapturedCallback = new ImageCapture.OnImageCapturedCallback() {
 		@SuppressLint("StaticFieldLeak")
 		@Override
 		public void onCaptureSuccess(@NonNull ImageProxy image) {

+ 0 - 3
app/src/main/java/ch/threema/app/camera/CameraXModule.java

@@ -91,9 +91,6 @@ import ch.threema.app.utils.ConfigUtils;
 
 import static androidx.camera.core.ImageCapture.FLASH_MODE_OFF;
 
-/* Threema-specific imports */
-
-
 /** CameraX use case operation built on @{link androidx.camera.core}. */
 @SuppressLint("RestrictedApi")
 @TargetApi(21)

+ 6 - 4
app/src/main/java/ch/threema/app/fragments/ComposeMessageFragment.java

@@ -2372,12 +2372,10 @@ public class ComposeMessageFragment extends Fragment implements
 		return messageModels;
 	}
 
-	/**
-	 * Appending Records to the list
-	 * do not call the notfiy on change on adding to speed up list ctrl
-	 */
 	/**
 	 * Append records to the list, adding date separators if necessary
+	 * Locks list by calling setNotifyOnChange(false) on the adapter to speed up list ctrl
+	 * Don't forget to call notifyDataSetChanged() on the adapter in the UI thread after inserting
 	 * @param values MessageModels to insert
 	 * @param clear Whether previous list entries should be cleared before appending
 	 * @param markasread Whether chat should be marked as read
@@ -2828,6 +2826,10 @@ public class ComposeMessageFragment extends Fragment implements
 
 						@Override
 						protected void onPostExecute(Integer result) {
+							if (result != null) {
+								composeMessageAdapter.notifyDataSetChanged();
+							}
+
 							if (getContext() != null) {
 								if (result != null && result > 0) {
 									if (getFragmentManager() != null) {

+ 15 - 4
app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment1.java

@@ -38,6 +38,7 @@ import ch.threema.app.threemasafe.ThreemaSafeAdvancedDialog;
 import ch.threema.app.threemasafe.ThreemaSafeServerInfo;
 import ch.threema.app.utils.AppRestrictionUtil;
 import ch.threema.app.utils.ConfigUtils;
+import ch.threema.app.utils.EditTextUtil;
 import ch.threema.app.utils.TestUtil;
 
 import static ch.threema.app.threemasafe.ThreemaSafeServiceImpl.MAX_PW_LENGTH;
@@ -172,12 +173,22 @@ public class WizardFragment1 extends WizardFragment implements ThreemaSafeAdvanc
 	}
 
 	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
+	public void onResume() {
+		super.onResume();
+		initValues();
+		if (this.password1 != null) {
+			this.password1.requestFocus();
+			EditTextUtil.showSoftKeyboard(this.password1);
+		}
+	}
 
-		if (isVisibleToUser) {
-			initValues();
+	@Override
+	public void onPause() {
+		if (this.password1 != null) {
+			this.password1.clearFocus();
+			EditTextUtil.hideSoftKeyboard(this.password1);
 		}
+		super.onPause();
 	}
 
 	private void initValues() {

+ 15 - 4
app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment2.java

@@ -31,6 +31,7 @@ import android.view.ViewGroup;
 import android.widget.EditText;
 
 import ch.threema.app.R;
+import ch.threema.app.utils.EditTextUtil;
 import ch.threema.app.utils.RuntimeUtil;
 import ch.threema.app.utils.TestUtil;
 
@@ -90,12 +91,22 @@ public class WizardFragment2 extends WizardFragment {
     }
 
 	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
+	public void onResume() {
+		super.onResume();
+		new Handler().postDelayed(() -> RuntimeUtil.runOnUiThread(this::initValues), 50);
+		if (this.nicknameText != null) {
+			this.nicknameText.requestFocus();
+			EditTextUtil.showSoftKeyboard(this.nicknameText);
+		}
+	}
 
-		if (isVisibleToUser) {
-			new Handler().postDelayed(() -> RuntimeUtil.runOnUiThread(this::initValues), 50);
+	@Override
+	public void onPause() {
+		if (this.nicknameText != null) {
+			this.nicknameText.clearFocus();
+			EditTextUtil.hideSoftKeyboard(this.nicknameText);
 		}
+		super.onPause();
 	}
 
 	private void initValues() {

+ 16 - 4
app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment3.java

@@ -57,6 +57,7 @@ import java.util.Set;
 
 import ch.threema.app.R;
 import ch.threema.app.utils.ConfigUtils;
+import ch.threema.app.utils.EditTextUtil;
 import ch.threema.app.utils.TestUtil;
 
 /**
@@ -387,13 +388,24 @@ public class WizardFragment3 extends WizardFragment {
 	}
 
 	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
-		if (isVisibleToUser) {
-			initValues();
+	public void onResume() {
+		super.onResume();
+		initValues();
+		if (this.phoneText != null) {
+			this.phoneText.requestFocus();
+			EditTextUtil.showSoftKeyboard(this.phoneText);
 		}
 	}
 
+	@Override
+	public void onPause() {
+		if (this.phoneText != null) {
+			this.phoneText.clearFocus();
+			EditTextUtil.hideSoftKeyboard(this.phoneText);
+		}
+		super.onPause();
+	}
+
 	void initValues() {
 		if (isResumed()) {
 			WizardFragment5.SettingsInterface callback = (WizardFragment5.SettingsInterface) getActivity();

+ 3 - 5
app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment4.java

@@ -82,11 +82,9 @@ public class WizardFragment4 extends WizardFragment {
 	}
 
 	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
-		if (isVisibleToUser) {
-			initValues();
-		}
+	public void onResume() {
+		super.onResume();
+		initValues();
 	}
 
 	void initValues() {

+ 0 - 8
app/src/main/java/ch/threema/app/fragments/wizard/WizardFragment5.java

@@ -104,14 +104,6 @@ public class WizardFragment5 extends WizardFragment implements View.OnClickListe
 		callback = (SettingsInterface) activity;
 	}
 
-	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
-		if (isVisibleToUser) {
-			initValues();
-		}
-	}
-
 	void initValues() {
 		if (isResumed()) {
 			String email = callback.getEmail();

+ 25 - 16
app/src/main/java/ch/threema/app/mediaattacher/MediaRepository.java

@@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
 import android.content.ContentUris;
 import android.content.Context;
 import android.database.Cursor;
+import android.media.MediaMetadataRetriever;
 import android.net.Uri;
 import android.provider.MediaStore;
 
@@ -38,6 +39,7 @@ import java.util.List;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
+import ch.threema.app.ThreemaApplication;
 import ch.threema.app.ui.MediaItem;
 import ch.threema.app.utils.MimeUtil;
 
@@ -150,23 +152,30 @@ public class MediaRepository {
 					orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns.ORIENTATION));
 				}
 
-				// this flag appears to be set by default on some devices
-//				int isPrivate = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.IS_PRIVATE));
-//				if (isPrivate == 0) {
-					int type;
-					if (MimeUtil.isVideoFile(mimeType)) {
-						type = MediaItem.TYPE_VIDEO;
-					} else if (MimeUtil.isGifFile(mimeType)) {
-						type = MediaItem.TYPE_GIF;
-					} else {
-						type = MediaItem.TYPE_IMAGE;
+				int type;
+				if (MimeUtil.isVideoFile(mimeType)) {
+					type = MediaItem.TYPE_VIDEO;
+					if (duration == 0) {
+						// do not use automatic resource management on MediaMetadataRetriever
+						MediaMetadataRetriever metaDataRetriever = new MediaMetadataRetriever();
+						try {
+							metaDataRetriever.setDataSource(ThreemaApplication.getAppContext(), contentUri);
+							duration = Integer.parseInt(metaDataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+						} catch (Exception ignored) {
+						} finally {
+							metaDataRetriever.release();
+						}
 					}
-					MediaAttachItem item = new MediaAttachItem(
-						id, dateAdded, dateTaken, dateModified, contentUri,
-						displayName, bucketName, orientation, duration, type
-					);
-					mediaList.add(item);
-//				}
+				} else if (MimeUtil.isGifFile(mimeType)) {
+					type = MediaItem.TYPE_GIF;
+				} else {
+					type = MediaItem.TYPE_IMAGE;
+				}
+				MediaAttachItem item = new MediaAttachItem(
+					id, dateAdded, dateTaken, dateModified, contentUri,
+					displayName, bucketName, orientation, duration, type
+				);
+				mediaList.add(item);
 			}
 		}
 	}

+ 12 - 11
app/src/main/java/ch/threema/app/mediaattacher/MediaSelectionBaseActivity.java

@@ -62,7 +62,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -361,18 +361,19 @@ abstract public class MediaSelectionBaseActivity extends ThreemaActivity impleme
 
 				// Extract buckets and media types
 				final HashSet<String> buckets = new HashSet<>();
-				final List<String> mediaTypes = new ArrayList<>();
+				final HashMap<Integer, String> mediaTypes = new HashMap<>();
 				for (MediaAttachItem mediaItem : mediaAttachItems) {
 					buckets.add(mediaItem.getBucketName());
-					String mediaTypeName = getMimeTypeTitle(mediaItem.getType());
-					if (!mediaTypes.contains(mediaTypeName)) {
-						mediaTypes.add(mediaTypeName);
+					int type = mediaItem.getType();
+					if (!mediaTypes.containsKey(type)) {
+						String mediaTypeName = getMimeTypeTitle(type);
+						mediaTypes.put(type, mediaTypeName);
 					}
 				}
 
 				// Fill menu
-				for (int i = 0; i < mediaTypes.size(); i++) {
-					String mediaTypeName = mediaTypes.get(i);
+				for (int mediaTypeKey : mediaTypes.keySet()) {
+					String mediaTypeName = mediaTypes.get(mediaTypeKey);
 					MenuItem item = menu.add(mediaTypeName).setOnMenuItemClickListener(menuItem -> {
 						filterMediaByMimeType(menuItem.toString());
 						menuTitle.setText(menuItem.toString());
@@ -380,14 +381,14 @@ abstract public class MediaSelectionBaseActivity extends ThreemaActivity impleme
 						return true;
 					});
 
-					switch(i) {
-						case 0:
+					switch(mediaTypeKey) {
+						case MediaItem.TYPE_IMAGE:
 							item.setIcon(R.drawable.ic_image_outline);
 							break;
-						case 1:
+						case MediaItem.TYPE_VIDEO:
 							item.setIcon(R.drawable.ic_movie_outline);
 							break;
-						case 2:
+						case MediaItem.TYPE_GIF:
 							item.setIcon(R.drawable.ic_gif_24dp);
 							break;
 					}

+ 33 - 44
app/src/main/java/ch/threema/app/routines/SynchronizeContactsRoutine.java

@@ -313,64 +313,53 @@ public class SynchronizeContactsRoutine implements Runnable {
 	}
 
 	private Map<String, ContactMatchKeyPhone> readPhoneNumbers() {
-		Map<String, ContactMatchKeyPhone> phones = new HashMap<String, ContactMatchKeyPhone>();
-		String selection = null;
+		Map<String, ContactMatchKeyPhone> phoneNumbers = new HashMap<>();
+		String selection;
 
 		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-			selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > 0 AND " + ContactsContract.CommonDataKinds.Email.IN_DEFAULT_DIRECTORY + " = 1";
+			selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > 0 AND " + ContactsContract.CommonDataKinds.Phone.IN_DEFAULT_DIRECTORY + " = 1";
 		} else {
 			selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > 0";
 		}
 
-		try (Cursor contactCursor = this.contentResolver.query(
-			ContactsContract.Contacts.CONTENT_URI,
-			null,
+		try (Cursor phonesCursor = this.contentResolver.query(
+			ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+			new String[]{
+				ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
+				ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
+				ContactsContract.CommonDataKinds.Phone.NUMBER,
+			},
 			selection,
 			null,
 			null)) {
 
-			if (contactCursor != null && contactCursor.getCount() > 0) {
-				int idColumnIndex = contactCursor.getColumnIndex(ContactsContract.Contacts._ID);
-				int lookupKeyIndex = contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
-				int hasPhoneNumberColumnIndex = contactCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
-
-				while (contactCursor.moveToNext()) {
-					String id = contactCursor.getString(idColumnIndex);
-					if (Integer.parseInt(contactCursor.getString(hasPhoneNumberColumnIndex)) > 0) {
-						String lookupKey = contactCursor.getString(lookupKeyIndex);
-						int contactId = contactCursor.getInt(idColumnIndex);
-
-						try (Cursor phoneCursor = this.contentResolver.query(
-							ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
-							null,
-							ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
-							new String[]{id},
-							null)) {
-
-							if (phoneCursor != null) {
-								int numberColumnIndex = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
-								while (phoneCursor.moveToNext()) {
-									String phoneNumber = phoneCursor.getString(numberColumnIndex);
-									if (!TestUtil.empty(phoneNumber)) {
-										ContactMatchKeyPhone matchKey = new ContactMatchKeyPhone();
-										matchKey.contactId = contactId;
-										matchKey.lookupKey = lookupKey;
-										matchKey.phoneNumber = phoneNumber;
-										phones.put(phoneNumber, matchKey);
-									}
-								}
-							}
-						}
+			if (phonesCursor != null && phonesCursor.getCount() > 0) {
+				int idColumnIndex = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
+				final int lookupKeyColumnIndex = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY);
+				final int phoneNumberIndex = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
+
+				while (phonesCursor.moveToNext()) {
+					int contactId = phonesCursor.getInt(idColumnIndex);
+					String lookupKey = phonesCursor.getString(lookupKeyColumnIndex);
+					String phoneNumber = phonesCursor.getString(phoneNumberIndex);
+
+//					logger.debug("id: " + contactId + " phone: " + phoneNumber + " lookupKey: " + lookupKey);
+					if (lookupKey != null && !TestUtil.empty(phoneNumber)) {
+						ContactMatchKeyPhone matchKey = new ContactMatchKeyPhone();
+						matchKey.contactId = contactId;
+						matchKey.lookupKey = lookupKey;
+						matchKey.phoneNumber = phoneNumber;
+						phoneNumbers.put(phoneNumber, matchKey);
 					}
 				}
 			}
 		}
 
-		return phones;
+		return phoneNumbers;
 	}
 
 	private Map<String, ContactMatchKeyEmail> readEmails() {
-		Map<String, ContactMatchKeyEmail> emails = new HashMap<String, ContactMatchKeyEmail>();
+		Map<String, ContactMatchKeyEmail> emails = new HashMap<>();
 		String selection = null;
 
 		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -380,7 +369,7 @@ public class SynchronizeContactsRoutine implements Runnable {
 		try (Cursor emailsCursor = this.contentResolver.query(
 			ContactsContract.CommonDataKinds.Email.CONTENT_URI,
 			new String[]{
-				ContactsContract.CommonDataKinds.Email._ID,
+				ContactsContract.CommonDataKinds.Email.CONTACT_ID,
 				ContactsContract.CommonDataKinds.Email.LOOKUP_KEY,
 				ContactsContract.CommonDataKinds.Email.DATA
 			},
@@ -388,8 +377,8 @@ public class SynchronizeContactsRoutine implements Runnable {
 			null,
 			null)) {
 
-			if (emailsCursor != null) {
-				int idColumnIndex = emailsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email._ID);
+			if (emailsCursor != null && emailsCursor.getCount() > 0) {
+				int idColumnIndex = emailsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.CONTACT_ID);
 				final int lookupKeyColumnIndex = emailsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.LOOKUP_KEY);
 				final int emailIndex = emailsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
 
@@ -398,7 +387,7 @@ public class SynchronizeContactsRoutine implements Runnable {
 					String lookupKey = emailsCursor.getString(lookupKeyColumnIndex);
 					String email = emailsCursor.getString(emailIndex);
 
-//				logger.debug("id: " + contactId + " email: " + email + " inDefaultDirectory: " + inDefaultDirectory);
+//					logger.debug("id: " + contactId + " email: " + email + " lookupKey: " + lookupKey);
 					if (lookupKey != null && !TestUtil.empty(email)) {
 						ContactMatchKeyEmail matchKey = new ContactMatchKeyEmail();
 						matchKey.contactId = contactId;

+ 9 - 6
app/src/main/java/ch/threema/app/services/MessageServiceImpl.java

@@ -1724,7 +1724,7 @@ public class MessageServiceImpl implements MessageService {
 		if(newModel) {
 			this.fireOnCreatedMessage(messageModel);
 
-			if (canDownload(messageModel.getMessageContentsType())) {
+			if (canDownload(messageModel)) {
 				if (fileData.getFileSize() <= FILE_AUTO_DOWNLOAD_MAX_SIZE_ISO) {
 					downloadMediaMessage(messageModel, null);
 				}
@@ -1787,13 +1787,16 @@ public class MessageServiceImpl implements MessageService {
 		return false;
 	}
 
-	private boolean canDownload(@MessageContentsType int messageContentsType) {
+	private boolean canDownload(@NonNull AbstractMessageModel messageModel) {
 		MessageType type = MessageType.FILE;
 
-		if (messageContentsType == MessageContentsType.IMAGE) {
-			type = MessageType.IMAGE;
-		} else if (messageContentsType == MessageContentsType.VIDEO) {
-			type = MessageType.VIDEO;
+		if (messageModel.getFileData() != null && messageModel.getFileData().getRenderingType() != FileData.RENDERING_DEFAULT) {
+			// treat media with default (file) rendering like a file for the sake of auto-download
+			if (messageModel.getMessageContentsType() == MessageContentsType.IMAGE) {
+				type = MessageType.IMAGE;
+			} else if (messageModel.getMessageContentsType() == MessageContentsType.VIDEO) {
+				type = MessageType.VIDEO;
+			}
 		}
 
 		if (preferenceService != null) {

+ 6 - 0
app/src/main/java/ch/threema/app/ui/AvatarEditView.java

@@ -528,6 +528,12 @@ public class AvatarEditView extends FrameLayout implements DefaultLifecycleObser
 		} else if (this.avatarData.getGroupModel() != null) {
 			return isEditable && groupService.isGroupOwner(this.avatarData.getGroupModel());
 		}
+
+		// we have neither a group model nor a contact model => user is creating a new group
+		if (this.avatarData.getContactModel() == null && this.avatarData.getGroupModel() == null) {
+			return isEditable;
+		}
+
 		return false;
 	}
 

+ 5 - 8
app/src/main/java/ch/threema/app/utils/ConfigUtils.java

@@ -34,7 +34,6 @@ import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -75,6 +74,7 @@ import java.util.Locale;
 
 import javax.net.ssl.SSLSocketFactory;
 
+import androidx.annotation.AnyRes;
 import androidx.annotation.AttrRes;
 import androidx.annotation.ColorInt;
 import androidx.annotation.DrawableRes;
@@ -316,13 +316,10 @@ public class ConfigUtils {
 		return color;
 	}
 
-	public static ColorStateList getColorStateListFromAttribute(Context context, @AttrRes int attr) {
-		TypedArray typedArray = context.getTheme().obtainStyledAttributes(new int[] { attr });
-		ColorStateList color = typedArray.getColorStateList(0);
-
-		typedArray.recycle();
-
-		return color;
+	public static @AnyRes int getResourceFromAttribute(Context context, @AttrRes final int attr) {
+		final TypedValue typedValue = new TypedValue();
+		context.getTheme().resolveAttribute(attr, typedValue, true);
+		return typedValue.resourceId;
 	}
 
 	public static @ColorInt int getPrimaryColor() {

+ 25 - 13
app/src/main/java/ch/threema/app/voip/activities/CallActivity.java

@@ -737,7 +737,11 @@ public class CallActivity extends ThreemaActivity implements
 
 		adjustPipLayout();
 
-		restoreState(getIntent(), savedInstanceState);
+		if (!restoreState(getIntent(), savedInstanceState)) {
+			logger.info("Unable to init state. Finishing");
+			finish();
+			return;
+		}
 
 		// Check for mandatory permissions
 		logger.debug("Checking for audio permission...");
@@ -812,7 +816,6 @@ public class CallActivity extends ThreemaActivity implements
 		// Activity mode sanity checks
 		if (this.activityMode == MODE_INCOMING_CALL && callState.isIdle()) {
 			logger.error("Started CallActivity (incoming call) when call state is IDLE");
-			finish();
 			return false;
 		}
 
@@ -822,6 +825,10 @@ public class CallActivity extends ThreemaActivity implements
 
 		// Fetch contact
 		this.contact = contactService.getByIdentity(contactIdentity);
+		if (this.contact == null) {
+			logger.info("Contact is null");
+			return false;
+		}
 
 		return true;
 	}
@@ -926,6 +933,9 @@ public class CallActivity extends ThreemaActivity implements
 				logger.error("Error in initializeActivity", e);
 				this.abortWithError();
 			}
+		} else {
+			logger.info("Unable to restore state");
+			this.abortWithError();
 		}
 	}
 
@@ -986,6 +996,7 @@ public class CallActivity extends ThreemaActivity implements
 			if (incomingVideo || outgoingVideo) {
 				// Make video views visible
 				this.videoViews.fullscreenVideoRenderer.setVisibility(View.VISIBLE);
+				this.commonViews.backgroundView.setVisibility(View.INVISIBLE);
 
 				this.videoViews.switchCamButton.setVisibility(outgoingVideo &&
 					(voipStateService.getVideoContext() != null && voipStateService.getVideoContext().hasMultipleCameras()) ?
@@ -1005,6 +1016,7 @@ public class CallActivity extends ThreemaActivity implements
 				this.videoViews.fullscreenVideoRenderer.setVisibility(View.GONE);
 				this.videoViews.switchCamButton.setVisibility(View.GONE);
 				this.videoViews.pipButton.setVisibility(View.GONE);
+				this.commonViews.backgroundView.setVisibility(View.VISIBLE);
 			}
 		}
 	}
@@ -1453,7 +1465,7 @@ public class CallActivity extends ThreemaActivity implements
 					videoContext.setFrameDimensions(x, y);
 				}
 			});
-			this.videoViews.fullscreenVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
+			this.videoViews.fullscreenVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_BALANCED);
 			this.videoViews.fullscreenVideoRenderer.setMirror(false);
 			this.videoViews.fullscreenVideoRenderer.setOnClickListener(new View.OnClickListener() {
 				@Override
@@ -1465,7 +1477,7 @@ public class CallActivity extends ThreemaActivity implements
 			});
 
 			this.videoViews.pipVideoRenderer.init(videoContext.getEglBaseContext(), null);
-			this.videoViews.pipVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
+			this.videoViews.pipVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_BALANCED);
 			this.videoViews.pipVideoRenderer.setMirror(true);
 			this.videoViews.pipVideoRenderer.setZOrderMediaOverlay(true);
 			this.videoViews.pipVideoRenderer.setOnClickListener(v -> this.setSwappedFeeds(!this.isSwappedFeeds));
@@ -1501,14 +1513,14 @@ public class CallActivity extends ThreemaActivity implements
 
 							if (newX < layoutMargin) {
 								newX = layoutMargin;
-							} else if (newX > videoViews.fullscreenVideoRenderer.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin) {
-								newX = videoViews.fullscreenVideoRenderer.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin;
+							} else if (newX > commonViews.backgroundView.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin) {
+								newX = commonViews.backgroundView.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin;
 							}
 
 							if (newY < layoutMargin) {
 								newY = layoutMargin;
-							} else if (newY > videoViews.fullscreenVideoRenderer.getHeight() - videoViews.pipVideoRenderer.getHeight() - layoutMargin) {
-								newY = videoViews.fullscreenVideoRenderer.getHeight() - videoViews.pipVideoRenderer.getHeight() - layoutMargin;
+							} else if (newY > commonViews.backgroundView.getHeight() - videoViews.pipVideoRenderer.getHeight() - layoutMargin) {
+								newY = commonViews.backgroundView.getHeight() - videoViews.pipVideoRenderer.getHeight() - layoutMargin;
 							}
 
 							v.animate()
@@ -1559,7 +1571,7 @@ public class CallActivity extends ThreemaActivity implements
 
 		int topSnap = callerContainer.getBottom() + layoutMargin;
 		int bottomSnap = commonViews.inCallButtonContainer.getTop() - videoViews.pipVideoRenderer.getHeight() - layoutMargin;
-		int rightSnap = videoViews.fullscreenVideoRenderer.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin;
+		int rightSnap = commonViews.backgroundView.getWidth() - videoViews.pipVideoRenderer.getWidth() - layoutMargin;
 		int snappedX, snappedY;
 
 		pipPosition = 0;
@@ -1992,10 +2004,10 @@ public class CallActivity extends ThreemaActivity implements
 			aspectRatio = new Rational(videoContext.getFrameHeight(), videoContext.getFrameWidth());
 		}
 
-		launchBounds = new Rect(this.videoViews.fullscreenVideoRenderer.getLeft(),
-			this.videoViews.fullscreenVideoRenderer.getTop(),
-			this.videoViews.fullscreenVideoRenderer.getRight(),
-			this.videoViews.fullscreenVideoRenderer.getBottom());
+		launchBounds = new Rect(this.commonViews.backgroundView.getLeft(),
+			this.commonViews.backgroundView.getTop(),
+			this.commonViews.backgroundView.getRight(),
+			this.commonViews.backgroundView.getBottom());
 
 		PictureInPictureParams pipParams = new PictureInPictureParams.Builder()
 			.setAspectRatio(aspectRatio)

+ 6 - 1
app/src/main/java/ch/threema/app/voip/services/VoipStateService.java

@@ -1329,7 +1329,12 @@ public class VoipStateService implements AudioManager.OnAudioFocusChangeListener
 					}
 				}
 			} catch (ExecutionException e) {
-				logger.info("cancelOnWearable: ExecutionException while trying to connect to wearable: {}", e.getMessage());
+				final String message = e.getMessage();
+				if (message != null && message.contains("Wearable.API is not available on this device")) {
+					logger.debug("cancelOnWearable: ExecutionException while trying to connect to wearable: {}", message);
+				} else {
+					logger.info("cancelOnWearable: ExecutionException while trying to connect to wearable: {}", message);
+				}
 			} catch (InterruptedException e) {
 				logger.info("cancelOnWearable: Interrupted while waiting for wearable client");
 				// Restore interrupted state...

+ 7 - 4
app/src/main/java/ch/threema/app/voip/util/VideoCapturerUtil.java

@@ -128,20 +128,23 @@ public class VideoCapturerUtil {
 		}
 
 		final String[] deviceNames = enumerator.getDeviceNames();
+		logger.info("Found {} camera devices", deviceNames.length);
 		for (String deviceName : deviceNames) {
 			if (enumerator.isFrontFacing(deviceName)) {
 				if (frontCamera == null) {
+					logger.info("Using {} as front camera", deviceName);
 					frontCamera = deviceName;
+				} else {
+					logger.info("Not using {} as front camera", deviceName);
 				}
 			} else if (enumerator.isBackFacing(deviceName)) {
 				if (backCamera == null) {
+					logger.info("Using {} as back camera", deviceName);
 					backCamera = deviceName;
+				} else {
+					logger.info("Not using {} as back camera", deviceName);
 				}
 			}
-
-			if (frontCamera != null && backCamera != null) {
-				break;
-			}
 		}
 		return new Pair<>(frontCamera, backCamera);
 	}

+ 7 - 1
app/src/main/java/ch/threema/app/webclient/webrtc/PeerConnectionWrapper.java

@@ -327,12 +327,18 @@ public class PeerConnectionWrapper {
 			// Ignore without m-line
 			if (candidate.getSdpMLineIndex() == null) {
 				logger.warn(
-					"Received candidate without SdpMLineIndex, ignoring: %s",
+					"Received candidate without SdpMLineIndex, ignoring: {}",
 					candidate.getSdp()
 				);
 				continue;
 			}
 
+			// Ignore candidates with empty SDP
+			if (candidate.getSdp() == null || candidate.getSdp().trim().equals("")) {
+				logger.warn("Received candidate with empty SDP, ignoring");
+				continue;
+			}
+
 			// Ignore IPv6 (if requested)
 			if (!this.allowIpv6 && SdpUtil.isIpv6Candidate(candidate.getSdp())) {
 				logger.info("Ignoring IPv6 candidate due to settings: {}", candidate.getSdp());

+ 6 - 4
app/src/main/res/layout/activity_call.xml

@@ -4,7 +4,8 @@
 	xmlns:tools="http://schemas.android.com/tools"
 	android:id="@+id/call_layout"
 	android:layout_width="match_parent"
-	android:layout_height="match_parent">
+	android:layout_height="match_parent"
+	android:background="@color/gallery_background">
 
 	<!-- Background image (for audio calls) -->
 	<ImageView
@@ -21,12 +22,13 @@
 	<!-- Gradient on top of fullscreen renderer for better readability -->
 	<org.webrtc.SurfaceViewRenderer
 		android:id="@+id/fullscreen_video_view"
-		android:layout_width="match_parent"
-		android:layout_height="match_parent"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
 		android:visibility="gone"
 		app:layout_constraintEnd_toEndOf="parent"
 		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toTopOf="parent" />
+		app:layout_constraintTop_toTopOf="parent"
+		app:layout_constraintBottom_toBottomOf="parent"/>
 
 	<View
 		android:id="@+id/fullscreen_video_view_gradient"

+ 2 - 1
app/src/main/res/layout/activity_send_media.xml

@@ -74,7 +74,8 @@
 			android:layout_alignParentTop="true"
 			android:layout_width="match_parent"
 			android:layout_height="48dp"
-			android:background="#77000000">
+			android:background="#77000000"
+			android:visibility="gone">
 
 			<ImageButton
 				android:id="@+id/flip"

+ 2 - 12
app/src/main/res/layout/activity_wizard.xml

@@ -51,15 +51,6 @@
 		android:layout_width="match_parent"
 		android:layout_height="match_parent"/>
 
-	<FrameLayout
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_gravity="bottom">
-		<!--
-			<include layout="@layout/new_wizard_info"/>
-		-->
-	</FrameLayout>
-
 	<View
 		android:id="@+id/divider"
 		android:layout_width="match_parent"
@@ -92,9 +83,8 @@
 				android:paddingTop="10dp"
 				android:scaleX="-1"
 				android:src="@drawable/ic_next_alpha"
-				android:tint="@color/new_wizard_color_accent"
 				android:visibility="visible"
-				/>
+				custom:tint="@color/new_wizard_color_accent" />
 
 			<Button
 				android:id="@+id/prev_text"
@@ -143,7 +133,7 @@
 				android:paddingBottom="10dp"
 				android:paddingTop="10dp"
 				android:src="@drawable/ic_next_alpha"
-				android:tint="@color/new_wizard_color_accent"/>
+				custom:tint="@color/new_wizard_color_accent" />
 
 			<androidx.appcompat.widget.AppCompatButton
 				android:id="@+id/next_text"

+ 0 - 1
app/src/main/res/layout/conversation_list_item_quote.xml

@@ -111,7 +111,6 @@
 		android:layout_height="wrap_content"
 		android:visibility="gone"
 		android:paddingTop="36dp"
-		android:background="?attr/chat_bubble_fade_send"
 		app:layout_constraintLeft_toLeftOf="parent"
 		app:layout_constraintLeft_toRightOf="parent"
 		app:layout_constraintBottom_toBottomOf="@id/text_view">

+ 1 - 13
app/src/main/res/layout/conversation_list_item_quote_recv.xml

@@ -34,17 +34,5 @@
 		<include layout="@layout/conversation_bubble_footer_recv"/>
 
 	</LinearLayout>
-<!--
-	<ImageView
-		android:id="@+id/quote_icon"
-		android:layout_width="24dp"
-		android:layout_height="24dp"
-		android:layout_toRightOf="@id/message_block"
-		android:layout_centerVertical="true"
-		android:layout_marginLeft="12dp"
-		app:srcCompat="@drawable/ic_quote_outline"
-		android:tint="?attr/textColorTertiary"
-		android:visibility="gone"
-		/>
--->
+
 </RelativeLayout>

+ 1 - 13
app/src/main/res/layout/conversation_list_item_quote_send.xml

@@ -33,17 +33,5 @@
 		<include layout="@layout/conversation_bubble_footer_send"/>
 
 	</LinearLayout>
-<!--
-	<ImageView
-		android:id="@+id/quote_icon"
-		android:layout_width="24dp"
-		android:layout_height="24dp"
-		android:layout_alignParentLeft="true"
-		android:layout_centerVertical="true"
-		android:layout_marginLeft="12dp"
-		app:srcCompat="@drawable/ic_quote_outline"
-		android:tint="?attr/textColorTertiary"
-		android:visibility="gone"
-		/>
--->
+
 </RelativeLayout>

+ 16 - 13
app/src/main/res/layout/conversation_list_item_recv.xml

@@ -45,21 +45,24 @@
 					android:ellipsize="end"
 					android:maxLength="@integer/max_bubble_text_length"/>
 
-		</FrameLayout>
+			<FrameLayout
+				android:id="@+id/read_on_container"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:paddingTop="36dp"
+				android:background="?attr/chat_bubble_fade_recv"
+				android:layout_gravity="bottom"
+				android:visibility="gone">
 
-		<FrameLayout
-			android:id="@+id/read_on_container"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:visibility="gone">
+				<com.google.android.material.chip.Chip
+					android:id="@+id/read_on_button"
+					style="@style/Threema.Chip.VideoTranscoder"
+					android:layout_width="wrap_content"
+					android:layout_height="wrap_content"
+					android:layout_gravity="center_horizontal"
+					android:text="@string/read_on" />
 
-			<com.google.android.material.chip.Chip
-				android:id="@+id/read_on_button"
-				style="@style/Threema.Chip.VideoTranscoder"
-				android:layout_width="wrap_content"
-				android:layout_height="wrap_content"
-				android:layout_gravity="center_horizontal"
-				android:text="@string/read_on" />
+			</FrameLayout>
 
 		</FrameLayout>
 

+ 3 - 3
app/src/main/res/layout/fragment_wizard.xml

@@ -58,9 +58,9 @@
 					android:layout_width="wrap_content"
 					android:layout_height="wrap_content"
 					app:srcCompat="@drawable/ic_about_outline"
-					android:tint="@color/new_wizard_color_primary"
 					android:textSize="14sp"
-					android:textColor="@color/new_wizard_color_accent"/>
+					android:textColor="@color/new_wizard_color_accent"
+					app:tint="@color/new_wizard_color_primary" />
 
 				<TextView
 						android:id="@+id/wizard_id_info"
@@ -69,7 +69,7 @@
 						android:layout_marginLeft="8dp"
 						android:text="@string/new_wizard_more_information"
 						android:fontFamily="sans-serif-light"
-						android:textSize="14sp"
+						android:textSize="@dimen/wizard_small_text_size"
 						android:textColor="@color/new_wizard_color_accent"/>
 
 			</LinearLayout>

+ 0 - 1
app/src/main/res/layout/fragment_wizard0.xml

@@ -28,7 +28,6 @@
 			android:text="@string/new_wizard_this_is_your_id"
 			android:layout_marginTop="@dimen/wizard_paragraph_height"/>
 
-
 	<ch.threema.app.ui.TextViewRobotoMedium
 			style="@style/WizardTitleText"
 			android:id="@+id/wizard_id_title"

+ 2 - 5
app/src/main/res/layout/fragment_wizard1.xml

@@ -24,7 +24,7 @@
 		android:layout_height="wrap_content"
 		android:layout_below="@id/safe_enable_title"
 		android:layout_centerHorizontal="true"
-		android:layout_marginTop="32dp"
+		android:layout_marginTop="@dimen/wizard_paragraph_height"
 		android:gravity="center_horizontal"
 		android:text="@string/safe_configure_choose_password"/>
 
@@ -51,10 +51,7 @@
 			android:nextFocusForward="@+id/safe_password2"
 			android:singleLine="true"
 			android:textIsSelectable="false"
-			android:hint="@string/password_hint">
-
-			<requestFocus/>
-		</ch.threema.app.ui.ThreemaEditText>
+			android:hint="@string/password_hint"/>
 
 	</com.google.android.material.textfield.TextInputLayout>
 

+ 1 - 3
app/src/main/res/layout/fragment_wizard3.xml

@@ -79,9 +79,7 @@
 						android:layout_marginLeft="1dp"
 						android:hint="@string/new_wizard_hint_mobile_number"
 						android:nextFocusForward="@+id/wizard_email"
-						android:inputType="phone">
-					<requestFocus/>
-				</ch.threema.app.emojis.EmojiEditText>
+						android:inputType="phone"/>
 
 			</LinearLayout>
 

+ 1 - 1
app/src/main/res/values-de/strings.xml

@@ -1291,7 +1291,7 @@ sicheren Ort gesichert oder ausgedruckt haben.</string>
 	<string name="original_file_no_longer_avilable">Kein Zugriff mehr auf die Original-Datei. Bitte senden Sie diese Nachricht erneut.</string>
 	<string name="state_transcoding">wird transcodiert</string>
 	<string name="importing_files">Dateien werden importiert</string>
-	<string name="tooltip_image_resolution_hint">Bildauflösung individuell anpassen.</string>
+	<string name="tooltip_image_resolution_hint">Hier tippen, um die Bildauflösung individuell anzupassen.</string>
 	<string name="image_labeling_stuck_error">Der Indexierungsprozess für die Bildersuche ist stehen geblieben und wurde abgebrochen. Versuchen Sie es später nochmals.</string>
 	<string name="max_selectable_media_exceeded">Es können maximal %d Objekte aufs Mal versendet werden.</string>
 	<string name="ballot_created_successfully">Die Umfrage wurde erfolgreich erstellt.</string>

+ 0 - 12
app/src/main/res/values-h533dp/dimens.xml

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- regular dimensions for screens with a height of at least 533dp -->
-<resources>
-	<dimen name="wizard_paragraph_height">42dp</dimen>
-	<dimen name="wizard_small_paragraph_height">16dp</dimen>
-	<dimen name="wizard_title_text_size">28dp</dimen>
-	<dimen name="wizard_subtitle_text_size">20dp</dimen>
-	<dimen name="wizard_summary_spacing">16dp</dimen>
-	<dimen name="wizard_button_text_size">16dp</dimen>
-	<dimen name="wizard_backup_code_edittext_minheight">80dp</dimen>
-	<dimen name="wizard_contents_padding_horizontal">16dp</dimen>
-</resources>

+ 24 - 0
app/src/main/res/values-h640dp/dimens.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<dimen name="wizard_paragraph_height">32dp</dimen>
+	<dimen name="wizard_small_paragraph_height">16dp</dimen>
+	<dimen name="wizard_title_text_size">28dp</dimen>
+	<dimen name="wizard_subtitle_text_size">20dp</dimen>
+	<dimen name="wizard_summary_spacing">16dp</dimen>
+	<dimen name="wizard_button_height">42dp</dimen>
+	<dimen name="wizard_button_text_size">16dp</dimen>
+	<dimen name="wizard_default_view_height">42dp</dimen>
+	<dimen name="wizard_backup_code_edittext_minheight">80dp</dimen>
+	<dimen name="wizard_contents_padding_horizontal">16dp</dimen>
+	<dimen name="wizard_small_text_size">15dp</dimen>
+	<dimen name="wizard_medium_text_size">17dp</dimen>
+	<dimen name="wizard_summary_title_text_size">14dp</dimen>
+	<dimen name="wizard_summary_body_text_size">16dp</dimen>
+	<dimen name="wizard_edittext_text_size">16dp</dimen>
+	<dimen name="wizard_edittext_padding">10dp</dimen>
+	<dimen name="wizard_edittext_hint_padding">6dp</dimen>
+	<!-- wizard_default_view_height + wizard_edittext_hint_padding -->
+	<dimen name="wizard_hinted_edittext_height">48dp</dimen>
+	<!-- wizard_edittext_padding + wizard_edittext_padding -->
+	<dimen name="wizard_hinted_edittext_padding_top">16dp</dimen>
+</resources>

+ 15 - 9
app/src/main/res/values/dimens.xml

@@ -154,21 +154,27 @@
 
 	<!-- wizard -->
 	<dimen name="wizard_contents_padding">16dp</dimen>
-	<dimen name="wizard_button_height">42dp</dimen>
-	<dimen name="wizard_default_view_height">42dp</dimen>
-	<dimen name="wizard_edittext_padding">10dp</dimen>
-	<dimen name="wizard_edittext_hint_padding">6dp</dimen>
+	<dimen name="wizard_button_height">32dp</dimen>
+	<dimen name="wizard_default_view_height">32dp</dimen>
+	<dimen name="wizard_edittext_text_size">14dp</dimen>
+	<dimen name="wizard_edittext_padding">8dp</dimen>
+	<dimen name="wizard_edittext_hint_padding">4dp</dimen>
 	<!-- wizard_default_view_height + wizard_edittext_hint_padding -->
-	<dimen name="wizard_hinted_edittext_height">48dp</dimen>
+	<dimen name="wizard_hinted_edittext_height">36dp</dimen>
 	<!-- wizard_edittext_padding + wizard_edittext_padding -->
-	<dimen name="wizard_hinted_edittext_padding_top">16dp</dimen>
+	<dimen name="wizard_hinted_edittext_padding_top">8dp</dimen>
 
-	<!-- reduced dimensions for screens with less than 533dp -->
+	<!-- reduced dimensions for screens with less than 640dp -->
 	<dimen name="wizard_paragraph_height">16dp</dimen>
 	<dimen name="wizard_small_paragraph_height">8dp</dimen>
-	<dimen name="wizard_title_text_size">24sp</dimen>
-	<dimen name="wizard_subtitle_text_size">16sp</dimen>
+	<dimen name="wizard_title_text_size">20dp</dimen>
+	<dimen name="wizard_subtitle_text_size">16dp</dimen>
+	<dimen name="wizard_medium_text_size">15dp</dimen>
+	<dimen name="wizard_small_text_size">14dp</dimen>
 	<dimen name="wizard_summary_spacing">12dp</dimen>
+	<dimen name="wizard_summary_title_text_size">13dp</dimen>
+	<dimen name="wizard_summary_body_text_size">15dp</dimen>
+
 	<!-- use dp for button fonts to prevent people from using large text destroying the layout -->
 	<dimen name="wizard_button_text_size">14dp</dimen>
 	<dimen name="wizard_backup_code_edittext_minheight">60dp</dimen>

+ 2 - 2
app/src/main/res/values/strings.xml

@@ -258,7 +258,7 @@
 	<string name="emoji_nature">Animals &amp; Nature</string>
 	<string name="emoji_food">Food &amp; Drink</string>
 	<string name="emoji_recent">Recent</string>
-	<string name="emoji_emotions">Smilyes &amp; People</string>
+	<string name="emoji_emotions">Smilies &amp; People</string>
 	<string name="emoji_activities">Activities</string>
 	<string name="emoji_flags">Flags</string>
 	<string name="title_lock">Lock</string>
@@ -1220,7 +1220,7 @@
 	<string name="original_file_no_longer_avilable">The original file is no longer accessible. Please re-send the message.</string>
 	<string name="state_transcoding">transcoding</string>
 	<string name="importing_files">Importing files</string>
-	<string name="tooltip_image_resolution_hint">Adjust image resolutions.</string>
+	<string name="tooltip_image_resolution_hint">Tap here to adjust image resolution.</string>
 	<string name="image_labeling_stuck_error">The indexing process for image search got stuck and was cancelled. Please retry later.</string>
 	<string name="ballot_created_successfully">The poll was successfully created.</string>
 	<string name="file_size">File size</string>

+ 11 - 12
app/src/main/res/values/styles.xml

@@ -467,14 +467,14 @@
 		<item name="android:textAlignment">center</item>
 		<item name="android:fontFamily">sans-serif-light</item>
 		<item name="android:textStyle">normal</item>
-		<item name="android:textSize">15sp</item>
+		<item name="android:textSize">@dimen/wizard_small_text_size</item>
 	</style>
 
 	<style name="WizardMediumText" parent="@android:style/TextAppearance">
 		<item name="android:textAlignment">center</item>
 		<item name="android:fontFamily">sans-serif-light</item>
 		<item name="android:textStyle">normal</item>
-		<item name="android:textSize">17sp</item>
+		<item name="android:textSize">@dimen/wizard_medium_text_size</item>
 	</style>
 
 	<style name="WizardSummaryTitleText" parent="@android:style/TextAppearance">
@@ -482,7 +482,7 @@
 		<item name="android:fontFamily">sans-serif-light</item>
 		<item name="android:textStyle">normal</item>
 		<item name="android:textColor">?android:attr/textColorSecondary</item>
-		<item name="android:textSize">14sp</item>
+		<item name="android:textSize">@dimen/wizard_summary_title_text_size</item>
 	</style>
 
 	<style name="WizardSummaryBodyText" parent="@android:style/TextAppearance">
@@ -490,7 +490,7 @@
 		<item name="android:fontFamily">sans-serif-light</item>
 		<item name="android:textStyle">normal</item>
 		<item name="android:textColor">?android:attr/textColorPrimary</item>
-		<item name="android:textSize">16sp</item>
+		<item name="android:textSize">@dimen/wizard_summary_body_text_size</item>
 	</style>
 
 	<style name="WizardEditTextIcon">
@@ -507,7 +507,7 @@
 		<item name="android:padding">@dimen/wizard_edittext_padding</item>
 		<item name="android:background">@drawable/wizard_edittext_shape_selector</item>
 		<item name="android:fontFamily">sans-serif-light</item>
-		<item name="android:textSize">16sp</item>
+		<item name="android:textSize">@dimen/wizard_edittext_text_size</item>
 		<item name="android:textColor">@drawable/wizard_edittext_selector</item>
 	</style>
 
@@ -519,7 +519,7 @@
 		<item name="android:paddingBottom">@dimen/wizard_edittext_padding</item>
 		<item name="android:background">@drawable/wizard_hinted_edittext_layerlist</item>
 		<item name="android:fontFamily">sans-serif-light</item>
-		<item name="android:textSize">16sp</item>
+		<item name="android:textSize">@dimen/wizard_edittext_text_size</item>
 		<item name="android:textColor">@drawable/wizard_edittext_selector</item>
 	</style>
 
@@ -529,7 +529,7 @@
 		<item name="android:paddingRight">16dp</item>
 		<item name="android:background">@drawable/shape_switch</item>
 		<item name="android:fontFamily">sans-serif-light</item>
-		<item name="android:textSize">16sp</item>
+		<item name="android:textSize">@dimen/wizard_button_text_size</item>
 	</style>
 
 	<style name="WizardSwitchSmallText">
@@ -538,7 +538,7 @@
 		<item name="android:paddingRight">16dp</item>
 		<item name="android:background">@drawable/shape_switch</item>
 		<item name="android:fontFamily">sans-serif-light</item>
-		<item name="android:textSize">15sp</item>
+		<item name="android:textSize">@dimen/wizard_small_text_size</item>
 	</style>
 
 	<style name="WizardSpinner" parent="Widget.AppCompat.Spinner">
@@ -710,11 +710,10 @@
 	</style>
 
 	<style name="Threema.PopupMenuStyle" parent="@style/Widget.AppCompat.PopupMenu">
-		<item name="dropdownListPreferredItemHeight">38dp</item>
+		<item name="dropdownListPreferredItemHeight">40dp</item>
 		<item name="android:paddingLeft">0dp</item>
-		<item name="android:paddingRight">-4dp</item>
+		<item name="android:paddingRight">0dp</item>
 		<item name="android:layout_marginRight">0dp</item>
-		<item name="android:textSize">14sp</item>
+		<item name="android:textSize">15sp</item>
 	</style>
-
 </resources>

+ 1 - 1
app/src/main/res/values/themes.xml

@@ -90,7 +90,7 @@
 		<item name="textColorSecondary">@color/settings_second_line_text_color</item>
 		<item name="toolbarStyle">@style/Threema.ToolbarStyle</item>
 		<item name="background_secondary">@color/activity_background_secondary</item>
-		<item name="settings_multipane_selection_bg">@color/text_color_second_line_dark</item>
+		<item name="settings_multipane_selection_bg">@color/settings_multipane_selection_bg_light</item>
 
 		<!-- Status bar and navigation bar color -->
 		<item name="android:windowLightStatusBar" tools:targetApi="23">true</item>