Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellverzeichnis  MediaItem.java

  Sprache: JAVA
 

package ch.threema.app.ui;

import static ch.threema.app.preference.service.PreferenceService.IMAGE_SCALE_DEFAULT;
import static ch.threema.app.preference.service.PreferenceService.VIDEO_SIZE_DEFAULT;
import static ch.threema.app.utils.BitmapUtil.FLIP_HORIZONTAL;
import static ch.threema.app.utils.BitmapUtil.FLIP_NONE;
import static ch.threema.app.utils.BitmapUtil.FLIP_VERTICAL;

import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.slf4j.Logger;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

import ch.threema.app.preference.service.PreferenceService;
import ch.threema.app.utils.BitmapUtil;
import ch.threema.app.utils.FileUtil;
import ch.threema.app.utils.MimeUtil;
import static ch.threema.base.utils.LoggingKt.getThreemaLogger;
import ch.threema.domain.protocol.csp.messages.file.FileData;

/**
 * This class holds all meta information about a media item to be sent
 */

public class MediaItem implements Parcelable {
    @MediaType
    private int type;
    private Uri originalUri; // Uri of original media item before creating a local copy
    private Uri uri;
    private Orientation orientation;
    private int exifRotation;
    private long durationMs;
    private String caption;
    private long startTimeMs;
    private long endTimeMs;
    @BitmapUtil.FlipType
    private int exifFlip;
    private String mimeType;
    @FileData.RenderingType
    int renderingType;
    @PreferenceService.ImageScale
    private int imageScale; // desired image scale
    @PreferenceService.VideoSize
    private int videoSize; // desired video scale factor
    private String filename;
    private boolean deleteAfterUse;
    private boolean isEdited;
    private boolean muted;

    private static final Logger logger = getThreemaLogger("MediaItem");

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({TYPE_FILE, TYPE_IMAGE, TYPE_VIDEO, TYPE_IMAGE_CAM, TYPE_VIDEO_CAM, TYPE_VOICEMESSAGE, TYPE_TEXT, TYPE_LOCATION, TYPE_IMAGE_ANIMATED})
    public @interface MediaType {
    }

    public static final int TYPE_FILE = 0;
    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_VIDEO = 2;
    public static final int TYPE_IMAGE_CAM = 3;
    public static final int TYPE_VIDEO_CAM = 4;
    public static final int TYPE_VOICEMESSAGE = 5;
    public static final int TYPE_TEXT = 6;
    public static final int TYPE_LOCATION = 7;
    public static final int TYPE_IMAGE_ANIMATED = 8; // animated images such as animated WebP

    public static final long TIME_UNDEFINED = Long.MIN_VALUE;

    public static class Orientation {
        private int rotation;
        private int flip;

        public static Orientation getMixedOrientation(@NonNull Orientation first, @NonNull Orientation second) {
            int mixedRotation = first.getRotation() + second.getRotation();
            int mixedFlip = FLIP_NONE;
            mixedFlip |= first.isHorizontalFlip() ^ second.isHorizontalFlip() ? FLIP_HORIZONTAL : FLIP_NONE;
            mixedFlip |= first.isVerticalFlip() ^ second.isVerticalFlip() ? FLIP_VERTICAL : FLIP_NONE;
            return new Orientation(mixedRotation, mixedFlip);
        }

        public Orientation() {
            this(0, FLIP_NONE);
        }

        public Orientation(int rotation, int flip) {
            this.rotation = rotation;
            this.flip = flip;
        }

        public int getRotation() {
            return rotation;
        }

        public void setRotation(int rotation) {
            this.rotation = rotation;
            clampRotation();
        }

        public void rotateBy(int degrees) {
            this.rotation += degrees;
            clampRotation();
        }

        public int getFlip() {
            return flip;
        }

        public void setFlip(int flip) {
            this.flip = flip;
        }

        public void flip() {
            int currentFlip = flip;
            if (getRotation() == 90 || getRotation() == 270) {
                if ((currentFlip & FLIP_VERTICAL) == FLIP_VERTICAL) {
                    // clear vertical flag
                    currentFlip &= ~FLIP_VERTICAL;
                } else {
                    currentFlip |= FLIP_VERTICAL;
                }
            } else {
                if ((currentFlip & FLIP_HORIZONTAL) == FLIP_HORIZONTAL) {
                    // clear horizontal flag
                    currentFlip &= ~FLIP_HORIZONTAL;
                } else {
                    currentFlip |= FLIP_HORIZONTAL;
                }
            }
            flip = currentFlip;
        }

