Update camera layout for better support across different screen sizes.

This commit is contained in:
Alex Hart 2022-09-07 14:55:39 -03:00 committed by Greyson Parrelli
parent 993e49db48
commit 24b7593178
20 changed files with 407 additions and 299 deletions

View file

@ -554,7 +554,7 @@
</activity> </activity>
<activity android:name=".mediasend.AvatarSelectionActivity" <activity android:name=".mediasend.AvatarSelectionActivity"
android:theme="@style/TextSecure.FullScreenMedia" android:theme="@style/TextSecure.DarkNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".blocked.BlockedUsersActivity" <activity android:name=".blocked.BlockedUsersActivity"
@ -659,7 +659,7 @@
<activity android:name=".wallpaper.crop.WallpaperImageSelectionActivity" <activity android:name=".wallpaper.crop.WallpaperImageSelectionActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.FullScreenMedia" /> android:theme="@style/TextSecure.DarkNoActionBar" />
<activity android:name=".wallpaper.crop.WallpaperCropActivity" <activity android:name=".wallpaper.crop.WallpaperCropActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"

View file

@ -5,6 +5,7 @@ import android.content.Intent;
import android.graphics.Point; import android.graphics.Point;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -62,6 +63,11 @@ public class AvatarSelectionActivity extends AppCompatActivity implements Camera
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
);
setContentView(R.layout.avatar_selection_activity); setContentView(R.layout.avatar_selection_activity);
if (isGalleryFirst()) { if (isGalleryFirst()) {

View file

@ -27,6 +27,11 @@ import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.view.ViewKt;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.MultiTransformation; import com.bumptech.glide.load.MultiTransformation;
@ -48,6 +53,7 @@ import org.thoughtcrime.securesms.stories.viewer.page.StoryDisplay;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.Stopwatch; import org.signal.core.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -125,7 +131,7 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
View cameraParent = view.findViewById(R.id.camera_preview_parent); View cameraParent = view.findViewById(R.id.camera_preview_parent);
onOrientationChanged(getResources().getConfiguration().orientation); onOrientationChanged();
cameraPreview.setSurfaceTextureListener(this); cameraPreview.setSurfaceTextureListener(this);
@ -134,7 +140,7 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
view.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { view.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
// Let's assume portrait for now, so 9:16 // Let's assume portrait for now, so 9:16
float aspectRatio = CameraFragment.getAspectRatioForOrientation(getResources().getConfiguration().orientation); float aspectRatio = CameraFragment.getAspectRatioForOrientation(Configuration.ORIENTATION_PORTRAIT);
float width = right - left; float width = right - left;
float height = Math.min((1f / aspectRatio) * width, bottom - top); float height = Math.min((1f / aspectRatio) * width, bottom - top);
@ -231,12 +237,6 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
}); });
} }
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onOrientationChanged(newConfig.orientation);
}
@Override @Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.d(TAG, "onSurfaceTextureAvailable"); Log.d(TAG, "onSurfaceTextureAvailable");
@ -322,25 +322,13 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
View galleryButton = requireView().findViewById(R.id.camera_gallery_button); View galleryButton = requireView().findViewById(R.id.camera_gallery_button);
View countButton = requireView().findViewById(R.id.camera_review_button); View countButton = requireView().findViewById(R.id.camera_review_button);
View toggleSpacer = requireView().findViewById(R.id.toggle_spacer);
mostRecentItemDisposable.dispose(); mostRecentItemDisposable.dispose();
mostRecentItemDisposable = controller.getMostRecentMediaItem() mostRecentItemDisposable = controller.getMostRecentMediaItem()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(item -> presentRecentItemThumbnail(item.orElse(null))); .subscribe(item -> presentRecentItemThumbnail(item.orElse(null)));
if (toggleSpacer != null) { initializeViewFinderAndControlsPositioning();
if (Stories.isFeatureEnabled()) {
StoryDisplay storyDisplay = StoryDisplay.Companion.getStoryDisplay(getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
if (storyDisplay == StoryDisplay.SMALL) {
toggleSpacer.setVisibility(View.VISIBLE);
} else {
toggleSpacer.setVisibility(View.GONE);
}
} else {
toggleSpacer.setVisibility(View.GONE);
}
}
captureButton.setOnClickListener(v -> { captureButton.setOnClickListener(v -> {
captureButton.setEnabled(false); captureButton.setEnabled(false);
@ -368,6 +356,27 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
countButton.setOnClickListener(v -> controller.onCameraCountButtonClicked()); countButton.setOnClickListener(v -> controller.onCameraCountButtonClicked());
} }
private void initializeViewFinderAndControlsPositioning() {
CardView cameraCard = requireView().findViewById(R.id.camera_preview_parent);
View controls = requireView().findViewById(R.id.camera_controls_container);
CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireView());
if (!cameraDisplay.getRoundViewFinderCorners()) {
cameraCard.setRadius(0f);
}
ViewUtil.setBottomMargin(controls, cameraDisplay.getCameraCaptureMarginBottom(getResources()));
if (cameraDisplay.getCameraViewportGravity() == CameraDisplay.CameraViewportGravity.CENTER) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone((ConstraintLayout) requireView());
constraintSet.connect(R.id.camera_preview_parent, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
constraintSet.applyTo((ConstraintLayout) requireView());
} else {
ViewUtil.setBottomMargin(cameraCard, cameraDisplay.getCameraViewportMarginBottom());
}
}
private void onCaptureClicked() { private void onCaptureClicked() {
orderEnforcer.reset(); orderEnforcer.reset();
@ -426,9 +435,8 @@ public class Camera1Fragment extends LoggingFragment implements CameraFragment,
return new PointF(scaleX, scaleY); return new PointF(scaleX, scaleY);
} }
private void onOrientationChanged(int orientation) { private void onOrientationChanged() {
int layout = orientation == Configuration.ORIENTATION_PORTRAIT ? R.layout.camera_controls_portrait int layout = R.layout.camera_controls_portrait;
: R.layout.camera_controls_landscape;
controlsContainer.removeAllViews(); controlsContainer.removeAllViews();
controlsContainer.addView(LayoutInflater.from(getContext()).inflate(layout, controlsContainer, false)); controlsContainer.addView(LayoutInflater.from(getContext()).inflate(layout, controlsContainer, false));

View file

@ -0,0 +1,139 @@
package org.thoughtcrime.securesms.mediasend
import android.content.res.Resources
import android.view.View
import androidx.annotation.Dimension
import androidx.annotation.Px
import androidx.window.WindowManager
import org.signal.core.util.dp
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.stories.Stories
/**
* Description of the Camera Viewport, Controls, and Toggle position information.
*/
enum class CameraDisplay(
private val aspectRatio: Float,
val roundViewFinderCorners: Boolean,
private val withTogglePositionInfo: PositionInfo,
private val withoutTogglePositionInfo: PositionInfo,
@Dimension(unit = Dimension.DP) private val toggleBottomMargin: Int
) {
DISPLAY_20_9(
aspectRatio = 9f / 20f,
roundViewFinderCorners = true,
withTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 130,
cameraViewportMarginBottomDp = 106,
cameraViewportGravity = CameraViewportGravity.BOTTOM
),
withoutTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 130,
cameraViewportGravity = CameraViewportGravity.CENTER
),
toggleBottomMargin = 52
),
DISPLAY_19_9(
aspectRatio = 9f / 19f,
roundViewFinderCorners = true,
withTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 128,
cameraViewportMarginBottomDp = 104,
cameraViewportGravity = CameraViewportGravity.BOTTOM
),
withoutTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 128,
cameraViewportGravity = CameraViewportGravity.CENTER
),
toggleBottomMargin = 52
),
DISPLAY_18_9(
aspectRatio = 9f / 18f,
roundViewFinderCorners = true,
withTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 120,
cameraViewportGravity = CameraViewportGravity.CENTER
),
withoutTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 84,
cameraViewportGravity = CameraViewportGravity.CENTER
),
toggleBottomMargin = 54
),
DISPLAY_16_9(
aspectRatio = 9f / 16f,
roundViewFinderCorners = false,
withTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 120,
cameraViewportGravity = CameraViewportGravity.BOTTOM
),
withoutTogglePositionInfo = PositionInfo(
cameraCaptureMarginBottomDp = 84,
cameraViewportGravity = CameraViewportGravity.BOTTOM
),
toggleBottomMargin = 54
);
@Px
fun getCameraCaptureMarginBottom(resources: Resources): Int {
val positionInfo = if (Stories.isFeatureEnabled()) withTogglePositionInfo else withoutTogglePositionInfo
return positionInfo.cameraCaptureMarginBottomDp.dp - getCameraButtonSizeOffset(resources)
}
@Px
fun getCameraViewportMarginBottom(): Int {
val positionInfo = if (Stories.isFeatureEnabled()) withTogglePositionInfo else withoutTogglePositionInfo
return positionInfo.cameraViewportMarginBottomDp.dp
}
fun getCameraViewportGravity(): CameraViewportGravity {
val positionInfo = if (Stories.isFeatureEnabled()) withTogglePositionInfo else withoutTogglePositionInfo
return positionInfo.cameraViewportGravity
}
@Px
fun getToggleBottomMargin(): Int {
return toggleBottomMargin.dp
}
companion object {
@Px
@JvmStatic
private fun getCameraButtonSizeOffset(resources: Resources): Int {
val cameraCaptureButtonSize = resources.getDimensionPixelSize(R.dimen.camera_capture_button_size)
val cameraCaptureImageButtonSize = resources.getDimensionPixelSize(R.dimen.camera_capture_image_button_size)
return (cameraCaptureButtonSize - cameraCaptureImageButtonSize) / 2
}
@JvmStatic
fun getDisplay(view: View): CameraDisplay {
val windowManager = WindowManager(view.context)
val windowMetrics = windowManager.getCurrentWindowMetrics()
val width = windowMetrics.bounds.width()
val height = windowMetrics.bounds.height()
val aspectRatio = width.toFloat() / height
return when {
aspectRatio <= DISPLAY_20_9.aspectRatio -> DISPLAY_20_9
aspectRatio <= DISPLAY_19_9.aspectRatio -> DISPLAY_19_9
aspectRatio <= DISPLAY_18_9.aspectRatio -> DISPLAY_18_9
else -> DISPLAY_16_9
}
}
}
enum class CameraViewportGravity {
CENTER,
BOTTOM
}
data class PositionInfo(
@Dimension(unit = Dimension.DP) val cameraCaptureMarginBottomDp: Int,
@Dimension(unit = Dimension.DP) val cameraViewportMarginBottomDp: Int = 0,
val cameraViewportGravity: CameraViewportGravity
)
}

