/** * Wire * Copyright (C) 2016 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.waz.zclient.pages.main.profile.camera; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.TextView; import com.waz.api.ImageAsset; import com.waz.api.ImageAssetFactory; import com.waz.utils.wrappers.URI; import com.waz.zclient.OnBackPressedListener; import com.waz.zclient.R; import com.waz.zclient.camera.CameraPreviewObserver; import com.waz.zclient.camera.FlashMode; import com.waz.zclient.camera.views.CameraPreviewTextureView; import com.waz.zclient.controllers.accentcolor.AccentColorObserver; import com.waz.zclient.controllers.camera.CameraActionObserver; import com.waz.zclient.controllers.drawing.DrawingController; import com.waz.zclient.controllers.drawing.IDrawingController; import com.waz.zclient.controllers.orientation.OrientationControllerObserver; import com.waz.zclient.pages.BaseFragment; import com.waz.zclient.pages.extendedcursor.image.ImagePreviewLayout; import com.waz.zclient.pages.main.conversation.AssetIntentsManager; import com.waz.zclient.pages.main.profile.camera.controls.CameraBottomControl; import com.waz.zclient.pages.main.profile.camera.controls.CameraTopControl; import com.waz.zclient.ui.animation.interpolators.penner.Expo; import com.waz.zclient.utils.LayoutSpec; import com.waz.zclient.utils.SquareOrientation; import com.waz.zclient.utils.ViewUtils; import com.waz.zclient.views.ProgressView; import java.util.Set; public class CameraFragment extends BaseFragment<CameraFragment.Container> implements CameraPreviewObserver, OrientationControllerObserver, AccentColorObserver, OnBackPressedListener, ImagePreviewLayout.Callback, CameraTopControl.CameraTopControlCallback, CameraBottomControl.CameraBottomControlCallback { public static final String TAG = CameraFragment.class.getName(); private static final String CAMERA_CONTEXT = "CAMERA_CONTEXT"; private static final String SHOW_GALLERY = "SHOW_GALLERY"; private static final String ALREADY_OPENED_GALLERY = "ALREADY_OPENED_GALLERY"; private FrameLayout imagePreviewContainer; private ProgressView previewProgressBar; private CameraPreviewTextureView cameraPreview; private TextView cameraNotAvailableTextView; private ImageAsset imageAsset; private CameraTopControl cameraTopControl; private CameraBottomControl cameraBottomControl; private CameraFocusView focusView; private AssetIntentsManager intentsManager; private CameraContext cameraContext = null; private boolean alreadyOpenedGallery; private int cameraPreviewAnimationDuration; private int cameraControlAnimationDuration; //TODO pictureFromCamera is for tracking only, try to remove private boolean pictureFromCamera; public static CameraFragment newInstance(CameraContext cameraContext) { return newInstance(cameraContext, false); } public static CameraFragment newInstance(CameraContext cameraContext, boolean showGallery) { CameraFragment fragment = new CameraFragment(); Bundle bundle = new Bundle(); bundle.putInt(CAMERA_CONTEXT, cameraContext.ordinal()); bundle.putBoolean(SHOW_GALLERY, showGallery); fragment.setArguments(bundle); return fragment; } @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { // opening from profile ensureCameraContext(); if (nextAnim == R.anim.camera__from__profile__transition) { int controlHeight = getResources().getDimensionPixelSize(R.dimen.camera__control__height); return new ProfileToCameraAnimation(enter, getResources().getInteger(R.integer.framework_animation_duration_medium), 0, controlHeight, 0); } return super.onCreateAnimation(transit, enter, nextAnim); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); intentsManager = new AssetIntentsManager(getActivity(), new AssetIntentsManager.Callback() { @Override public void onDataReceived(AssetIntentsManager.IntentType type, URI uri) { processGalleryImage(uri); } @Override public void onCanceled(AssetIntentsManager.IntentType type) { showCameraFeed(); } @Override public void onFailed(AssetIntentsManager.IntentType type) { showCameraFeed(); } @Override public void openIntent(Intent intent, AssetIntentsManager.IntentType intentType) { startActivityForResult(intent, intentType.requestCode); } @Override public void onPermissionFailed(AssetIntentsManager.IntentType type) {} }, savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup c, Bundle savedInstanceState) { ensureCameraContext(); final View view = inflater.inflate(R.layout.fragment_camera, c, false); //TODO allow selection of a camera 'facing' for different cameraContexts cameraPreview = ViewUtils.getView(view, R.id.cptv__camera_preview); cameraPreview.setObserver(this); cameraNotAvailableTextView = ViewUtils.getView(view, R.id.ttv__camera_not_available_message); cameraTopControl = ViewUtils.getView(view, R.id.ctp_top_controls); cameraTopControl.setCameraTopControlCallback(this); cameraTopControl.setAlpha(0); cameraTopControl.setVisibility(View.VISIBLE); cameraBottomControl = ViewUtils.getView(view, R.id.cbc__bottom_controls); cameraBottomControl.setCameraBottomControlCallback(this); cameraBottomControl.setMode(cameraContext); cameraBottomControl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // do nothing but consume event } }); imagePreviewContainer = ViewUtils.getView(view, R.id.fl__preview_container); if (cameraContext != CameraContext.MESSAGE) { imagePreviewContainer.setVisibility(View.GONE); } imagePreviewContainer.setVisibility(View.GONE); previewProgressBar = ViewUtils.getView(view, R.id.pv__preview); previewProgressBar.setVisibility(View.GONE); focusView = ViewUtils.getView(view, R.id.cfv__focus); if (savedInstanceState != null) { alreadyOpenedGallery = savedInstanceState.getBoolean(ALREADY_OPENED_GALLERY); } cameraControlAnimationDuration = getResources().getInteger(R.integer.camera__control__ainmation__duration); cameraPreviewAnimationDuration = getResources().getInteger(R.integer.camera__preview__ainmation__duration); view.setBackgroundResource(R.color.black); return view; } @Override public void onStart() { super.onStart(); getControllerFactory().getAccentColorController().addAccentColorObserver(this); if (LayoutSpec.isPhone(getActivity())) { getControllerFactory().getOrientationController().addOrientationControllerObserver(this); } } @Override public void onSaveInstanceState(Bundle outState) { outState.putBoolean(ALREADY_OPENED_GALLERY, alreadyOpenedGallery); intentsManager.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public void onStop() { getControllerFactory().getAccentColorController().removeAccentColorObserver(this); getControllerFactory().getOrientationController().removeOrientationControllerObserver(this); super.onStop(); } @Override public void onDestroyView() { hideCameraFeed(); imagePreviewContainer = null; previewProgressBar = null; imageAsset = null; focusView = null; cameraTopControl = null; cameraBottomControl.animate() .translationY(getView().getMeasuredHeight()) .setDuration(cameraControlAnimationDuration) .setInterpolator(new Expo.EaseIn()); super.onDestroyView(); } @Override public boolean onBackPressed() { onClose(); return true; } public CameraContext getCameraContext() { return cameraContext; } private void ensureCameraContext() { if (cameraContext != null) { return; } cameraContext = CameraContext.values()[getArguments().getInt(CAMERA_CONTEXT)]; } private void disableCameraButtons() { if (getView() == null) { return; } ViewUtils.getView(getView(), R.id.gtv__camera_control__take_a_picture).setVisibility(View.GONE); ViewUtils.getView(getView(), R.id.gtv__camera__top_control__change_camera).setVisibility(View.GONE); ViewUtils.getView(getView(), R.id.gtv__camera__top_control__flash_setting).setVisibility(View.GONE); } @Override public void onCameraLoaded(Set<FlashMode> flashModes) { cameraTopControl.setFlashStates(flashModes, cameraPreview.getCurrentFlashMode()); cameraTopControl.enableCameraSwitchButtion(cameraPreview.getNumberOfCameras() > 1); showCameraFeed(); boolean openGalleryArg = getArguments().getBoolean(SHOW_GALLERY); if (!alreadyOpenedGallery && openGalleryArg) { alreadyOpenedGallery = true; openGallery(); } cameraNotAvailableTextView.setVisibility(View.GONE); } @Override public void onCameraLoadingFailed() { if (getContainer() != null) { getControllerFactory().getCameraController().onCameraNotAvailable(cameraContext); } disableCameraButtons(); cameraNotAvailableTextView.setVisibility(View.VISIBLE); } @Override public void onCameraReleased() { //no need to override since we don't exit the app } @Override public void onPictureTaken(ImageAsset imageAsset) { this.imageAsset = imageAsset; showPreview(imageAsset, true); } @Override public void onFocusBegin(Rect focusArea) { focusView.setColor(getControllerFactory().getAccentColorController().getColor()); int x = focusArea.centerX(); int y = focusArea.centerY(); focusView.setX(x - focusView.getWidth() / 2); focusView.setY(y - focusView.getHeight() / 2); focusView.showFocusView(); } @Override public void onFocusComplete() { if (focusView != null) { focusView.hideFocusView(); } } public void openGallery() { intentsManager.openGallery(); getActivity().overridePendingTransition(R.anim.camera_in, R.anim.camera_out); } @Override public void nextCamera() { cameraPreview.nextCamera(); } @Override public void setFlashMode(FlashMode mode) { cameraPreview.setFlashMode(mode); } @Override public FlashMode getFlashMode() { return cameraPreview.getCurrentFlashMode(); } @Override public void onClose() { cameraPreview.setFlashMode(FlashMode.OFF); //set back to default off when leaving camera getControllerFactory().getCameraController().closeCamera(cameraContext); } @Override public void onTakePhoto() { if (cameraContext != CameraContext.SIGN_UP) { previewProgressBar.setVisibility(View.VISIBLE); } ViewUtils.fadeOutView(cameraTopControl, cameraControlAnimationDuration); cameraPreview.takePicture(); } @Override public void onOpenImageGallery() { openGallery(); } @Override public void onCancelPreview() { previewProgressBar.setVisibility(View.GONE); ObjectAnimator animator = ObjectAnimator.ofFloat(imagePreviewContainer, View.ALPHA, 1, 0); animator.setDuration(cameraControlAnimationDuration); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { hideImagePreviewOnAnimationEnd(); } @Override public void onAnimationEnd(Animator animation) { hideImagePreviewOnAnimationEnd(); } }); animator.start(); showCameraFeed(); } @Override public void onSketchOnPreviewPicture(ImageAsset imageAsset, ImagePreviewLayout.Source source, DrawingController.DrawingMethod method) { getControllerFactory().getDrawingController().showDrawing(imageAsset, IDrawingController.DrawingDestination.CAMERA_PREVIEW_VIEW); } @Override public void onSendPictureFromPreview(ImageAsset imageAsset, ImagePreviewLayout.Source source) { getControllerFactory().getCameraController().onBitmapSelected(imageAsset, pictureFromCamera, cameraContext); } private void showPreview(ImageAsset imageAsset, boolean bitmapFromCamera) { pictureFromCamera = bitmapFromCamera; hideCameraFeed(); previewProgressBar.setVisibility(View.GONE); ImagePreviewLayout imagePreviewLayout = (ImagePreviewLayout) LayoutInflater.from(getContext()).inflate( R.layout.fragment_cursor_images_preview, imagePreviewContainer, false); imagePreviewLayout.showSketch(cameraContext == CameraContext.MESSAGE); String previewTitle = cameraContext == CameraContext.MESSAGE ? getStoreFactory().getConversationStore().getCurrentConversation().getName() : ""; imagePreviewLayout.setImageAsset(imageAsset, ImagePreviewLayout.Source.CAMERA, this); imagePreviewLayout.setAccentColor(getControllerFactory().getAccentColorController().getAccentColor().getColor()); imagePreviewLayout.setTitle(previewTitle); imagePreviewContainer.addView(imagePreviewLayout); imagePreviewContainer.setVisibility(View.VISIBLE); ObjectAnimator.ofFloat(imagePreviewContainer, View.ALPHA, 0, 1).setDuration(cameraPreviewAnimationDuration).start(); cameraBottomControl.setVisibility(View.GONE); } private void hideImagePreviewOnAnimationEnd() { if (imagePreviewContainer != null && cameraBottomControl != null) { imagePreviewContainer.setVisibility(View.GONE); cameraBottomControl.setVisibility(View.VISIBLE); } } private void showCameraFeed() { final Activity activity = getActivity(); if (activity != null && LayoutSpec.isTablet(activity)) { ViewUtils.lockCurrentOrientation(activity, SquareOrientation.PORTRAIT_STRAIGHT); } ViewUtils.fadeInView(cameraTopControl, cameraControlAnimationDuration); if (cameraPreview != null) { cameraPreview.setVisibility(View.VISIBLE); } cameraBottomControl.enableShutterButton(); } private void hideCameraFeed() { if (LayoutSpec.isTablet(getActivity())) { ViewUtils.unlockOrientation(getActivity()); } ViewUtils.fadeOutView(cameraTopControl, cameraControlAnimationDuration); if (cameraPreview != null) { cameraPreview.setVisibility(View.GONE); } } @Override public void onOrientationHasChanged(SquareOrientation squareOrientation) { cameraTopControl.setConfigOrientation(squareOrientation); cameraBottomControl.setConfigOrientation(squareOrientation); } @Override public void onAccentColorHasChanged(Object sender, int color) { previewProgressBar.setTextColor(color); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); intentsManager.onActivityResult(requestCode, resultCode, data); } private void processGalleryImage(URI uri) { imageAsset = null; hideCameraFeed(); if (cameraContext != CameraContext.SIGN_UP) { previewProgressBar.setVisibility(View.VISIBLE); } imageAsset = ImageAssetFactory.getImageAsset(uri); showPreview(imageAsset, false); } public interface Container extends CameraActionObserver { } }