        public Orientation getInverse() {
            Orientation inverse = new Orientation(-rotation, FLIP_NONE);
            if (rotation == 90 || rotation == 270) {
                inverse.flip |= isVerticalFlip() ? FLIP_HORIZONTAL : FLIP_NONE;
                inverse.flip |= isHorizontalFlip() ? FLIP_VERTICAL : FLIP_NONE;
            } else {
                inverse.flip = flip;
            }
            return inverse;
        }

        /**
         * Get the transformation matrix based on rotation and flip.
         */

        @NonNull
        public Matrix getTransformationMatrix() {
            Matrix matrix = new Matrix();
            boolean flipHorizontal = (getFlip() & FLIP_HORIZONTAL) == FLIP_HORIZONTAL;
            boolean flipVertical = (getFlip() & FLIP_VERTICAL) == FLIP_VERTICAL;
            matrix.postScale(flipHorizontal ? -1 : 1, flipVertical ? -1 : 1);
            matrix.postRotate(getRotation());
            return matrix;
        }

        public boolean isHorizontalFlip() {
            return (flip & FLIP_HORIZONTAL) == FLIP_HORIZONTAL;
        }

        public boolean isVerticalFlip() {
            return (flip & FLIP_VERTICAL) == FLIP_VERTICAL;
        }

        private void clampRotation() {
            rotation %= 360;
            if (rotation < 0) {
                rotation += 360;
            }
        }

    }

    public static ArrayList<MediaItem> getFromUris(List<Uri> uris, Context context) {
        return getFromUris(uris, context, false);
    }

    public static ArrayList<MediaItem> getFromUris(@NonNull List<Uri> uris, @NonNull Context context, boolean asFile) {
        ArrayList<MediaItem> mediaItems = new ArrayList<>(uris.size());

        for (Uri uri : uris) {
            mediaItems.add(getFromUri(uri, context, asFile));
        }

        return mediaItems;
    }

    @NonNull
    public static MediaItem getFromUri(@NonNull Uri uri, @NonNull Context context, boolean asFile) {
        String mimeType = FileUtil.getMimeTypeFromUri(context, uri);
        Uri originalUri = uri;
        try {
            // log the number of permissions due to limit https://commonsware.com/blog/2020/06/13/count-your-saf-uri-permission-grants.html
            logger.info("Number of taken persistable uri permissions {}", context.getContentResolver().getPersistedUriPermissions().size());
            context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } catch (Exception e) {
            logger.info(e.getMessage());
            uri = FileUtil.getFileUri(uri);
        }

        MediaItem mediaItem = new MediaItem(uri, mimeType, null);

        // Set exif orientation
        BitmapUtil.ExifOrientation exifOrientation = BitmapUtil.getExifOrientation(context, uri);
        mediaItem.setExifRotation((int) exifOrientation.getRotation());
        mediaItem.setExifFlip(exifOrientation.getFlip());

        mediaItem.setOriginalUri(originalUri);
        mediaItem.setFilename(FileUtil.getFilenameFromUri(context.getContentResolver(), mediaItem));
        if (asFile) {
            if (MimeUtil.isSupportedImageFile(mimeType)) {
                mediaItem.setImageScale(PreferenceService.IMAGE_SCALE_SEND_AS_FILE);
            } else if (MimeUtil.isVideoFile(mimeType)) {
                mediaItem.setVideoSize(PreferenceService.VIDEO_SIZE_SEND_AS_FILE);
            } else {
                mediaItem.setType(TYPE_FILE);
            }
        }
        return mediaItem;
    }

    public MediaItem(Uri uri, @MediaType int type) {
        init();

        this.type = type;
        this.uri = uri;
    }

    public MediaItem(Uri uri, @MediaType int type, String mimeType, String caption) {
        init();

        this.type = type;
        this.uri = uri;
        this.mimeType = mimeType;
        this.caption = caption;
    }