View file

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.mediasend;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.camera.view.video.ExperimentalVideo; import androidx.camera.view.video.ExperimentalVideo;

View file

@ -25,21 +25,23 @@ import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException; import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy; import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.view.CameraController; import androidx.camera.view.CameraController;
import androidx.camera.view.LifecycleCameraController; import androidx.camera.view.LifecycleCameraController;
import androidx.camera.view.PreviewView; import androidx.camera.view.PreviewView;
import androidx.camera.view.video.ExperimentalVideo; import androidx.camera.view.video.ExperimentalVideo;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.util.Executors; import com.bumptech.glide.util.Executors;
import org.signal.core.util.Stopwatch;
import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.LoggingFragment; import org.thoughtcrime.securesms.LoggingFragment;
@ -52,11 +54,9 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaAnimations;
import org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton; import org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.stories.viewer.page.StoryDisplay;
import org.thoughtcrime.securesms.util.MemoryFileDescriptor; import org.thoughtcrime.securesms.util.MemoryFileDescriptor;
import org.signal.core.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.video.VideoUtil; import org.thoughtcrime.securesms.video.VideoUtil;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@ -147,11 +147,11 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
previewView.setScaleType(PREVIEW_SCALE_TYPE); previewView.setScaleType(PREVIEW_SCALE_TYPE);
previewView.setController(cameraController); previewView.setController(cameraController);
onOrientationChanged(getResources().getConfiguration().orientation); onOrientationChanged();
view.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { view.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
// Let's assume portrait for now, so 9:16 // Let's assume portrait for now, so 9:16
float aspectRatio = CameraFragment.getAspectRatioForOrientation(getResources().getConfiguration().orientation); float aspectRatio = CameraFragment.getAspectRatioForOrientation(Configuration.ORIENTATION_PORTRAIT);
float width = right - left; float width = right - left;
float height = Math.min((1f / aspectRatio) * width, bottom - top); float height = Math.min((1f / aspectRatio) * width, bottom - top);
@ -176,6 +176,11 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} }
@Override
public void onPause() {
super.onPause();
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
@ -184,12 +189,6 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); requireActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} }
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onOrientationChanged(newConfig.orientation);
}
@Override @Override
public void fadeOutControls(@NonNull Runnable onEndAction) { public void fadeOutControls(@NonNull Runnable onEndAction) {
controlsContainer.setEnabled(false); controlsContainer.setEnabled(false);
@ -221,12 +220,11 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
}); });
} }
private void onOrientationChanged(int orientation) { private void onOrientationChanged() {
int layout = orientation == Configuration.ORIENTATION_PORTRAIT ? R.layout.camera_controls_portrait int layout = R.layout.camera_controls_portrait;
: R.layout.camera_controls_landscape;
int resolution = CameraXUtil.getIdealResolution(Resources.getSystem().getDisplayMetrics().widthPixels, Resources.getSystem().getDisplayMetrics().heightPixels); int resolution = CameraXUtil.getIdealResolution(Resources.getSystem().getDisplayMetrics().widthPixels, Resources.getSystem().getDisplayMetrics().heightPixels);
Size size = CameraXUtil.buildResolutionForRatio(resolution, ASPECT_RATIO_16_9, orientation == Configuration.ORIENTATION_PORTRAIT); Size size = CameraXUtil.buildResolutionForRatio(resolution, ASPECT_RATIO_16_9, true);
CameraController.OutputSize outputSize = new CameraController.OutputSize(size); CameraController.OutputSize outputSize = new CameraController.OutputSize(size);
cameraController.setImageCaptureTargetSize(outputSize); cameraController.setImageCaptureTargetSize(outputSize);
@ -280,28 +278,37 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
} }
} }
@SuppressLint({"ClickableViewAccessibility", "MissingPermission"}) private void initializeViewFinderAndControlsPositioning() {
private void initControls() { CardView cameraCard = requireView().findViewById(R.id.camerax_camera_parent);
View flipButton = requireView().findViewById(R.id.camera_flip_button); View controls = requireView().findViewById(R.id.camerax_controls_container);
CameraButtonView captureButton = requireView().findViewById(R.id.camera_capture_button); CameraDisplay cameraDisplay = CameraDisplay.getDisplay(requireView());
View galleryButton = requireView().findViewById(R.id.camera_gallery_button);
View countButton = requireView().findViewById(R.id.camera_review_button);
CameraXFlashToggleView flashButton = requireView().findViewById(R.id.camera_flash_button);
View toggleSpacer = requireView().findViewById(R.id.toggle_spacer);
if (toggleSpacer != null) { if (!cameraDisplay.getRoundViewFinderCorners()) {
if ( Stories.isFeatureEnabled()) { cameraCard.setRadius(0f);
StoryDisplay storyDisplay = StoryDisplay.Companion.getStoryDisplay(getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
if (storyDisplay == StoryDisplay.SMALL) {
toggleSpacer.setVisibility(View.VISIBLE);
} else {
toggleSpacer.setVisibility(View.GONE);
}
} else {
toggleSpacer.setVisibility(View.GONE);
}
} }
ViewUtil.setBottomMargin(controls, cameraDisplay.getCameraCaptureMarginBottom(getResources()));
if (cameraDisplay.getCameraViewportGravity() == CameraDisplay.CameraViewportGravity.CENTER) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone((ConstraintLayout) requireView());
constraintSet.connect(R.id.camerax_camera_parent, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
constraintSet.applyTo((ConstraintLayout) requireView());
} else {
ViewUtil.setBottomMargin(cameraCard, cameraDisplay.getCameraViewportMarginBottom());
}
}
@SuppressLint({ "ClickableViewAccessibility", "MissingPermission" })
private void initControls() {
View flipButton = requireView().findViewById(R.id.camera_flip_button);
CameraButtonView captureButton = requireView().findViewById(R.id.camera_capture_button);
View galleryButton = requireView().findViewById(R.id.camera_gallery_button);
View countButton = requireView().findViewById(R.id.camera_review_button);
CameraXFlashToggleView flashButton = requireView().findViewById(R.id.camera_flash_button);
initializeViewFinderAndControlsPositioning();
mostRecentItemDisposable.dispose(); mostRecentItemDisposable.dispose();
mostRecentItemDisposable = controller.getMostRecentMediaItem() mostRecentItemDisposable = controller.getMostRecentMediaItem()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -392,10 +399,10 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
} }
private boolean isVideoRecordingSupported(@NonNull Context context) { private boolean isVideoRecordingSupported(@NonNull Context context) {
return Build.VERSION.SDK_INT >= 26 && return Build.VERSION.SDK_INT >= 26 &&
requireArguments().getBoolean(IS_VIDEO_ENABLED, true) && requireArguments().getBoolean(IS_VIDEO_ENABLED, true) &&
MediaConstraints.isVideoTranscodeAvailable() && MediaConstraints.isVideoTranscodeAvailable() &&
CameraXUtil.isMixedModeSupported(context) && CameraXUtil.isMixedModeSupported(context) &&
VideoUtil.getMaxVideoRecordDurationInSeconds(context, controller.getMediaConstraints()) > 0; VideoUtil.getMaxVideoRecordDurationInSeconds(context, controller.getMediaConstraints()) > 0;
} }
@ -509,7 +516,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
} }
} }
@SuppressLint({"MissingPermission"}) @SuppressLint({ "MissingPermission" })
private void initializeFlipButton(@NonNull View flipButton, @NonNull CameraXFlashToggleView flashButton) { private void initializeFlipButton(@NonNull View flipButton, @NonNull CameraXFlashToggleView flashButton) {
if (getContext() == null) { if (getContext() == null) {
Log.w(TAG, "initializeFlipButton called either before or after fragment was attached."); Log.w(TAG, "initializeFlipButton called either before or after fragment was attached.");
@ -518,7 +525,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment {
if (cameraController.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) && cameraController.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA)) { if (cameraController.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) && cameraController.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA)) {
flipButton.setVisibility(View.VISIBLE); flipButton.setVisibility(View.VISIBLE);
flipButton.setOnClickListener(v -> { flipButton.setOnClickListener(v -> {
cameraController.setCameraSelector(cameraController.getCameraSelector() == CameraSelector.DEFAULT_FRONT_CAMERA cameraController.setCameraSelector(cameraController.getCameraSelector() == CameraSelector.DEFAULT_FRONT_CAMERA
? CameraSelector.DEFAULT_BACK_CAMERA ? CameraSelector.DEFAULT_BACK_CAMERA
: CameraSelector.DEFAULT_FRONT_CAMERA); : CameraSelector.DEFAULT_FRONT_CAMERA);

View file

@ -6,6 +6,8 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
@ -13,6 +15,7 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
@ -29,6 +32,7 @@ import org.thoughtcrime.securesms.conversation.MessageSendType
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
import org.thoughtcrime.securesms.mediasend.CameraDisplay
import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
import org.thoughtcrime.securesms.mediasend.v2.review.MediaReviewFragment import org.thoughtcrime.securesms.mediasend.v2.review.MediaReviewFragment
@ -80,6 +84,10 @@ class MediaSelectionActivity :
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
setContentView(R.layout.media_selection_activity) setContentView(R.layout.media_selection_activity)
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
)
val sendType: MessageSendType = requireNotNull(intent.getParcelableExtra(MESSAGE_SEND_TYPE)) val sendType: MessageSendType = requireNotNull(intent.getParcelableExtra(MESSAGE_SEND_TYPE))
val initialMedia: List<Media> = intent.getParcelableArrayListExtra(MEDIA) ?: listOf() val initialMedia: List<Media> = intent.getParcelableArrayListExtra(MEDIA) ?: listOf()
val message: CharSequence? = if (shareToTextStory) null else draftText val message: CharSequence? = if (shareToTextStory) null else draftText
@ -89,6 +97,12 @@ class MediaSelectionActivity :
viewModel = ViewModelProvider(this, factory)[MediaSelectionViewModel::class.java] viewModel = ViewModelProvider(this, factory)[MediaSelectionViewModel::class.java]
val textStoryToggle: ConstraintLayout = findViewById(R.id.switch_widget) val textStoryToggle: ConstraintLayout = findViewById(R.id.switch_widget)
val cameraDisplay = CameraDisplay.getDisplay(textStoryToggle)
textStoryToggle.updateLayoutParams<FrameLayout.LayoutParams> {
bottomMargin = cameraDisplay.getToggleBottomMargin()
}
val cameraSelectedConstraintSet = ConstraintSet().apply { val cameraSelectedConstraintSet = ConstraintSet().apply {
clone(textStoryToggle) clone(textStoryToggle)
} }

View file

@ -3,20 +3,20 @@ package org.thoughtcrime.securesms.mediasend.v2.gallery
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.Toolbar import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import org.signal.core.util.Stopwatch import org.signal.core.util.Stopwatch
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.recyclerview.GridDividerDecoration import org.thoughtcrime.securesms.components.recyclerview.GridDividerDecoration
import org.thoughtcrime.securesms.databinding.V2MediaGalleryFragmentBinding
import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mediasend.MediaRepository import org.thoughtcrime.securesms.mediasend.MediaRepository
import org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton
import org.thoughtcrime.securesms.util.Material3OnScrollHelper import org.thoughtcrime.securesms.util.Material3OnScrollHelper
import org.thoughtcrime.securesms.util.ViewUtil import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
@ -36,12 +36,6 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
private lateinit var callbacks: Callbacks private lateinit var callbacks: Callbacks
private lateinit var toolbar: Toolbar
private lateinit var galleryRecycler: RecyclerView
private lateinit var countButton: MediaCountIndicatorButton
private lateinit var bottomBarGroup: View
private lateinit var selectedRecycler: RecyclerView
private var selectedMediaTouchHelper: ItemTouchHelper? = null private var selectedMediaTouchHelper: ItemTouchHelper? = null
private val galleryAdapter = MappingAdapter() private val galleryAdapter = MappingAdapter()
@ -57,29 +51,39 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
callbacks = requireListener() callbacks = requireListener()
val binding = V2MediaGalleryFragmentBinding.bind(view)
toolbar = view.findViewById(R.id.media_gallery_toolbar) binding.root.setPadding(
galleryRecycler = view.findViewById(R.id.media_gallery_grid) 0,
selectedRecycler = view.findViewById(R.id.media_gallery_selected) 0,
countButton = view.findViewById(R.id.media_gallery_count_button) 0,
bottomBarGroup = view.findViewById(R.id.media_gallery_bottom_bar_group) ViewUtil.getNavigationBarHeight(view)
)
(galleryRecycler.layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { binding.mediaGalleryToolbar.updateLayoutParams<ConstraintLayout.LayoutParams> {
topMargin = ViewUtil.getStatusBarHeight(view)
}
binding.mediaGalleryStatusBarBackground.updateLayoutParams {
height = ViewUtil.getStatusBarHeight(view)
}
(binding.mediaGalleryGrid.layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int { override fun getSpanSize(position: Int): Int {
val isFolder: Boolean = (galleryRecycler.adapter as MappingAdapter).getModel(position).map { it is MediaGallerySelectableItem.FolderModel }.orElse(false) val isFolder: Boolean = (binding.mediaGalleryGrid.adapter as MappingAdapter).getModel(position).map { it is MediaGallerySelectableItem.FolderModel }.orElse(false)
return if (isFolder) 2 else 1 return if (isFolder) 2 else 1
} }
} }
toolbar.setNavigationOnClickListener { binding.mediaGalleryToolbar.setNavigationOnClickListener {
onBack() onBack()
} }
Material3OnScrollHelper(requireActivity(), toolbar).attach(galleryRecycler) Material3OnScrollHelper(requireActivity(), listOf(binding.mediaGalleryToolbar, binding.mediaGalleryStatusBarBackground)).attach(binding.mediaGalleryGrid)
if (callbacks.isCameraEnabled()) { if (callbacks.isCameraEnabled()) {
toolbar.setOnMenuItemClickListener { item -> binding.mediaGalleryToolbar.setOnMenuItemClickListener { item ->
if (item.itemId == R.id.action_camera) { if (item.itemId == R.id.action_camera) {
callbacks.onNavigateToCamera() callbacks.onNavigateToCamera()
true true
@ -88,18 +92,18 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
} }
} }
} else { } else {
toolbar.menu.findItem(R.id.action_camera).isVisible = false binding.mediaGalleryToolbar.menu.findItem(R.id.action_camera).isVisible = false
} }
countButton.setOnClickListener { binding.mediaGalleryCountButton.setOnClickListener {
callbacks.onSubmit() callbacks.onSubmit()
} }
MediaGallerySelectedItem.register(selectedAdapter) { media -> MediaGallerySelectedItem.register(selectedAdapter) { media ->
callbacks.onSelectedMediaClicked(media) callbacks.onSelectedMediaClicked(media)
} }
selectedRecycler.adapter = selectedAdapter binding.mediaGallerySelected.adapter = selectedAdapter
selectedMediaTouchHelper?.attachToRecyclerView(selectedRecycler) selectedMediaTouchHelper?.attachToRecyclerView(binding.mediaGallerySelected)
MediaGallerySelectableItem.registerAdapter( MediaGallerySelectableItem.registerAdapter(
mappingAdapter = galleryAdapter, mappingAdapter = galleryAdapter,
@ -117,25 +121,25 @@ class MediaGalleryFragment : Fragment(R.layout.v2_media_gallery_fragment) {
callbacks.isMultiselectEnabled() callbacks.isMultiselectEnabled()
) )
galleryRecycler.adapter = galleryAdapter binding.mediaGalleryGrid.adapter = galleryAdapter
galleryRecycler.addItemDecoration(GridDividerDecoration(4, ViewUtil.dpToPx(2))) binding.mediaGalleryGrid.addItemDecoration(GridDividerDecoration(4, ViewUtil.dpToPx(2)))
viewStateLiveData.observe(viewLifecycleOwner) { state -> viewStateLiveData.observe(viewLifecycleOwner) { state ->
bottomBarGroup.visible = state.selectedMedia.isNotEmpty() binding.mediaGalleryBottomBarGroup.visible = state.selectedMedia.isNotEmpty()
countButton.setCount(state.selectedMedia.size) binding.mediaGalleryCountButton.setCount(state.selectedMedia.size)
val stopwatch = Stopwatch("mediaSubmit") val stopwatch = Stopwatch("mediaSubmit")
selectedAdapter.submitList(state.selectedMedia.map { MediaGallerySelectedItem.Model(it) }) { selectedAdapter.submitList(state.selectedMedia.map { MediaGallerySelectedItem.Model(it) }) {
stopwatch.split("after-submit") stopwatch.split("after-submit")
stopwatch.stop("MediaGalleryFragment") stopwatch.stop("MediaGalleryFragment")
if (state.selectedMedia.isNotEmpty()) { if (state.selectedMedia.isNotEmpty()) {
selectedRecycler.smoothScrollToPosition(state.selectedMedia.size - 1) binding.mediaGallerySelected.smoothScrollToPosition(state.selectedMedia.size - 1)
} }
} }
} }
viewModel.state.observe(viewLifecycleOwner) { state -> viewModel.state.observe(viewLifecycleOwner) { state ->
toolbar.title = state.bucketTitle ?: requireContext().getString(R.string.AttachmentKeyboard_gallery) binding.mediaGalleryToolbar.title = state.bucketTitle ?: requireContext().getString(R.string.AttachmentKeyboard_gallery)
} }
val galleryItemsWithSelection = LiveDataUtil.combineLatest( val galleryItemsWithSelection = LiveDataUtil.combineLatest(

View file

@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.LifecycleDisposable import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.fragments.requireListener import org.thoughtcrime.securesms.util.fragments.requireListener
import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout
@ -90,6 +91,13 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
callback = requireListener() callback = requireListener()
view.setPadding(
0,
ViewUtil.getStatusBarHeight(view),
0,
ViewUtil.getNavigationBarHeight(view)
)
drawToolButton = view.findViewById(R.id.draw_tool) drawToolButton = view.findViewById(R.id.draw_tool)
cropAndRotateButton = view.findViewById(R.id.crop_and_rotate_tool) cropAndRotateButton = view.findViewById(R.id.crop_and_rotate_tool)
qualityButton = view.findViewById(R.id.quality_selector) qualityButton = view.findViewById(R.id.quality_selector)

View file

@ -4,10 +4,11 @@ import android.content.pm.ActivityInfo
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.widget.AppCompatImageView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.drawToBitmap import androidx.core.view.drawToBitmap
import androidx.core.view.postDelayed import androidx.core.view.postDelayed
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -15,8 +16,10 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
import org.thoughtcrime.securesms.databinding.StoriesTextPostCreationFragmentBinding
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
import org.thoughtcrime.securesms.mediasend.CameraDisplay
import org.thoughtcrime.securesms.mediasend.v2.HudCommand import org.thoughtcrime.securesms.mediasend.v2.HudCommand
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel
import org.thoughtcrime.securesms.mediasend.v2.stories.StoriesMultiselectForwardActivity import org.thoughtcrime.securesms.mediasend.v2.stories.StoriesMultiselectForwardActivity
@ -24,18 +27,14 @@ import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendReposi
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendResult import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendResult
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.stories.StoryTextPostView
import org.thoughtcrime.securesms.util.LifecycleDisposable import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
import org.thoughtcrime.securesms.util.visible import org.thoughtcrime.securesms.util.visible
class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creation_fragment), TextStoryPostTextEntryFragment.Callback, SafetyNumberBottomSheet.Callbacks { class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creation_fragment), TextStoryPostTextEntryFragment.Callback, SafetyNumberBottomSheet.Callbacks {
private lateinit var scene: ConstraintLayout private var _binding: StoriesTextPostCreationFragmentBinding? = null
private lateinit var backgroundButton: AppCompatImageView private val binding: StoriesTextPostCreationFragmentBinding get() = _binding!!
private lateinit var send: View
private lateinit var storyTextPostView: StoryTextPostView
private lateinit var sendInProgressCard: View
private val sharedViewModel: MediaSelectionViewModel by viewModels( private val sharedViewModel: MediaSelectionViewModel by viewModels(
ownerProducer = { ownerProducer = {
@ -66,16 +65,9 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
scene = view.findViewById(R.id.scene) _binding = StoriesTextPostCreationFragmentBinding.bind(view)
backgroundButton = view.findViewById(R.id.background_selector)
send = view.findViewById(R.id.send)
storyTextPostView = view.findViewById(R.id.story_text_post)
sendInProgressCard = view.findViewById(R.id.send_in_progress_indicator)
val backgroundProtection: View = view.findViewById(R.id.background_protection) binding.storyTextPost.showCloseButton()
val addLinkProtection: View = view.findViewById(R.id.add_link_protection)
storyTextPostView.showCloseButton()
lifecycleDisposable.bindTo(viewLifecycleOwner) lifecycleDisposable.bindTo(viewLifecycleOwner)
lifecycleDisposable += sharedViewModel.hudCommands.subscribe { lifecycleDisposable += sharedViewModel.hudCommands.subscribe {
@ -85,12 +77,12 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
} }
viewModel.typeface.observe(viewLifecycleOwner) { typeface -> viewModel.typeface.observe(viewLifecycleOwner) { typeface ->
storyTextPostView.setTypeface(typeface) binding.storyTextPost.setTypeface(typeface)
} }
viewModel.state.observe(viewLifecycleOwner) { state -> viewModel.state.observe(viewLifecycleOwner) { state ->
backgroundButton.background = state.backgroundColor.chatBubbleMask binding.backgroundSelector.background = state.backgroundColor.chatBubbleMask
storyTextPostView.bindFromCreationState(state) binding.storyTextPost.bindFromCreationState(state)
if (state.linkPreviewUri != null) { if (state.linkPreviewUri != null) {
linkPreviewViewModel.onTextChanged(requireContext(), state.linkPreviewUri, 0, state.linkPreviewUri.lastIndex) linkPreviewViewModel.onTextChanged(requireContext(), state.linkPreviewUri, 0, state.linkPreviewUri.lastIndex)
@ -99,32 +91,32 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
} }
val canSend = state.body.isNotEmpty() || !state.linkPreviewUri.isNullOrEmpty() val canSend = state.body.isNotEmpty() || !state.linkPreviewUri.isNullOrEmpty()
send.alpha = if (canSend) 1f else 0.5f binding.send.alpha = if (canSend) 1f else 0.5f
send.isEnabled = canSend binding.send.isEnabled = canSend
} }
LiveDataUtil.combineLatest(viewModel.state, linkPreviewViewModel.linkPreviewState) { viewState, linkState -> LiveDataUtil.combineLatest(viewModel.state, linkPreviewViewModel.linkPreviewState) { viewState, linkState ->
Pair(viewState.body.isBlank(), linkState) Pair(viewState.body.isBlank(), linkState)
}.observe(viewLifecycleOwner) { (useLargeThumb, linkState) -> }.observe(viewLifecycleOwner) { (useLargeThumb, linkState) ->
storyTextPostView.bindLinkPreviewState(linkState, View.GONE, useLargeThumb) binding.storyTextPost.bindLinkPreviewState(linkState, View.GONE, useLargeThumb)
storyTextPostView.postAdjustLinkPreviewTranslationY() binding.storyTextPost.postAdjustLinkPreviewTranslationY()
} }
storyTextPostView.setTextViewClickListener { binding.storyTextPost.setTextViewClickListener {
storyTextPostView.hidePostContent() binding.storyTextPost.hidePostContent()
storyTextPostView.isEnabled = false binding.storyTextPost.isEnabled = false
TextStoryPostTextEntryFragment().show(childFragmentManager, null) TextStoryPostTextEntryFragment().show(childFragmentManager, null)
} }
backgroundProtection.setOnClickListener { binding.backgroundProtection.setOnClickListener {
viewModel.cycleBackgroundColor() viewModel.cycleBackgroundColor()
} }
addLinkProtection.setOnClickListener { binding.addLinkProtection.setOnClickListener {
TextStoryPostLinkEntryFragment().show(childFragmentManager, null) TextStoryPostLinkEntryFragment().show(childFragmentManager, null)
} }
storyTextPostView.setLinkPreviewCloseListener { binding.storyTextPost.setLinkPreviewCloseListener {
viewModel.setLinkPreview("") viewModel.setLinkPreview("")
} }
@ -132,23 +124,23 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
performSend(it.toSet()) performSend(it.toSet())
} else { } else {
send.isClickable = true binding.send.isClickable = true
sendInProgressCard.visible = false binding.sendInProgressIndicator.visible = false
} }
} }
send.setOnClickListener { binding.send.setOnClickListener {
send.isClickable = false binding.send.isClickable = false
sendInProgressCard.visible = true binding.sendInProgressIndicator.visible = true
storyTextPostView.hideCloseButton() binding.storyTextPost.hideCloseButton()
val contacts = (sharedViewModel.destination.getRecipientSearchKeyList() + sharedViewModel.destination.getRecipientSearchKey()) val contacts = (sharedViewModel.destination.getRecipientSearchKeyList() + sharedViewModel.destination.getRecipientSearchKey())
.filterIsInstance(ContactSearchKey::class.java) .filterIsInstance(ContactSearchKey::class.java)
.toSet() .toSet()
if (contacts.isEmpty()) { if (contacts.isEmpty()) {
val bitmap = storyTextPostView.drawToBitmap() val bitmap = binding.storyTextPost.drawToBitmap()
viewModel.compressToBlob(bitmap).observeOn(AndroidSchedulers.mainThread()).subscribe { uri -> viewModel.compressToBlob(bitmap).observeOn(AndroidSchedulers.mainThread()).subscribe { uri ->
launcher.launch( launcher.launch(
StoriesMultiselectForwardActivity.Args( StoriesMultiselectForwardActivity.Args(
@ -166,18 +158,54 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
performSend(contacts) performSend(contacts)
} }
} }
initializeScenePositioning()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
storyTextPostView.showCloseButton() binding.storyTextPost.showCloseButton()
requireActivity().requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT requireActivity().requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} }
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onTextStoryPostTextEntryDismissed() { override fun onTextStoryPostTextEntryDismissed() {
storyTextPostView.postDelayed(resources.getInteger(R.integer.text_entry_exit_duration).toLong()) { binding.storyTextPost.postDelayed(resources.getInteger(R.integer.text_entry_exit_duration).toLong()) {
storyTextPostView.showPostContent() binding.storyTextPost.showPostContent()
storyTextPostView.isEnabled = true binding.storyTextPost.isEnabled = true
}
}
private fun initializeScenePositioning() {
val cameraDisplay = CameraDisplay.getDisplay(requireView())
if (!cameraDisplay.roundViewFinderCorners) {
binding.storyTextPostCard.radius = 0f
}
binding.send.updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin = cameraDisplay.getToggleBottomMargin()
}
listOf(binding.backgroundProtection, binding.addLinkProtection).forEach {
it.updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin += cameraDisplay.getCameraCaptureMarginBottom(resources)
}
}
if (cameraDisplay.getCameraViewportGravity() == CameraDisplay.CameraViewportGravity.CENTER) {
val constraintSet = ConstraintSet()
constraintSet.clone(binding.scene)
constraintSet.connect(R.id.story_text_post_card, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
constraintSet.applyTo(binding.scene)
} else {
binding.storyTextPostCard.updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin = cameraDisplay.getCameraViewportMarginBottom()
}
} }
} }
@ -196,8 +224,8 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
requireActivity().finish() requireActivity().finish()
} }
is TextStoryPostSendResult.UntrustedRecordsError -> { is TextStoryPostSendResult.UntrustedRecordsError -> {
send.isClickable = true binding.send.isClickable = true
sendInProgressCard.visible = false binding.sendInProgressIndicator.visible = false
SafetyNumberBottomSheet SafetyNumberBottomSheet
.forIdentityRecordsAndDestinations(result.untrustedRecords, contacts.toList()) .forIdentityRecordsAndDestinations(result.untrustedRecords, contacts.toList())

View file

@ -224,6 +224,15 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
Mode mode = Mode.getByCode(requireArguments().getString(KEY_MODE)); Mode mode = Mode.getByCode(requireArguments().getString(KEY_MODE));
if (mode == Mode.AVATAR_CAPTURE || mode == Mode.AVATAR_EDIT) {
view.setPadding(
0,
ViewUtil.getStatusBarHeight(view),
0,
ViewUtil.getNavigationBarHeight(view)
);
}
imageEditorHud = view.findViewById(R.id.scribble_hud); imageEditorHud = view.findViewById(R.id.scribble_hud);
imageEditorView = view.findViewById(R.id.image_editor_view); imageEditorView = view.findViewById(R.id.image_editor_view);

View file

@ -4,6 +4,7 @@ import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -40,6 +41,11 @@ public final class WallpaperImageSelectionActivity extends AppCompatActivity
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
);
setContentView(R.layout.wallpaper_image_selection_activity); setContentView(R.layout.wallpaper_image_selection_activity);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()

View file

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.thoughtcrime.securesms.mediasend.CameraButtonView
android:id="@+id/camera_capture_button"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_marginEnd="18dp"
android:contentDescription="@string/CameraXFragment_capture_description"
app:imageCaptureSize="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:recordSize="42dp" />
<org.thoughtcrime.securesms.mediasend.camerax.CameraXFlashToggleView
android:id="@+id/camera_flash_button"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginStart="16dp"
android:layout_marginTop="14dp"
android:background="@drawable/circle_transparent_black_40"
android:padding="6dp"
android:src="@drawable/camerax_flash_toggle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/camera_flip_button"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginTop="36dp"
android:layout_marginEnd="40dp"
android:background="@drawable/media_selection_camera_switch_background"
android:contentDescription="@string/CameraXFragment_change_camera_description"
android:scaleType="centerInside"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_switch_camera_28"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/camera_gallery_button_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:layout_marginBottom="36dp"
android:background="@drawable/circle_tintable"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/camera_gallery_button"
android:layout_width="52dp"
android:layout_height="52dp"
android:contentDescription="@string/CameraXFragment_open_gallery_description"
android:padding="2dp"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Circle"
tools:src="@color/black" />
</FrameLayout>
<org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton
android:id="@+id/camera_review_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:layout_marginBottom="36dp"
android:background="@drawable/v2_media_count_indicator_background"
android:minHeight="44dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/camera_capture_button"
tools:visibility="visible" />
<View
android:id="@+id/camera_selfie_flash"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
android:background="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -8,11 +8,11 @@
<org.thoughtcrime.securesms.mediasend.CameraButtonView <org.thoughtcrime.securesms.mediasend.CameraButtonView
android:id="@+id/camera_capture_button" android:id="@+id/camera_capture_button"
android:layout_width="124dp" android:layout_width="@dimen/camera_capture_button_size"
android:layout_height="124dp" android:layout_height="@dimen/camera_capture_button_size"
android:contentDescription="@string/CameraXFragment_capture_description" android:contentDescription="@string/CameraXFragment_capture_description"
app:imageCaptureSize="76dp" app:imageCaptureSize="@dimen/camera_capture_image_button_size"
app:layout_constraintBottom_toTopOf="@id/toggle_spacer" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:recordSize="54dp" /> app:recordSize="54dp" />
@ -40,7 +40,7 @@
android:contentDescription="@string/CameraXFragment_change_camera_description" android:contentDescription="@string/CameraXFragment_change_camera_description"
android:scaleType="centerInside" android:scaleType="centerInside"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/toggle_spacer" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_switch_camera_28" app:srcCompat="@drawable/ic_switch_camera_28"
tools:visibility="visible" /> tools:visibility="visible" />
@ -52,7 +52,7 @@
android:layout_marginEnd="40dp" android:layout_marginEnd="40dp"
android:layout_marginBottom="36dp" android:layout_marginBottom="36dp"
android:background="@drawable/circle_tintable" android:background="@drawable/circle_tintable"
app:layout_constraintBottom_toTopOf="@id/toggle_spacer" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"> app:layout_constraintEnd_toEndOf="parent">
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
@ -91,14 +91,5 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/toggle_spacer"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -12,7 +12,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="18dp" app:cardCornerRadius="18dp"
app:cardElevation="0dp" app:cardElevation="0dp"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintBottom_toBottomOf="parent">
<TextureView <TextureView
android:id="@+id/camera_preview" android:id="@+id/camera_preview"
@ -25,7 +25,7 @@
android:id="@+id/camera_controls_container" android:id="@+id/camera_controls_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/camera_preview_parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="@id/camera_preview_parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
tools:viewBindingIgnore="true">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/camerax_camera_parent" android:id="@+id/camerax_camera_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
app:cardCornerRadius="18dp" app:cardCornerRadius="18dp"
app:cardElevation="0dp" app:cardElevation="0dp"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintBottom_toBottomOf="parent">
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
android:id="@+id/camerax_camera" android:id="@+id/camerax_camera"
@ -26,7 +26,7 @@
android:id="@+id/camerax_controls_container" android:id="@+id/camerax_controls_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/camerax_camera_parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="@id/camerax_camera_parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
tools:viewBindingIgnore="true">
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container" android:id="@+id/fragment_container"
@ -15,8 +15,7 @@
android:id="@+id/switch_widget" android:id="@+id/switch_widget"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="bottom|center_horizontal">
android:layout_marginBottom="8dp">
<View <View
android:id="@+id/selected_pill" android:id="@+id/selected_pill"
@ -31,8 +30,9 @@
<TextView <TextView
android:id="@+id/camera_switch" android:id="@+id/camera_switch"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="36sp" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:minHeight="36sp"
android:paddingHorizontal="18dp" android:paddingHorizontal="18dp"
android:shadowColor="@color/black" android:shadowColor="@color/black"
android:text="@string/MediaSelectionActivity__camera" android:text="@string/MediaSelectionActivity__camera"
@ -47,8 +47,9 @@
<TextView <TextView
android:id="@+id/text_switch" android:id="@+id/text_switch"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="36sp" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:minHeight="36sp"
android:paddingHorizontal="18dp" android:paddingHorizontal="18dp"
android:shadowColor="@color/black" android:shadowColor="@color/black"
android:shadowRadius="3" android:shadowRadius="3"

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scene" android:id="@+id/scene"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -11,15 +10,12 @@
android:id="@+id/story_text_post_card" android:id="@+id/story_text_post_card"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginBottom="12dp"
app:cardCornerRadius="18dp" app:cardCornerRadius="18dp"
app:cardElevation="0dp" app:cardElevation="0dp"
app:layout_constraintBottom_toTopOf="@id/toggle_spacer" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="9:16" app:layout_constraintDimensionRatio="9:16"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent">
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0">
<org.thoughtcrime.securesms.stories.StoryTextPostView <org.thoughtcrime.securesms.stories.StoryTextPostView
android:id="@+id/story_text_post" android:id="@+id/story_text_post"
@ -36,7 +32,7 @@
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:padding="2dp" android:padding="2dp"
android:src="@drawable/circle_tintable" android:src="@drawable/circle_tintable"
app:layout_constraintBottom_toTopOf="@id/button_bar_barrier" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/story_text_post_card" app:layout_constraintStart_toStartOf="@id/story_text_post_card"
app:tint="@color/transparent_black_40" /> app:tint="@color/transparent_black_40" />
@ -60,7 +56,7 @@
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:padding="2dp" android:padding="2dp"
android:src="@drawable/circle_tintable" android:src="@drawable/circle_tintable"
app:layout_constraintBottom_toTopOf="@id/button_bar_barrier" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/background_protection" app:layout_constraintStart_toEndOf="@id/background_protection"
app:tint="@color/transparent_black_40" /> app:tint="@color/transparent_black_40" />
@ -84,35 +80,12 @@
android:background="@color/signal_colorOnSecondaryContainer" android:background="@color/signal_colorOnSecondaryContainer"
android:padding="4dp" android:padding="4dp"
android:scaleType="centerInside" android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="@id/toggle_spacer" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/toggle_spacer"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Circle" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Circle"
app:srcCompat="@drawable/ic_arrow_end_24" app:srcCompat="@drawable/ic_arrow_end_24"
app:tint="@color/signal_colorSecondaryContainer" /> app:tint="@color/signal_colorSecondaryContainer" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/button_bar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="barrier_helper,toggle_spacer" />
<View
android:id="@+id/barrier_helper"
android:layout_width="1dp"
android:layout_height="1dp"
app:layout_constraintTop_toBottomOf="@id/story_text_post_card" />
<View
android:id="@+id/toggle_spacer"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/send_in_progress_indicator" android:id="@+id/send_in_progress_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,11 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<View
android:id="@+id/media_gallery_status_bar_background"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/media_gallery_toolbar"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/media_gallery_toolbar" android:id="@+id/media_gallery_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -31,6 +31,8 @@
<dimen name="album_5_cell_size_small">69dp</dimen> <dimen name="album_5_cell_size_small">69dp</dimen>
<dimen name="camera_contacts_horizontal_margin">16dp</dimen> <dimen name="camera_contacts_horizontal_margin">16dp</dimen>
<dimen name="camera_capture_button_size">124dp</dimen>
<dimen name="camera_capture_image_button_size">76dp</dimen>
<dimen name="message_corner_radius">18dp</dimen> <dimen name="message_corner_radius">18dp</dimen>
<dimen name="message_corner_collapse_radius">4dp</dimen> <dimen name="message_corner_collapse_radius">4dp</dimen>