added media metadata support, bug fix
This commit is contained in:
parent
5808274a1e
commit
1b9edd2a9e
@ -44,11 +44,13 @@ public class MastodonMedia implements Media {
|
|||||||
private String description = "";
|
private String description = "";
|
||||||
private String blur;
|
private String blur;
|
||||||
private int type = UNDEFINED;
|
private int type = UNDEFINED;
|
||||||
|
private Meta meta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param json Mastodon status JSON format
|
* @param json Mastodon status JSON format
|
||||||
*/
|
*/
|
||||||
public MastodonMedia(JSONObject json) throws JSONException {
|
public MastodonMedia(JSONObject json) throws JSONException {
|
||||||
|
JSONObject metaJson = json.optJSONObject("meta");
|
||||||
String typeStr = json.getString("type");
|
String typeStr = json.getString("type");
|
||||||
String url = json.getString("url");
|
String url = json.getString("url");
|
||||||
String preview = json.optString("preview_url", "");
|
String preview = json.optString("preview_url", "");
|
||||||
@ -83,6 +85,9 @@ public class MastodonMedia implements Media {
|
|||||||
if (json.has("description") && !json.isNull("description")) {
|
if (json.has("description") && !json.isNull("description")) {
|
||||||
description = json.getString("description");
|
description = json.getString("description");
|
||||||
}
|
}
|
||||||
|
if (metaJson != null) {
|
||||||
|
meta = new MastodonMeta(metaJson);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,6 +127,13 @@ public class MastodonMedia implements Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Meta getMeta() {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Media))
|
if (!(obj instanceof Media))
|
||||||
@ -154,4 +166,90 @@ public class MastodonMedia implements Media {
|
|||||||
}
|
}
|
||||||
return tostring + " url=\"" + getUrl() + "\"";
|
return tostring + " url=\"" + getUrl() + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final class MastodonMeta implements Meta {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5103849502754551661L;
|
||||||
|
|
||||||
|
private double duration;
|
||||||
|
private int previewWidth;
|
||||||
|
private int previewHeight;
|
||||||
|
private int originalWidth;
|
||||||
|
private int originalHeight;
|
||||||
|
private int bitrate;
|
||||||
|
private float framerate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public MastodonMeta(JSONObject json) {
|
||||||
|
JSONObject original = json.optJSONObject("original");
|
||||||
|
JSONObject small = json.optJSONObject("small");
|
||||||
|
if (small != null) {
|
||||||
|
previewWidth = small.optInt("width", 1);
|
||||||
|
previewHeight = small.optInt("height", 1);
|
||||||
|
}
|
||||||
|
if (original != null) {
|
||||||
|
String framerateStr = original.optString("frame_rate");
|
||||||
|
originalWidth = original.optInt("width", 1);
|
||||||
|
originalHeight = original.optInt("height", 1);
|
||||||
|
bitrate = original.optInt("bitrate", 0) / 1024;
|
||||||
|
duration = original.optDouble("duration", 0.0);
|
||||||
|
// calculate framerate if any
|
||||||
|
int split = framerateStr.indexOf("/");
|
||||||
|
if (split > 0) {
|
||||||
|
String upper = framerateStr.substring(0, split);
|
||||||
|
String down = framerateStr.substring(split + 1);
|
||||||
|
if (upper.matches("\\d+") && down.matches("\\d+") && !down.equals("0")) {
|
||||||
|
framerate = (float) (Integer.parseInt(upper) / Integer.parseInt(down));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidthPreview() {
|
||||||
|
return previewWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeightPreview() {
|
||||||
|
return previewHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return originalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return originalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBitrate() {
|
||||||
|
return bitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getFrameRate() {
|
||||||
|
return framerate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -139,6 +139,13 @@ public class MediaV1 implements Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Meta getMeta() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Media))
|
if (!(obj instanceof Media))
|
||||||
|
@ -145,6 +145,13 @@ public class MediaV2 implements Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Meta getMeta() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Media))
|
if (!(obj instanceof Media))
|
||||||
|
@ -9,7 +9,6 @@ import androidx.annotation.WorkerThread;
|
|||||||
|
|
||||||
import org.nuclearfog.twidda.BuildConfig;
|
import org.nuclearfog.twidda.BuildConfig;
|
||||||
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@ -64,7 +63,7 @@ public abstract class AsyncExecutor<Parameter, Result> {
|
|||||||
try {
|
try {
|
||||||
Result result = doInBackground(parameter);
|
Result result = doInBackground(parameter);
|
||||||
onPostExecute(result, callbackReference);
|
onPostExecute(result, callbackReference);
|
||||||
} catch (InterruptedException | InterruptedIOException e) {
|
} catch (InterruptedException e) {
|
||||||
// caused by user interaction. No need to handle exception.
|
// caused by user interaction. No need to handle exception.
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
@ -121,7 +120,7 @@ public abstract class AsyncExecutor<Parameter, Result> {
|
|||||||
* @return result of the background task
|
* @return result of the background task
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
protected abstract Result doInBackground(@NonNull Parameter param) throws InterruptedException, InterruptedIOException;
|
protected abstract Result doInBackground(@NonNull Parameter param) throws InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback used to send task result to main thread
|
* Callback used to send task result to main thread
|
||||||
|
@ -11,6 +11,15 @@ import android.util.SparseArray;
|
|||||||
*/
|
*/
|
||||||
public class BlurHashDecoder {
|
public class BlurHashDecoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use in memory cache for the calculated math, reused by images with same size.
|
||||||
|
* if the cache does not exist yet it will be created and populated with new calculations.
|
||||||
|
* By default it is true.
|
||||||
|
*/
|
||||||
|
private static final boolean USE_CACHE = true;
|
||||||
|
|
||||||
|
private static final int DEFAULT_SIZE = 16;
|
||||||
|
|
||||||
// cache Math.cos() calculations to improve performance.
|
// cache Math.cos() calculations to improve performance.
|
||||||
// The number of calculations can be huge for many bitmaps: width * height * numCompX * numCompY * 2 * nBitmaps
|
// The number of calculations can be huge for many bitmaps: width * height * numCompX * numCompY * 2 * nBitmaps
|
||||||
// the cache is enabled by default, it is recommended to disable it only when just a few images are displayed
|
// the cache is enabled by default, it is recommended to disable it only when just a few images are displayed
|
||||||
@ -36,20 +45,40 @@ public class BlurHashDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* create blurred bitmap using hash string
|
||||||
*
|
*
|
||||||
|
* @param blurHash hash string
|
||||||
|
* @return blurred bitmap
|
||||||
*/
|
*/
|
||||||
public static Bitmap decode(String blurHash) {
|
public static Bitmap decode(String blurHash) {
|
||||||
return decode(blurHash, 16, 16, 1f, true);
|
return decode(blurHash, DEFAULT_SIZE, DEFAULT_SIZE, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a blur hash into a new bitmap.
|
* create scaled bitmap using hash string
|
||||||
*
|
*
|
||||||
* @param useCache use in memory cache for the calculated math, reused by images with same size.
|
* @param blurHash hash string
|
||||||
* if the cache does not exist yet it will be created and populated with new calculations.
|
* @param ratio ratio ob the bitmap to generate
|
||||||
* By default it is true.
|
* @return blurred bitmap
|
||||||
*/
|
*/
|
||||||
public static Bitmap decode(String blurHash, int width, int height, float punch, boolean useCache) {
|
public static Bitmap decode(String blurHash, float ratio) {
|
||||||
|
if (ratio > 1.0f) {
|
||||||
|
return decode(blurHash, DEFAULT_SIZE, (int) (DEFAULT_SIZE / ratio), 1f);
|
||||||
|
} else if (ratio < 1.0f && ratio > 0.0f) {
|
||||||
|
return decode(blurHash, (int) (DEFAULT_SIZE * ratio), DEFAULT_SIZE, 1f);
|
||||||
|
}
|
||||||
|
return decode(blurHash, DEFAULT_SIZE, DEFAULT_SIZE, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create blurred bitmap with custom size
|
||||||
|
*
|
||||||
|
* @param blurHash hash string
|
||||||
|
* @param width bitmap width
|
||||||
|
* @param height bitmap height
|
||||||
|
* @return blurred bitmap
|
||||||
|
*/
|
||||||
|
public static Bitmap decode(String blurHash, int width, int height, float punch) {
|
||||||
if (blurHash == null || blurHash.length() < 6)
|
if (blurHash == null || blurHash.length() < 6)
|
||||||
return null;
|
return null;
|
||||||
int numCompEnc = decode83(blurHash, 0, 1);
|
int numCompEnc = decode83(blurHash, 0, 1);
|
||||||
@ -66,7 +95,7 @@ public class BlurHashDecoder {
|
|||||||
int colorEnc = decode83(blurHash, from, from + 2);
|
int colorEnc = decode83(blurHash, from, from + 2);
|
||||||
colors[i] = decodeAc(colorEnc, maxAc * punch);
|
colors[i] = decodeAc(colorEnc, maxAc * punch);
|
||||||
}
|
}
|
||||||
return composeBitmap(width, height, numCompX, numCompY, colors, useCache);
|
return composeBitmap(width, height, numCompX, numCompY, colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,12 +149,12 @@ public class BlurHashDecoder {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static Bitmap composeBitmap(int width, int height, int numCompX, int numCompY, float[][] colors, boolean useCache) {
|
private static Bitmap composeBitmap(int width, int height, int numCompX, int numCompY, float[][] colors) {
|
||||||
// use an array for better performance when writing pixel colors
|
// use an array for better performance when writing pixel colors
|
||||||
int[] imageArray = new int[width * height];
|
int[] imageArray = new int[width * height];
|
||||||
boolean calculateCosX = !useCache || cacheCosinesX.get(width * numCompX) == null;
|
boolean calculateCosX = !USE_CACHE || cacheCosinesX.get(width * numCompX) == null;
|
||||||
double[] cosinesX = getArrayForCosinesX(calculateCosX, width, numCompX);
|
double[] cosinesX = getArrayForCosinesX(calculateCosX, width, numCompX);
|
||||||
boolean calculateCosY = !useCache || cacheCosinesY.get(height * numCompY) == null;
|
boolean calculateCosY = !USE_CACHE || cacheCosinesY.get(height * numCompY) == null;
|
||||||
double[] cosinesY = getArrayForCosinesY(calculateCosY, height, numCompY);
|
double[] cosinesY = getArrayForCosinesY(calculateCosY, height, numCompY);
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
|
@ -89,6 +89,13 @@ public class DatabaseMedia implements Media, MediaTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Meta getMeta() {
|
||||||
|
return null; // todo implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Media))
|
if (!(obj instanceof Media))
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.nuclearfog.twidda.model;
|
package org.nuclearfog.twidda.model;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,9 +66,62 @@ public interface Media extends Serializable, Comparable<Media> {
|
|||||||
*/
|
*/
|
||||||
String getBlurHash();
|
String getBlurHash();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return media information
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Meta getMeta();
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default int compareTo(Media o) {
|
default int compareTo(Media o) {
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(getKey(), o.getKey());
|
return String.CASE_INSENSITIVE_ORDER.compare(getKey(), o.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media information
|
||||||
|
*/
|
||||||
|
interface Meta extends Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get duration if video
|
||||||
|
*
|
||||||
|
* @return video duration in seconds
|
||||||
|
*/
|
||||||
|
double getDuration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return image width of the thumbnail
|
||||||
|
*/
|
||||||
|
int getWidthPreview();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return image height of the thumbnail
|
||||||
|
*/
|
||||||
|
int getHeightPreview();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return image/video with
|
||||||
|
*/
|
||||||
|
int getWidth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return image/video height
|
||||||
|
*/
|
||||||
|
int getHeight();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get audio/video if any
|
||||||
|
*
|
||||||
|
* @return bitrate in kbit/s
|
||||||
|
*/
|
||||||
|
int getBitrate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get video framerate if any
|
||||||
|
*
|
||||||
|
* @return frame rate
|
||||||
|
*/
|
||||||
|
float getFrameRate();
|
||||||
|
}
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ import org.nuclearfog.twidda.config.GlobalSettings;
|
|||||||
import org.nuclearfog.twidda.model.Media;
|
import org.nuclearfog.twidda.model.Media;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
||||||
|
import org.nuclearfog.twidda.ui.dialogs.MetaDialog;
|
||||||
import org.nuclearfog.twidda.ui.views.AnimatedImageView;
|
import org.nuclearfog.twidda.ui.views.AnimatedImageView;
|
||||||
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
||||||
import org.nuclearfog.zoomview.ZoomView;
|
import org.nuclearfog.zoomview.ZoomView;
|
||||||
@ -65,6 +66,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
private DescriptionView descriptionView;
|
private DescriptionView descriptionView;
|
||||||
|
|
||||||
private DescriptionDialog descriptionDialog;
|
private DescriptionDialog descriptionDialog;
|
||||||
|
private MetaDialog metaDialog;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Uri cacheUri;
|
private Uri cacheUri;
|
||||||
@ -74,6 +76,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
private ImageDownloader imageAsync;
|
private ImageDownloader imageAsync;
|
||||||
private GlobalSettings settings;
|
private GlobalSettings settings;
|
||||||
private File cacheFolder;
|
private File cacheFolder;
|
||||||
|
private Media.Meta meta;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -100,6 +103,8 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
|
|
||||||
imageAsync = new ImageDownloader(this);
|
imageAsync = new ImageDownloader(this);
|
||||||
descriptionDialog = new DescriptionDialog(this, this);
|
descriptionDialog = new DescriptionDialog(this, this);
|
||||||
|
metaDialog = new MetaDialog(this);
|
||||||
|
|
||||||
cacheFolder = new File(getExternalCacheDir(), ImageViewer.CACHE_FOLDER);
|
cacheFolder = new File(getExternalCacheDir(), ImageViewer.CACHE_FOLDER);
|
||||||
cacheFolder.mkdirs();
|
cacheFolder.mkdirs();
|
||||||
|
|
||||||
@ -109,6 +114,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
String description = null;
|
String description = null;
|
||||||
boolean animated = false;
|
boolean animated = false;
|
||||||
boolean local = false;
|
boolean local = false;
|
||||||
|
float ratio = 1.0f;
|
||||||
Serializable serializedData;
|
Serializable serializedData;
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
serializedData = savedInstanceState.getSerializable(KEY_IMAGE_DATA);
|
serializedData = savedInstanceState.getSerializable(KEY_IMAGE_DATA);
|
||||||
@ -123,10 +129,14 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
description = mediaStatus.getDescription();
|
description = mediaStatus.getDescription();
|
||||||
} else if (serializedData instanceof Media) {
|
} else if (serializedData instanceof Media) {
|
||||||
Media media = (Media) serializedData;
|
Media media = (Media) serializedData;
|
||||||
|
meta = media .getMeta();
|
||||||
blurHash = media.getBlurHash();
|
blurHash = media.getBlurHash();
|
||||||
imageUrl = media.getUrl();
|
imageUrl = media.getUrl();
|
||||||
description = media.getDescription();
|
description = media.getDescription();
|
||||||
animated = media.getMediaType() == Media.GIF;
|
animated = media.getMediaType() == Media.GIF;
|
||||||
|
if (meta != null) {
|
||||||
|
ratio = meta.getWidth() / (float) meta.getHeight();
|
||||||
|
}
|
||||||
} else if (serializedData instanceof String) {
|
} else if (serializedData instanceof String) {
|
||||||
imageUrl = (String) serializedData;
|
imageUrl = (String) serializedData;
|
||||||
} else {
|
} else {
|
||||||
@ -161,7 +171,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
}
|
}
|
||||||
// set image blur placeholder
|
// set image blur placeholder
|
||||||
if (blurHash != null && !blurHash.trim().isEmpty()) {
|
if (blurHash != null && !blurHash.trim().isEmpty()) {
|
||||||
Bitmap blur = BlurHashDecoder.decode(blurHash);
|
Bitmap blur = BlurHashDecoder.decode(blurHash, ratio);
|
||||||
zoomImage.setImageBitmap(blur);
|
zoomImage.setImageBitmap(blur);
|
||||||
zoomImage.setMovable(false);
|
zoomImage.setMovable(false);
|
||||||
}
|
}
|
||||||
@ -201,7 +211,9 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
MenuItem itemSave = menu.findItem(R.id.menu_image_save);
|
MenuItem itemSave = menu.findItem(R.id.menu_image_save);
|
||||||
|
MenuItem itemMeta = menu.findItem(R.id.menu_image_show_meta);
|
||||||
itemSave.setVisible(cacheUri != null);
|
itemSave.setVisible(cacheUri != null);
|
||||||
|
itemMeta.setVisible(meta != null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +228,11 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
|||||||
} else if (item.getItemId() == R.id.menu_image_add_description) {
|
} else if (item.getItemId() == R.id.menu_image_add_description) {
|
||||||
descriptionDialog.show();
|
descriptionDialog.show();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.menu_image_show_meta) {
|
||||||
|
if (meta != null) {
|
||||||
|
metaDialog.show(meta);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
|||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.config.Configuration;
|
import org.nuclearfog.twidda.config.Configuration;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConnectionDialog;
|
import org.nuclearfog.twidda.ui.dialogs.ConnectionDialog;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -51,7 +51,7 @@ import org.nuclearfog.twidda.config.Configuration;
|
|||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.Location;
|
import org.nuclearfog.twidda.model.Location;
|
||||||
import org.nuclearfog.twidda.notification.PushSubscription;
|
import org.nuclearfog.twidda.notification.PushSubscription;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.InfoDialog;
|
import org.nuclearfog.twidda.ui.dialogs.InfoDialog;
|
||||||
|
@ -46,6 +46,7 @@ import org.nuclearfog.twidda.backend.utils.LinkUtils;
|
|||||||
import org.nuclearfog.twidda.model.Media;
|
import org.nuclearfog.twidda.model.Media;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
||||||
|
import org.nuclearfog.twidda.ui.dialogs.MetaDialog;
|
||||||
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -83,6 +84,7 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
|||||||
private PlayerView playerView;
|
private PlayerView playerView;
|
||||||
|
|
||||||
private DescriptionDialog descriptionDialog;
|
private DescriptionDialog descriptionDialog;
|
||||||
|
private MetaDialog metaDialog;
|
||||||
|
|
||||||
private ExoPlayer player;
|
private ExoPlayer player;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -105,6 +107,7 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
|||||||
toolbar = findViewById(R.id.page_video_toolbar);
|
toolbar = findViewById(R.id.page_video_toolbar);
|
||||||
descriptionView = findViewById(R.id.page_video_description);
|
descriptionView = findViewById(R.id.page_video_description);
|
||||||
descriptionDialog = new DescriptionDialog(this, this);
|
descriptionDialog = new DescriptionDialog(this, this);
|
||||||
|
metaDialog = new MetaDialog(this);
|
||||||
player = new ExoPlayer.Builder(this, this).build();
|
player = new ExoPlayer.Builder(this, this).build();
|
||||||
|
|
||||||
toolbar.setTitle("");
|
toolbar.setTitle("");
|
||||||
@ -233,9 +236,11 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
|||||||
getMenuInflater().inflate(R.menu.video, menu);
|
getMenuInflater().inflate(R.menu.video, menu);
|
||||||
MenuItem menuOpenUrl = menu.findItem(R.id.menu_video_link);
|
MenuItem menuOpenUrl = menu.findItem(R.id.menu_video_link);
|
||||||
MenuItem menuDescription = menu.findItem(R.id.menu_video_add_description);
|
MenuItem menuDescription = menu.findItem(R.id.menu_video_add_description);
|
||||||
|
MenuItem menuMeta = menu.findItem(R.id.menu_video_show_meta);
|
||||||
AppStyles.setMenuIconColor(menu, Color.WHITE);
|
AppStyles.setMenuIconColor(menu, Color.WHITE);
|
||||||
menuOpenUrl.setVisible(media != null);
|
menuOpenUrl.setVisible(media != null);
|
||||||
menuDescription.setVisible(mediaStatus != null);
|
menuDescription.setVisible(mediaStatus != null);
|
||||||
|
menuMeta.setVisible(media != null && media.getMeta() != null);
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,9 +251,11 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
|||||||
if (media != null) {
|
if (media != null) {
|
||||||
LinkUtils.openMediaLink(this, Uri.parse(media.getUrl()));
|
LinkUtils.openMediaLink(this, Uri.parse(media.getUrl()));
|
||||||
}
|
}
|
||||||
}
|
} else if (item.getItemId() == R.id.menu_video_show_meta) {
|
||||||
//
|
if (media != null && media.getMeta() != null) {
|
||||||
else if (item.getItemId() == R.id.menu_video_add_description) {
|
metaDialog.show(media.getMeta());
|
||||||
|
}
|
||||||
|
} else if (item.getItemId() == R.id.menu_video_add_description) {
|
||||||
descriptionDialog.show();
|
descriptionDialog.show();
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.nuclearfog.twidda.ui.adapter.recyclerview;
|
package org.nuclearfog.twidda.ui.adapter.listview;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
@ -60,7 +60,7 @@ public class DropdownAdapter extends BaseAdapter {
|
|||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
TextView textItem;
|
TextView textItem;
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
convertView = inflater.inflate(R.layout.item_dropdown, parent, false);
|
convertView = inflater.inflate(R.layout.item_dropdown, parent, false);
|
||||||
textItem = convertView.findViewById(R.id.dropdown_textitem);
|
textItem = convertView.findViewById(R.id.dropdown_textitem);
|
||||||
textItem.setTextColor(settings.getTextColor());
|
textItem.setTextColor(settings.getTextColor());
|
@ -0,0 +1,119 @@
|
|||||||
|
package org.nuclearfog.twidda.ui.adapter.listview;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.nuclearfog.twidda.R;
|
||||||
|
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
||||||
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
|
import org.nuclearfog.twidda.model.Media;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListView adapter used to show Meta items
|
||||||
|
*
|
||||||
|
* @see org.nuclearfog.twidda.ui.dialogs.MetaDialog
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
|
public class MetaAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private List<String> keys = new ArrayList<>();
|
||||||
|
private List<String> values = new ArrayList<>();
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private GlobalSettings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public MetaAdapter(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
settings = GlobalSettings.get(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return Math.min(keys.size(), values.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return new String[] {keys.get(position), values.get(position)};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return keys.get(position).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
TextView key_text;
|
||||||
|
TextView value_text;
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = LayoutInflater.from(context).inflate(R.layout.item_meta_field, parent, false);
|
||||||
|
key_text = convertView.findViewById(R.id.item_meta_field_key);
|
||||||
|
value_text = convertView.findViewById(R.id.item_meta_field_value);
|
||||||
|
key_text.setTextColor(settings.getTextColor());
|
||||||
|
value_text.setTextColor(settings.getTextColor());
|
||||||
|
} else {
|
||||||
|
key_text = convertView.findViewById(R.id.item_meta_field_key);
|
||||||
|
value_text = convertView.findViewById(R.id.item_meta_field_value);
|
||||||
|
}
|
||||||
|
key_text.setText(keys.get(position));
|
||||||
|
value_text.setText(values.get(position));
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set adapter items
|
||||||
|
*
|
||||||
|
* @param meta media meta information
|
||||||
|
*/
|
||||||
|
public void setItems(Media.Meta meta) {
|
||||||
|
if (meta.getWidth() > 1 && meta.getHeight() > 1) {
|
||||||
|
keys.add(context.getString(R.string.dialog_meta_width));
|
||||||
|
values.add(StringUtils.NUMBER_FORMAT.format(meta.getWidth()));
|
||||||
|
keys.add(context.getString(R.string.dialog_meta_height));
|
||||||
|
values.add(StringUtils.NUMBER_FORMAT.format(meta.getHeight()));
|
||||||
|
}
|
||||||
|
if (meta.getBitrate() > 0) {
|
||||||
|
keys.add(context.getString(R.string.dialog_meta_bitrate));
|
||||||
|
values.add(StringUtils.NUMBER_FORMAT.format(meta.getBitrate()) + " kbit/s");
|
||||||
|
}
|
||||||
|
if (meta.getFrameRate() > 0) {
|
||||||
|
keys.add(context.getString(R.string.dialog_meta_framerate));
|
||||||
|
values.add(StringUtils.NUMBER_FORMAT.format(meta.getFrameRate()) + " fps");
|
||||||
|
}
|
||||||
|
if (meta.getDuration() > 0) {
|
||||||
|
StringBuilder durationValue = new StringBuilder();
|
||||||
|
keys.add(context.getString(R.string.dialog_meta_duration));
|
||||||
|
long hours = Math.round(meta.getDuration() / 3600);
|
||||||
|
long mins = Math.round(meta.getDuration() / 60) % 60L;
|
||||||
|
long sec = Math.round(meta.getDuration() % 60.0);
|
||||||
|
if (hours > 0) {
|
||||||
|
if (hours < 10)
|
||||||
|
durationValue.append('0');
|
||||||
|
durationValue.append(hours).append(':');
|
||||||
|
}
|
||||||
|
if (mins < 10)
|
||||||
|
durationValue.append('0');
|
||||||
|
durationValue.append(mins).append(':');
|
||||||
|
if (sec < 10)
|
||||||
|
durationValue.append('0');
|
||||||
|
durationValue.append(sec).append('\n');
|
||||||
|
values.add(durationValue.toString());
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ import org.nuclearfog.twidda.backend.helper.update.FilterUpdate;
|
|||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.model.Filter;
|
import org.nuclearfog.twidda.model.Filter;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.nuclearfog.twidda.ui.dialogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.nuclearfog.twidda.R;
|
||||||
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
|
import org.nuclearfog.twidda.model.Media.Meta;
|
||||||
|
import org.nuclearfog.twidda.ui.adapter.listview.MetaAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog to show media information
|
||||||
|
*
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
|
public class MetaDialog extends Dialog {
|
||||||
|
|
||||||
|
private MetaAdapter adapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public MetaDialog(Activity activity) {
|
||||||
|
super(activity, R.style.MetaDialog);
|
||||||
|
adapter = new MetaAdapter(activity.getApplicationContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.dialog_meta);
|
||||||
|
ViewGroup root = findViewById(R.id.dialog_meta_root);
|
||||||
|
ListView list = findViewById(R.id.dialog_meta_list);
|
||||||
|
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
AppStyles.setTheme(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show() {
|
||||||
|
// using show(Meta) instead
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dismiss() {
|
||||||
|
if (isShowing()) {
|
||||||
|
super.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void show(@NonNull Meta meta) {
|
||||||
|
if (!isShowing()) {
|
||||||
|
super.show();
|
||||||
|
adapter.setItems(meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ import org.nuclearfog.twidda.R;
|
|||||||
import org.nuclearfog.twidda.backend.helper.update.PollUpdate;
|
import org.nuclearfog.twidda.backend.helper.update.PollUpdate;
|
||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.model.Instance;
|
import org.nuclearfog.twidda.model.Instance;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.EditOptionsAdapter;
|
import org.nuclearfog.twidda.ui.adapter.recyclerview.EditOptionsAdapter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -22,7 +22,7 @@ import org.nuclearfog.twidda.backend.async.ReportUpdater.ReportResult;
|
|||||||
import org.nuclearfog.twidda.backend.helper.update.ReportUpdate;
|
import org.nuclearfog.twidda.backend.helper.update.ReportUpdate;
|
||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import org.nuclearfog.twidda.backend.helper.update.StatusUpdate;
|
|||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.Status;
|
import org.nuclearfog.twidda.model.Status;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -27,7 +27,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
|||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.WebPush;
|
import org.nuclearfog.twidda.model.WebPush;
|
||||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
25
app/src/main/res/layout/dialog_meta.xml
Normal file
25
app/src/main/res/layout/dialog_meta.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/dialog_meta_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".ui.dialogs.MetaDialog">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_meta_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:lines="1"
|
||||||
|
android:text="@string/dialog_meta_title"
|
||||||
|
android:textSize="@dimen/dialog_meta_textsize_title"
|
||||||
|
android:layout_margin="@dimen/dialog_meta_margin_layout" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/dialog_meta_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/dialog_meta_margin_layout" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
23
app/src/main/res/layout/item_meta_field.xml
Normal file
23
app/src/main/res/layout/item_meta_field.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_meta_field_key"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:layout_marginEnd="@dimen/dialog_media_field_layout_margin"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_meta_field_value"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="top|end"
|
||||||
|
android:maxLines="2" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -3,9 +3,13 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_image_save"
|
android:id="@+id/menu_image_save"
|
||||||
android:icon="@drawable/save"
|
|
||||||
android:title="@string/item_image_save" />
|
android:title="@string/item_image_save" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_image_show_meta"
|
||||||
|
android:title="@string/menu_media_show_meta"
|
||||||
|
android:visible="false" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_image_add_description"
|
android:id="@+id/menu_image_add_description"
|
||||||
android:title="@string/menu_media_add_description"
|
android:title="@string/menu_media_add_description"
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
android:title="@string/button_share"
|
android:title="@string/button_share"
|
||||||
android:visible="true" />
|
android:visible="true" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_video_show_meta"
|
||||||
|
android:title="@string/menu_media_show_meta"
|
||||||
|
android:visible="false" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_video_add_description"
|
android:id="@+id/menu_video_add_description"
|
||||||
android:title="@string/menu_media_add_description"
|
android:title="@string/menu_media_add_description"
|
||||||
|
@ -268,6 +268,7 @@
|
|||||||
<string name="popup_status_text_hint">Status</string>
|
<string name="popup_status_text_hint">Status</string>
|
||||||
<string name="info_account_selected">%1$s ausgewählt</string>
|
<string name="info_account_selected">%1$s ausgewählt</string>
|
||||||
<string name="menu_media_add_description">Beschreibung hinzufügen</string>
|
<string name="menu_media_add_description">Beschreibung hinzufügen</string>
|
||||||
|
<string name="menu_media_show_meta">zeige Medieninformationen</string>
|
||||||
<string name="menu_message">Nachricht schreiben</string>
|
<string name="menu_message">Nachricht schreiben</string>
|
||||||
<string name="status_replyname_empty">Antwort</string>
|
<string name="status_replyname_empty">Antwort</string>
|
||||||
<string name="status_metrics_title">Statusmetriken</string>
|
<string name="status_metrics_title">Statusmetriken</string>
|
||||||
@ -334,6 +335,12 @@
|
|||||||
<string name="dialog_report_category_label">Meldegrund</string>
|
<string name="dialog_report_category_label">Meldegrund</string>
|
||||||
<string name="dialog_report_title_status">Status melden</string>
|
<string name="dialog_report_title_status">Status melden</string>
|
||||||
<string name="dialog_report_apply">melden</string>
|
<string name="dialog_report_apply">melden</string>
|
||||||
|
<string name="dialog_meta_title">Medieninformationen</string>
|
||||||
|
<string name="dialog_meta_width">Breite</string>
|
||||||
|
<string name="dialog_meta_height">Höhe</string>
|
||||||
|
<string name="dialog_meta_bitrate">Bitrate</string>
|
||||||
|
<string name="dialog_meta_framerate">Framerate</string>
|
||||||
|
<string name="dialog_meta_duration">Dauer</string>
|
||||||
<string name="settings_enable_push_label">aktiviere Push-Benachrichtigung\n(ntfy-App erforderlich)</string>
|
<string name="settings_enable_push_label">aktiviere Push-Benachrichtigung\n(ntfy-App erforderlich)</string>
|
||||||
<string name="notification_favorite">%1$s hat einen Status favorisiert</string>
|
<string name="notification_favorite">%1$s hat einen Status favorisiert</string>
|
||||||
<string name="notification_repost">%1$s hat einen Status geteilt</string>
|
<string name="notification_repost">%1$s hat einen Status geteilt</string>
|
||||||
|
@ -373,4 +373,11 @@
|
|||||||
<dimen name="item_field_textsize_time">10sp</dimen>
|
<dimen name="item_field_textsize_time">10sp</dimen>
|
||||||
<dimen name="item_field_icon_size">18sp</dimen>
|
<dimen name="item_field_icon_size">18sp</dimen>
|
||||||
|
|
||||||
|
<!--dimens of dialog_meta.xml-->
|
||||||
|
<dimen name="dialog_meta_textsize_title">22sp</dimen>
|
||||||
|
<dimen name="dialog_meta_margin_layout">5dp</dimen>
|
||||||
|
|
||||||
|
<!--dimens of item_media_field.xml-->
|
||||||
|
<dimen name="dialog_media_field_layout_margin">5dp</dimen>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -192,6 +192,7 @@
|
|||||||
<string name="menu_toolbar_request">Follow requests</string>
|
<string name="menu_toolbar_request">Follow requests</string>
|
||||||
<string name="menu_toolbar_excluded_users">Blocklists</string>
|
<string name="menu_toolbar_excluded_users">Blocklists</string>
|
||||||
<string name="menu_media_add_description">add description</string>
|
<string name="menu_media_add_description">add description</string>
|
||||||
|
<string name="menu_media_show_meta">show metadata</string>
|
||||||
<string name="menu_message">write message</string>
|
<string name="menu_message">write message</string>
|
||||||
<string name="dialog_link_image_preview">Link preview image</string>
|
<string name="dialog_link_image_preview">Link preview image</string>
|
||||||
<string name="app_info_icons">svg icons from:</string>
|
<string name="app_info_icons">svg icons from:</string>
|
||||||
@ -358,6 +359,12 @@
|
|||||||
<string name="dialog_report_category_label">what to report</string>
|
<string name="dialog_report_category_label">what to report</string>
|
||||||
<string name="dialog_report_title_status">report status</string>
|
<string name="dialog_report_title_status">report status</string>
|
||||||
<string name="dialog_report_apply">report</string>
|
<string name="dialog_report_apply">report</string>
|
||||||
|
<string name="dialog_meta_title">Media information</string>
|
||||||
|
<string name="dialog_meta_width">width</string>
|
||||||
|
<string name="dialog_meta_height">height</string>
|
||||||
|
<string name="dialog_meta_bitrate">bitrate</string>
|
||||||
|
<string name="dialog_meta_framerate">framerate</string>
|
||||||
|
<string name="dialog_meta_duration">duration</string>
|
||||||
<string name="settings_enable_push_label">enable push notification\n(requires ntfy app)</string>
|
<string name="settings_enable_push_label">enable push notification\n(requires ntfy app)</string>
|
||||||
<string name="notification_favorite">%1$s favorited your status</string>
|
<string name="notification_favorite">%1$s favorited your status</string>
|
||||||
<string name="notification_repost">%1$s reposted your status</string>
|
<string name="notification_repost">%1$s reposted your status</string>
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
<item name="android:windowCloseOnTouchOutside">false</item>
|
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="MetaDialog" parent="DefaultDialog">
|
||||||
|
<item name="android:windowMinWidthMinor">60%</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="AppInfoDialog" parent="DefaultDialog">
|
<style name="AppInfoDialog" parent="DefaultDialog">
|
||||||
<item name="android:background">@android:color/white</item>
|
<item name="android:background">@android:color/white</item>
|
||||||
<item name="android:textColor">@android:color/black</item>
|
<item name="android:textColor">@android:color/black</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user