    public MediaItem(Uri uri, String mimeType, String caption) {
        init();

        this.type = MimeUtil.getMediaTypeFromMimeType(mimeType, uri);
        if (this.type == TYPE_FILE) {
            this.renderingType = FileData.RENDERING_DEFAULT;
        }

        this.uri = uri;
        this.mimeType = mimeType;
        this.caption = caption;
    }

    private void init() {
        this.orientation = new Orientation();
        this.exifRotation = 0;
        this.durationMs = 0;
        this.caption = null;
        this.startTimeMs = 0;
        this.endTimeMs = TIME_UNDEFINED;
        this.exifFlip = BitmapUtil.FLIP_NONE;
        this.mimeType = MimeUtil.MIME_TYPE_DEFAULT;
        this.renderingType = FileData.RENDERING_MEDIA;
        this.imageScale = IMAGE_SCALE_DEFAULT;
        this.videoSize = VIDEO_SIZE_DEFAULT;
        this.filename = null;
        this.deleteAfterUse = false;
        isEdited = false;
    }


    public MediaItem(Parcel in) {
        orientation = new Orientation();
        type = in.readInt();
        uri = in.readParcelable(Uri.class.getClassLoader());
        orientation.rotation = in.readInt();
        exifRotation = in.readInt();
        durationMs = in.readLong();
        caption = in.readString();
        startTimeMs = in.readLong();
        endTimeMs = in.readLong();
        orientation.flip = in.readInt();
        exifFlip = in.readInt();
        mimeType = in.readString();
        renderingType = in.readInt();
        imageScale = in.readInt();
        videoSize = in.readInt();
        filename = in.readString();
        deleteAfterUse = in.readInt() != 0;
        originalUri = in.readParcelable(Uri.class.getClassLoader());
        isEdited = in.readInt() != 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(type);
        dest.writeParcelable(uri, flags);
        dest.writeInt(orientation.rotation);
        dest.writeInt(exifRotation);
        dest.writeLong(durationMs);
        dest.writeString(caption);
        dest.writeLong(startTimeMs);
        dest.writeLong(endTimeMs);
        dest.writeInt(orientation.flip);
        dest.writeInt(exifFlip);
        dest.writeString(mimeType);
        dest.writeInt(renderingType);
        dest.writeInt(imageScale);
        dest.writeInt(videoSize);
        dest.writeString(filename);
        dest.writeInt(deleteAfterUse ? 1 : 0);
        dest.writeParcelable(originalUri, flags);
        dest.writeInt(isEdited ? 1 : 0);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MediaItem> CREATOR = new Creator<>() {
        @Override
        public MediaItem createFromParcel(Parcel in) {
            return new MediaItem(in);
        }

        @Override
        public MediaItem[] newArray(int size) {
            return new MediaItem[size];
        }
    };

    @MediaType
    public int getType() {
        return type;
    }

    public void setType(@MediaType int type) {
        this.type = type;
    }

    public Uri getUri() {
        return uri;
    }

    public void setUri(Uri uri) {
        this.uri = uri;
    }

    public int getRotation() {
        return orientation.getRotation();
    }

    public void setRotation(int rotation) {
        this.orientation.setRotation(rotation);
    }

    public int getExifRotation() {
        return exifRotation;
    }

    public void setExifRotation(int exifRotation) {
        this.exifRotation = exifRotation;
    }

    public long getDurationMs() {
        return durationMs;
    }

    /**
     * Return duration of media object after a possible trimming has been applied
     *
     * @return duration in ms
     */

    public long getTrimmedDurationMs() {
        return
            (startTimeMs != 0L && startTimeMs != TIME_UNDEFINED) ||
                (endTimeMs != TIME_UNDEFINED && endTimeMs != durationMs) ?
                endTimeMs - startTimeMs :
                durationMs;
    }

    /**
     * Return true, if the video needs to be trimmed
     */

    public boolean needsTrimming() {
        return getDurationMs() != getTrimmedDurationMs();
    }

    public void setDurationMs(long durationMs) {
        this.durationMs = durationMs;
    }

    public @Nullable String getCaption() {
        return caption;
    }

    public @Nullable String getTrimmedCaption() {
        if (caption != null) {
            return caption.trim();
        }
        return null;
    }

    public void setCaption(@Nullable String caption) {
        this.caption = caption;
    }

    public long getStartTimeMs() {
        return startTimeMs;
    }

    public void setStartTimeMs(long startTimeMs) {
        this.startTimeMs = startTimeMs;
    }

    public long getEndTimeMs() {
        return endTimeMs;
    }

    public void setEndTimeMs(long endTimeMs) {
        this.endTimeMs = endTimeMs;
    }

    public Orientation getOrientation() {
        return orientation;
    }

    public int getFlip() {
        return orientation.getFlip();
    }

    public void setFlip(int flip) {
        this.orientation.setFlip(flip);
    }

    public void flip() {
        orientation.flip();
    }

    @BitmapUtil.FlipType
    public int getExifFlip() {
        return exifFlip;
    }

    public void setExifFlip(@BitmapUtil.FlipType int exifFlip) {
        this.exifFlip = exifFlip;
    }

    /**
     * @return the MimeType override
     */

    public String getMimeType() {
        return mimeType;
    }

    /**
     * set MimeType override
     */

    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    public @FileData.RenderingType int getRenderingType() {
        return renderingType;
    }

    public void setRenderingType(@FileData.RenderingType int renderingType) {
        this.renderingType = renderingType;
    }

    public @PreferenceService.ImageScale int getImageScale() {
        return imageScale;
    }

    public void setImageScale(@PreferenceService.ImageScale int imageScale) {
        this.imageScale = imageScale;
    }

    public @PreferenceService.VideoSize int getVideoSize() {
        return videoSize;
    }

    public void setVideoSize(@PreferenceService.VideoSize int videoSize) {
        this.videoSize = videoSize;
    }

    public @Nullable String getFilename() {
        return filename;
    }

    public void setFilename(@Nullable String filename) {
        this.filename = filename;
    }

    public boolean getDeleteAfterUse() {
        return deleteAfterUse;
    }

    /**
     * Set this flag if the file is temporary and can be deleted after use
     *
     * @param deleteAfterUse 1 to signal the file is expendable, 0 otherwise
     */

    public void setDeleteAfterUse(boolean deleteAfterUse) {
        this.deleteAfterUse = deleteAfterUse;
    }

    public @Nullable Uri getOriginalUri() {
        if (originalUri == null) {
            return uri;
        }
        return originalUri;
    }

    /**
     * Set this to the original location of the file before creating a copy for persistence across activities
     *
     * @param originalUri Uri of the original media file
     */

    public void setOriginalUri(Uri originalUri) {
        this.originalUri = originalUri;
    }

    /**
     * Return true, if the picture has been edited (painted or cropped)
     */

    public boolean isEdited() {
        return isEdited;
    }

    /**
     * Mark this media item as edited (painted or cropped)
     */

    public void setEdited(boolean edited) {
        this.isEdited = edited;
    }

    /**
     * Return true, if the video should be sent without audio
     */

    public boolean isMuted() {
        return muted;
    }

    /**
     * Set flag to remove the audio before sending
     */

    public void setMuted(boolean muted) {
        this.muted = muted;
    }

    /**
     * Return true if the media item has been modified. For images this includes: crop, painted,
     * rotated, or flipped. For videos this includes: different start time, different end time
     */

    public boolean hasChanges() {
        if (type == TYPE_IMAGE) {
            return isEdited() || orientation.getRotation() != 0 || orientation.getFlip() != BitmapUtil.FLIP_NONE;
        } else if (type == TYPE_VIDEO || type == TYPE_VIDEO_CAM) {
            return needsTrimming() || muted;
        } else {
            return false;
        }
    }

    /**
     * Return true if the media item will be sent as file. This is the case for files, or for images
     * or videos with the quality set to file.
     */

    public boolean sendAsFile() {
        if (type == TYPE_VIDEO || type == TYPE_VIDEO_CAM) {
            return getVideoSize() == PreferenceService.VIDEO_SIZE_SEND_AS_FILE;
        } else if (type == TYPE_IMAGE || type == TYPE_IMAGE_CAM) {
            return getImageScale() == PreferenceService.IMAGE_SCALE_SEND_AS_FILE;
        } else {
            return type == TYPE_FILE || type == TYPE_IMAGE_ANIMATED;
        }
    }

}

Messung V0.5 in Prozent
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.32 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge