improved fanfou quote status

This commit is contained in:
Mariotaku Lee 2017-01-24 19:10:24 +08:00
parent ce4d921d7a
commit 03778784bc
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
37 changed files with 279 additions and 116 deletions

View File

@ -46,6 +46,9 @@ public interface AccountExtras extends Parcelable {
@JsonObject
class VideoLimit {
boolean supported;
@JsonField(name = "min_width")
int minWidth;
@JsonField(name = "min_height")
@ -74,6 +77,23 @@ public interface AccountExtras extends Parcelable {
@JsonField(name = "max_frame_rate")
double maxFrameRate;
@JsonField(name = "min_duration_sync")
long minDurationSync;
@JsonField(name = "min_duration_async")
long minDurationAsync;
@JsonField(name = "max_duration_sync")
long maxDurationSync;
@JsonField(name = "max_duration_async")
long maxDurationAsync;
public boolean isSupported() {
return supported;
}
public void setSupported(boolean supported) {
this.supported = supported;
}
public int getMinWidth() {
return minWidth;
}
@ -162,6 +182,38 @@ public interface AccountExtras extends Parcelable {
this.maxFrameRate = maxFrameRate;
}
public long getMinDurationSync() {
return minDurationSync;
}
public void setMinDurationSync(long minDurationSync) {
this.minDurationSync = minDurationSync;
}
public long getMinDurationAsync() {
return minDurationAsync;
}
public void setMinDurationAsync(long minDurationAsync) {
this.minDurationAsync = minDurationAsync;
}
public long getMaxDurationSync() {
return maxDurationSync;
}
public void setMaxDurationSync(long maxDurationSync) {
this.maxDurationSync = maxDurationSync;
}
public long getMaxDurationAsync() {
return maxDurationAsync;
}
public void setMaxDurationAsync(long maxDurationAsync) {
this.maxDurationAsync = maxDurationAsync;
}
@SuppressWarnings("RedundantIfStatement")
public boolean checkGeometry(int width, int height) {
// Check w & h
@ -189,6 +241,24 @@ public interface AccountExtras extends Parcelable {
return inRange(frameRate, getMinFrameRate(), getMaxFrameRate());
}
public boolean checkMinDuration(long duration, boolean async) {
final long limit = async ? getMinDurationAsync() : getMinDurationSync();
if (limit <= 0) return true;
return duration >= limit;
}
public boolean checkMaxDuration(long duration, boolean async) {
final long limit = async ? getMaxDurationAsync() : getMaxDurationSync();
if (limit <= 0) return true;
return duration <= limit;
}
public boolean checkSize(long size, boolean async) {
final long limit = async ? getMaxSizeAsync() : getMaxSizeSync();
if (limit <= 0) return true;
return size < limit;
}
@SuppressWarnings("RedundantIfStatement")
private boolean inRange(int num, int min, int max) {
if (min > 0 && num < min) return false;
@ -205,6 +275,7 @@ public interface AccountExtras extends Parcelable {
public static VideoLimit twitterDefault() {
VideoLimit videoLimit = new VideoLimit();
videoLimit.setSupported(true);
videoLimit.setMinWidth(32);
videoLimit.setMinHeight(32);
videoLimit.setMaxWidth(1280);
@ -216,6 +287,16 @@ public interface AccountExtras extends Parcelable {
videoLimit.setMaxSizeAsync(512 * 1024 * 1024);
videoLimit.setMinFrameRate(0);
videoLimit.setMaxFrameRate(40);
videoLimit.setMinDurationSync(500); // 0.5s
videoLimit.setMinDurationAsync(500); // 0.5s
videoLimit.setMaxDurationSync(30000); // 30s
videoLimit.setMaxDurationAsync(140000); // 140s
return videoLimit;
}
public static VideoLimit unsupported() {
VideoLimit videoLimit = new VideoLimit();
videoLimit.setSupported(false);
return videoLimit;
}
}

View File

@ -0,0 +1,14 @@
package org.mariotaku.ktextension
import android.media.MediaMetadataRetriever
/**
* Created by mariotaku on 2017/1/24.
*/
fun MediaMetadataRetriever.releaseSafe() {
try {
release()
} catch (e: Exception) {
// Ignore any exceptions
}
}

View File

@ -2,12 +2,12 @@ package org.mariotaku.twidere.extension.model
import android.content.Context
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.account.AccountExtras
import org.mariotaku.twidere.model.account.TwitterAccountExtras
import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.model.account.cred.OAuthCredentials
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.TwitterContentUtils
@ -42,4 +42,14 @@ fun <T> AccountDetails.newMicroBlogInstance(context: Context, includeEntities: B
}
val AccountDetails.is_oauth: Boolean
get() = credentials_type == Credentials.Type.OAUTH || credentials_type == Credentials.Type.XAUTH
get() = credentials_type == Credentials.Type.OAUTH || credentials_type == Credentials.Type.XAUTH
val AccountDetails.size_limit: UpdateStatusTask.SizeLimit
get() = when (type) {
AccountType.TWITTER -> {
val imageLimit = AccountExtras.ImageLimit.ofSize(2048, 1536)
val videoLimit = AccountExtras.VideoLimit.twitterDefault()
UpdateStatusTask.SizeLimit(imageLimit, videoLimit)
}
else -> UpdateStatusTask.SizeLimit(AccountExtras.ImageLimit(), AccountExtras.VideoLimit.unsupported())
}

View File

@ -869,7 +869,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
itemView.quotedMediaPreview.visibility = View.GONE
// Not available
val string = SpannableString.valueOf(context.getString(R.string.status_not_available_text))
val string = SpannableString.valueOf(context.getString(R.string.label_status_not_available))
string.setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context,
android.R.attr.textColorTertiary, itemView.text.currentTextColor)), 0,
string.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

View File

@ -337,8 +337,9 @@ class LengthyOperationsService : BaseIntentService("lengthy_operations") {
val mediaUri = Uri.parse(imageUri)
val listener = MessageMediaUploadListener(this, notificationManager,
builder, text)
val chucked = details.type == AccountType.TWITTER
val uploadResp = UpdateStatusTask.getBodyFromMedia(this, mediaLoader,
mediaUri, null, ParcelableMedia.Type.IMAGE, listener).use { body ->
mediaUri, null, ParcelableMedia.Type.IMAGE, chucked, listener).use { body ->
val resp = uploadMedia(twitterUpload, body.body)
body.deleteOnSuccess?.forEach { item ->
item.delete(this)

View File

@ -22,10 +22,7 @@ import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets
import org.apache.commons.lang3.ArrayUtils
import org.apache.commons.lang3.math.NumberUtils
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.ktextension.addAllTo
import org.mariotaku.ktextension.toDouble
import org.mariotaku.ktextension.toInt
import org.mariotaku.ktextension.toLong
import org.mariotaku.ktextension.*
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.fanfou.model.PhotoStatusUpdate
@ -42,6 +39,7 @@ import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.extension.model.size_limit
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.account.AccountExtras
import org.mariotaku.twidere.model.analyzer.UpdateStatus
@ -53,8 +51,10 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.io.ContentLengthInputStream
import org.mariotaku.twidere.util.io.DirectByteArrayOutputStream
import java.io.*
import java.io.Closeable
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -276,13 +276,12 @@ class UpdateStatusTask(
result.exceptions[i] = MicroBlogException(
context.getString(R.string.error_too_many_photos_fanfou))
} else {
val sizeLimit = getSizeLimit(account)
mediaBody = getBodyFromMedia(context, mediaLoader,
Uri.parse(statusUpdate.media[0].uri),
sizeLimit, statusUpdate.media[0].type,
ContentLengthInputStream.ReadListener { length, position ->
stateCallback.onUploadingProgressChanged(-1, position, length)
})
val sizeLimit = account.size_limit
val firstMedia = statusUpdate.media.first()
mediaBody = getBodyFromMedia(context, mediaLoader, Uri.parse(firstMedia.uri),
sizeLimit, firstMedia.type, false, ContentLengthInputStream.ReadListener { length, position ->
stateCallback.onUploadingProgressChanged(-1, position, length)
})
val photoUpdate = PhotoStatusUpdate(mediaBody.body,
pendingUpdate.overrideTexts[i])
val requestResult = microBlog.uploadPhoto(photoUpdate)
@ -465,7 +464,8 @@ class UpdateStatusTask(
upload: TwitterUpload,
account: AccountDetails,
update: ParcelableStatusUpdate,
ownerIds: Array<String>, chucked: Boolean
ownerIds: Array<String>,
chucked: Boolean
): SharedMediaUploadResult {
val deleteOnSuccess = ArrayList<MediaDeletionItem>()
val deleteAlways = ArrayList<MediaDeletionItem>()
@ -474,9 +474,9 @@ class UpdateStatusTask(
//noinspection TryWithIdenticalCatches
var body: MediaStreamBody? = null
try {
val sizeLimit = getSizeLimit(account)
val sizeLimit = account.size_limit
body = getBodyFromMedia(context, mediaLoader, Uri.parse(media.uri), sizeLimit,
media.type, ContentLengthInputStream.ReadListener { length, position ->
media.type, chucked, ContentLengthInputStream.ReadListener { length, position ->
stateCallback.onUploadingProgressChanged(index, position, length)
})
val mediaUploadEvent = MediaUploadEvent.create(context, media)
@ -586,12 +586,6 @@ class UpdateStatusTask(
return NumberUtils.toLong(draftUri.lastPathSegment, -1)
}
private fun getSizeLimit(details: AccountDetails): SizeLimit {
val imageLimit = AccountExtras.ImageLimit.ofSize(2048, 1536)
val videoLimit = AccountExtras.VideoLimit.twitterDefault()
return SizeLimit(imageLimit, videoLimit)
}
internal class PendingStatusUpdate(val length: Int, defaultText: String) {
constructor(statusUpdate: ParcelableStatusUpdate) : this(statusUpdate.accounts.size,
@ -621,7 +615,7 @@ class UpdateStatusTask(
val exception: UpdateStatusException?
val draftId: Long
val succeed: Boolean get() = !statuses.contains(null)
val succeed: Boolean get() = exception == null && statuses.isNotEmpty() && statuses.none { it == null }
constructor(count: Int, draftId: Long) {
this.statuses = arrayOfNulls(count)
@ -771,6 +765,7 @@ class UpdateStatusTask(
mediaUri: Uri,
sizeLimit: SizeLimit? = null,
@ParcelableMedia.Type type: Int,
chucked: Boolean,
readListener: ContentLengthInputStream.ReadListener
): MediaStreamBody {
val resolver = context.contentResolver
@ -787,7 +782,7 @@ class UpdateStatusTask(
ParcelableMedia.Type.IMAGE -> imageStream(context, resolver, mediaLoader, mediaUri,
mediaType, sizeLimit)
ParcelableMedia.Type.VIDEO -> videoStream(context, resolver, mediaUri, mediaType,
sizeLimit)
sizeLimit, chucked)
else -> null
} else null
@ -861,13 +856,15 @@ class UpdateStatusTask(
resolver: ContentResolver,
mediaUri: Uri,
defaultType: String?,
sizeLimit: SizeLimit
sizeLimit: SizeLimit,
chucked: Boolean
): MediaStreamData? {
var mediaType = defaultType
val videoLimit = sizeLimit.video
val geometry = Point()
var duration = -1L
var framerate = -1.0
var size = -1L
// TODO only transcode video if needed, use `MediaMetadataRetriever`
val retriever = MediaMetadataRetriever()
try {
@ -880,27 +877,36 @@ class UpdateStatusTask(
geometry.y = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt(-1)
duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong(-1)
framerate = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE).toDouble(-1.0)
size = resolver.openFileDescriptor(mediaUri, "r").use { it.statSize }
} catch (e: Exception) {
// Ignore
DebugLog.w(LOGTAG, "Unable to retrieve video info", e)
} finally {
try {
retriever.release()
} catch (e: Exception) {
// Ignore
}
retriever.releaseSafe()
}
if (videoLimit.checkGeometry(geometry.x, geometry.y)
&& videoLimit.checkFrameRate(framerate)) {
if (videoLimit.checkGeometry(geometry.x, geometry.y) && videoLimit.checkFrameRate(framerate)
&& videoLimit.checkSize(size, chucked)) {
// Size valid, upload directly
DebugLog.d(LOGTAG, "Upload video directly")
return null
}
if (!videoLimit.checkMinDuration(duration, chucked)) {
throw UploadException(context.getString(R.string.message_video_too_short))
}
if (!videoLimit.checkMaxDuration(duration, chucked)) {
throw UploadException(context.getString(R.string.message_video_too_long))
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
// Go get a new phone
return null
}
DebugLog.d(LOGTAG, "Transcoding video")
val ext = mediaUri.lastPathSegment.substringAfterLast(".")
val pfd = resolver.openFileDescriptor(mediaUri, "r")
val strategy = MediaFormatStrategyPresets.createAndroid720pStrategy()
val listener = object : MediaTranscoder.Listener {
override fun onTranscodeFailed(exception: Exception?) {
@ -916,12 +922,14 @@ class UpdateStatusTask(
}
}
val pfd = resolver.openFileDescriptor(mediaUri, "r")
val tempFile = File.createTempFile("twidere__encoded_video_", ".$ext", context.cacheDir)
val future = MediaTranscoder.getInstance().transcodeVideo(pfd.fileDescriptor,
tempFile.absolutePath, strategy, listener)
try {
future.get()
} catch (e: Exception) {
DebugLog.w(LOGTAG, "Error transcoding video, try upload directly", e)
tempFile.delete()
return null
}
@ -941,3 +949,4 @@ class UpdateStatusTask(
}
}

View File

@ -0,0 +1,30 @@
package org.mariotaku.twidere.util
import android.util.Log
import org.mariotaku.twidere.BuildConfig
/**
* Created by mariotaku on 2017/1/24.
*/
object DebugLog {
fun d(tag: String, msg: String, tr: Throwable? = null): Int {
if (!BuildConfig.DEBUG) return 0
if (tr != null) {
return Log.d(tag, msg, tr)
} else {
return Log.d(tag, msg)
}
}
fun w(tag: String, msg: String? = null, tr: Throwable? = null): Int {
if (!BuildConfig.DEBUG) return 0
if (msg != null && tr != null) {
return Log.w(tag, msg, tr)
} else if (msg != null) {
return Log.w(tag, msg)
} else {
return Log.w(tag, tr)
}
}
}

View File

@ -12,6 +12,7 @@ import android.widget.ImageView
import kotlinx.android.synthetic.main.list_item_status.view.*
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.USER_TYPE_FANFOU_COM
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter
import org.mariotaku.twidere.constant.SharedPreferenceConstants.VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE
import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable
@ -185,8 +186,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
quotedView.visibility = View.VISIBLE
val quoteContentAvailable = status.quoted_text_plain != null && status.quoted_text_unescaped != null
if (quoteContentAvailable) {
val isFanfouStatus = status.account_key.host == USER_TYPE_FANFOU_COM
if (quoteContentAvailable && !isFanfouStatus) {
quotedNameView.visibility = View.VISIBLE
quotedTextView.visibility = View.VISIBLE
@ -229,41 +230,30 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
quotedView.drawStart(ThemeUtils.getColorFromAttribute(context, R.attr.quoteIndicatorBackgroundColor, 0))
}
if (status.quoted_media?.isNotEmpty() ?: false) {
if (!adapter.sensitiveContentEnabled && status.is_possibly_sensitive) {
// Sensitive content, show label instead of media view
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.VISIBLE
} else if (!adapter.mediaPreviewEnabled) {
// Media preview disabled, just show label
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.VISIBLE
} else {
// Show media
quotedMediaPreview.visibility = View.VISIBLE
quotedMediaLabel.visibility = View.GONE
quotedMediaPreview.displayMedia(status.quoted_media, loader, status.account_key, -1,
null, null)
}
} else {
// No media, hide all related views
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.GONE
}
displayQuotedMedia(loader, status)
} else {
quotedNameView.visibility = View.GONE
quotedTextView.visibility = View.VISIBLE
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.GONE
// Not available
val string = SpannableString.valueOf(context.getString(R.string.status_not_available_text))
string.setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context,
android.R.attr.textColorTertiary, textView.currentTextColor)), 0,
string.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
quotedTextView.text = string
if (quoteContentAvailable) {
displayQuotedMedia(loader, status)
} else {
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.GONE
}
val quoteHint = if (!quoteContentAvailable) {
// Display 'not available' label
context.getString(R.string.label_status_not_available)
} else {
// Display 'original status' label
context.getString(R.string.label_original_status)
}
quotedTextView.text = SpannableString.valueOf(quoteHint).apply {
setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context,
android.R.attr.textColorTertiary, textView.currentTextColor)), 0,
length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
quotedView.drawStart(ThemeUtils.getColorFromAttribute(context, R.attr.quoteIndicatorBackgroundColor, 0))
}
@ -423,6 +413,32 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
}
private fun displayQuotedMedia(loader: MediaLoaderWrapper, status: ParcelableStatus) {
if (status.quoted_media?.isNotEmpty() ?: false) {
if (!adapter.sensitiveContentEnabled && status.is_possibly_sensitive) {
// Sensitive content, show label instead of media view
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.VISIBLE
} else if (!adapter.mediaPreviewEnabled) {
// Media preview disabled, just show label
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.VISIBLE
} else {
// Show media
quotedMediaPreview.visibility = View.VISIBLE
quotedMediaLabel.visibility = View.GONE
quotedMediaPreview.displayMedia(status.quoted_media, loader, status.account_key, -1,
null, null)
}
} else {
// No media, hide all related views
quotedMediaPreview.visibility = View.GONE
quotedMediaLabel.visibility = View.GONE
}
}
override fun onMediaClick(view: View, media: ParcelableMedia, accountKey: UserKey, extraId: Long) {
statusClickListener?.onMediaClick(this, view, media, layoutPosition)
}

View File

@ -418,7 +418,7 @@
<string name="unsubscribe">إلغاء الإشتراك</string>
<string name="background_toast_notification">التنبيهات البسيطة في الخلفية</string>
<string name="background_toast_notification_summary">اظهار التنبيهات الخفيفة اذا تحولت الى الخلفية</string>
<string name="original_status">التغريدة الأصلية</string>
<string name="label_original_status">التغريدة الأصلية</string>
<string name="ask">سأل</string>
<string name="no_close_after_status_updated">ترك \"كتابة التغريدة\" مفتوح بعد إرسال التغريدة</string>
<string name="no_close_after_status_updated_summary">لا تغلق النافذة عندما ترسل التغريدة</string>

View File

@ -432,7 +432,7 @@
<string name="unsubscribe">Desoscribise</string>
<string name="background_toast_notification">Fondu de burbuyes d\'avisu</string>
<string name="background_toast_notification_summary">Amuesa una burbuya d\'avisu al camudar de fondu</string>
<string name="original_status">Tuit orixinal</string>
<string name="label_original_status">Tuit orixinal</string>
<string name="ask">Entrugar</string>
<string name="no_close_after_status_updated">Caltener abiertu «Componer» dempués d\'unviar el tuit</string>
<string name="no_close_after_status_updated_summary">Un regalín pa «Chatterbox»</string>
@ -807,7 +807,7 @@
<string name="sign_in_with_ellip">Aniciar sesión con&#8230;</string>
<string name="edit_description">Editar descripción</string>
<string name="no_status_content_text">Ensin conteníu</string>
<string name="status_not_available_text">Tuit non disponible</string>
<string name="label_status_not_available">Tuit non disponible</string>
<string name="share_link">Compartir enllaz</string>
<string name="hide_retweets">Anubrir retuits</string>
<string name="hide_quotes">Anubrir cites</string>

View File

@ -426,7 +426,7 @@
<string name="unsubscribe">Canceŀla la subscripció</string>
<string name="background_toast_notification">Notificació en segon pla</string>
<string name="background_toast_notification_summary">Mostra una notificació emergent en canviar a segon pla</string>
<string name="original_status">Piulada original</string>
<string name="label_original_status">Piulada original</string>
<string name="ask">Pregunta</string>
<string name="no_close_after_status_updated">Mantén el menú «redacta» obert després d\'haver enviat la piulada</string>
<string name="no_close_after_status_updated_summary">Un regalet per a «chatterbox»</string>

View File

@ -383,7 +383,7 @@
<string name="sign_in_with_ellip">Přihlásit se pomocí&#8230;</string>
<string name="edit_description">Upravit popis</string>
<string name="no_status_content_text">Žádný obsah</string>
<string name="status_not_available_text">Tweet není k dispozici</string>
<string name="label_status_not_available">Tweet není k dispozici</string>
<string name="share_link">Sdílet odkaz</string>
<string name="hide_replies">Skrýt odpovědi</string>
<string name="no_extension_installed">Žádná nainstalovaná rozšíření</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">Abbestellen</string>
<string name="background_toast_notification">Hintergrundbenachrichtigung</string>
<string name="background_toast_notification_summary">Benachrichtigung einblenden, wenn die App in den Hintergrund geht</string>
<string name="original_status">Original-Tweet</string>
<string name="label_original_status">Original-Tweet</string>
<string name="ask">Frage</string>
<string name="no_close_after_status_updated">Eingabemaske geöffnet lassen, nachdem der Tweet gesendet wurde</string>
<string name="no_close_after_status_updated_summary">Ein kleines Geschenk an die Chatterbox</string>
@ -810,7 +810,7 @@
<string name="sign_in_with_ellip">Anmelden mit&#8230;</string>
<string name="edit_description">Beschreibung bearbeiten</string>
<string name="no_status_content_text">Ohne Inhalt</string>
<string name="status_not_available_text">Tweet nicht verfügbar</string>
<string name="label_status_not_available">Tweet nicht verfügbar</string>
<string name="share_link">Link teilen</string>
<string name="hide_retweets">Retweets verbergen</string>
<string name="hide_quotes">Zitate verbergen</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">Desuscribirse</string>
<string name="background_toast_notification">Notificaciones emergentes en segundo plano</string>
<string name="background_toast_notification_summary">Mostrar notificaciones emergentes cuando esté activa en segundo plano</string>
<string name="original_status">Tweet original</string>
<string name="label_original_status">Tweet original</string>
<string name="ask">Preguntar</string>
<string name="no_close_after_status_updated">Mantener «redactar» abierto después de enviar el tweet</string>
<string name="no_close_after_status_updated_summary">Un pequeño regalo para chatterbox</string>
@ -810,7 +810,7 @@
<string name="sign_in_with_ellip">Registrarse con&#8230;</string>
<string name="edit_description">Editar descripción</string>
<string name="no_status_content_text">No hay contenido</string>
<string name="status_not_available_text">Tweet no disponible</string>
<string name="label_status_not_available">Tweet no disponible</string>
<string name="share_link">Compartir enlace</string>
<string name="hide_retweets">Esconder retweets</string>
<string name="hide_quotes">Esconder citas</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">مشترک نشدن</string>
<string name="background_toast_notification">آگاهی تست پس‌زمینه</string>
<string name="background_toast_notification_summary">نمایش آگاهی تست هنگام تعویض به پس‌زمینه</string>
<string name="original_status">توییت اصلی</string>
<string name="label_original_status">توییت اصلی</string>
<string name="ask">پرسش</string>
<string name="no_close_after_status_updated">باز نگه داشتن «ایجاد»، پس از ارسال توییت</string>
<string name="no_close_after_status_updated_summary">هدیه‌ای گوچک برای پرحرف‌ها</string>
@ -809,7 +809,7 @@
<string name="sign_in_with_ellip">ورود با&#8230;</string>
<string name="edit_description">ویرایش توضیح</string>
<string name="no_status_content_text">بدون محتوا</string>
<string name="status_not_available_text">توییت موجود نیست</string>
<string name="label_status_not_available">توییت موجود نیست</string>
<string name="share_link">هم‌رسانی پیوند</string>
<string name="hide_retweets">پنهان کردن بازتوییت‌ها</string>
<string name="hide_quotes">پنهان کردن نقل‌قول‌ها</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">Poista tilaus</string>
<string name="background_toast_notification">Taustapalvelun ilmoitukset tilapalkissa</string>
<string name="background_toast_notification_summary">Näytä tilapalkin ilmoitukset, kun ohjelma on taustalla</string>
<string name="original_status">Alkuperäinen twiitti</string>
<string name="label_original_status">Alkuperäinen twiitti</string>
<string name="ask">Kysy</string>
<string name="no_close_after_status_updated">Pidä Kirjoita-näkymä auki twiitin lähettämisen jälkeen</string>
<string name="no_close_after_status_updated_summary">Pieni lisä chatterboxiin</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">Se désabonner</string>
<string name="background_toast_notification">Notification de fond</string>
<string name="background_toast_notification_summary">Afficher une notification lorsque l\'application est en arrière-plan</string>
<string name="original_status">Tweet original</string>
<string name="label_original_status">Tweet original</string>
<string name="ask">Demander</string>
<string name="no_close_after_status_updated">Gardez « Composer » ouvert après avoir envoyé le tweet</string>
<string name="no_close_after_status_updated_summary">Un petit cadeau pour Chatterbox</string>
@ -810,7 +810,7 @@
<string name="sign_in_with_ellip">Se connecter avec&#8230;</string>
<string name="edit_description">Éditer la description</string>
<string name="no_status_content_text">Aucun contenu</string>
<string name="status_not_available_text">Tweet non disponible</string>
<string name="label_status_not_available">Tweet non disponible</string>
<string name="share_link">Partager le lien</string>
<string name="hide_retweets">Masquer les retweets</string>
<string name="hide_quotes">Masquer les citations</string>

View File

@ -427,7 +427,7 @@
<string name="unsubscribe">Anular subscrición</string>
<string name="background_toast_notification">Notificación Toast en segundo plano</string>
<string name="background_toast_notification_summary">Amosar notificación Toast cando se cambia a segundo plano</string>
<string name="original_status">Chío orixinal</string>
<string name="label_original_status">Chío orixinal</string>
<string name="ask">Pregunta</string>
<string name="no_close_after_status_updated">Deixar aberta a caixa \"Escribir\" despois de chiar</string>
<string name="no_close_after_status_updated_summary">Un pequeno agasallo para chatterbox</string>

View File

@ -387,7 +387,7 @@
<string name="unsubscribe">Otkaži pretplatu</string>
<string name="background_toast_notification">Pozadinska Toast obavijest</string>
<string name="background_toast_notification_summary">Prikaži Toast obavijest kada je u pozadini</string>
<string name="original_status">Originalni tweet</string>
<string name="label_original_status">Originalni tweet</string>
<string name="ask">Pitaj</string>
<string name="no_close_after_status_updated">Ostavi \"Sastavi\" otvorenim kada je tweet poslan</string>
<string name="no_close_after_status_updated_summary">Maleni poklon za brbljavce</string>

View File

@ -423,7 +423,7 @@
<string name="unsubscribe">Leiratkozás</string>
<string name="background_toast_notification">Felugró értesítés a háttérben</string>
<string name="background_toast_notification_summary">Mutassa a felugró értesítéseket mikor a háttérben van</string>
<string name="original_status">Fiók törlése</string>
<string name="label_original_status">Fiók törlése</string>
<string name="ask">Kérdezzen</string>
<string name="no_close_after_status_updated">Maradjon nyitva a szerkesztő tweet után</string>
<string name="no_close_after_status_updated_summary">Egy apró ajándék a locsifecsiknek</string>

View File

@ -429,7 +429,7 @@
<string name="unsubscribe">Batal ikuti</string>
<string name="background_toast_notification">Notifikasi Toast latar belakang</string>
<string name="background_toast_notification_summary">Tampilkan notifikasi Toast saat diset ke latar belakang</string>
<string name="original_status">Cuit asli</string>
<string name="label_original_status">Cuit asli</string>
<string name="ask">Bertanya</string>
<string name="no_close_after_status_updated">Tetap \"Menulis\" terbuka setelah tweet terkirim</string>
<string name="no_close_after_status_updated_summary">Pemberian kecil untuk chatterbox</string>
@ -805,7 +805,7 @@
<string name="sign_in_with_ellip">Masuk menggunakan&#8230;</string>
<string name="edit_description">Mengubah deskripsi</string>
<string name="no_status_content_text">Tanpa konten</string>
<string name="status_not_available_text">Tweet tidak tersedia</string>
<string name="label_status_not_available">Tweet tidak tersedia</string>
<string name="share_link">Bagikan tautan</string>
<string name="hide_retweets">Sembunyikan retweet</string>
<string name="hide_quotes">Sembunyikan kutipan</string>

View File

@ -429,7 +429,7 @@
<string name="unsubscribe">Cancellati</string>
<string name="background_toast_notification">Notifica in background</string>
<string name="background_toast_notification_summary">Mostra le notifiche toast quando si passa in background</string>
<string name="original_status">Tweet originale</string>
<string name="label_original_status">Tweet originale</string>
<string name="ask">Chiedi</string>
<string name="no_close_after_status_updated">Mantieni \"Componi\" aperto dopo aver inviato il tweet</string>
<string name="no_close_after_status_updated_summary">Un piccolo regalo per i chiacchieroni</string>
@ -780,7 +780,7 @@
<string name="sign_in_with_ellip">Collegati con&#8230;</string>
<string name="edit_description">Modifica descrizione</string>
<string name="no_status_content_text">Nessun contenuto</string>
<string name="status_not_available_text">Tweet non disponibile</string>
<string name="label_status_not_available">Tweet non disponibile</string>
<string name="share_link">Condividi link</string>
<string name="hide_retweets">Nascondi retweets</string>
<string name="hide_quotes">Nascondi citazioni</string>

View File

@ -326,7 +326,7 @@
<string name="added_user_to_list">המשתמש <xliff:g id="user">%1$s</xliff:g> נוסף לרשימה "<xliff:g id="list">%2$s</xliff:g>\".</string>
<string name="deleted_user_from_list">המשתמש <xliff:g id="user">%1$s</xliff:g> נמחק מהרשימה "<xliff:g id="list">%2$s</xliff:g>\".</string>
<string name="delete_user_from_list_confirm">מחק את <xliff:g id="user">%1$s</xliff:g> מהרשימה "<xliff:g id="list">%2$s</xliff:g>\"?</string>
<string name="original_status">ציוץ מקורי</string>
<string name="label_original_status">ציוץ מקורי</string>
<string name="no_close_after_status_updated">אל תסגור את \"כתוב\" לאחר שליחת ציוץ</string>
<string name="no_close_after_status_updated_summary">מתנה קטנה לchatterbox</string>
<string name="status_saved_to_draft">הציוץ נשמר לטיוטה.</string>

View File

@ -431,7 +431,7 @@
<string name="unsubscribe">保存を解除</string>
<string name="background_toast_notification">バックグラウンドトースト通知</string>
<string name="background_toast_notification_summary">バックグラウンドに切り替わったときにトースト通知を表示します。</string>
<string name="original_status">元のツイート</string>
<string name="label_original_status">元のツイート</string>
<string name="ask">尋ねる</string>
<string name="no_close_after_status_updated">編集画面をツイート送信後に開いたままにする</string>
<string name="no_close_after_status_updated_summary">ツイート編集画面の動作を変更します。</string>
@ -806,7 +806,7 @@
<string name="sign_in_with_ellip">ログイン方法</string>
<string name="edit_description">説明を編集</string>
<string name="no_status_content_text">コンテンツがありません</string>
<string name="status_not_available_text">このツイートは利用できません</string>
<string name="label_status_not_available">このツイートは利用できません</string>
<string name="share_link">共有リンク</string>
<string name="hide_retweets">リツイートを非表示</string>
<string name="hide_quotes">引用リツイートを非表示</string>

View File

@ -426,7 +426,7 @@
<string name="unsubscribe">구독 취소</string>
<string name="background_toast_notification">백그라운드 토스트 알림</string>
<string name="background_toast_notification_summary">백그라운드로 전환되었을 때 토스트 알림을 보여줍니다.</string>
<string name="original_status">원본 트윗</string>
<string name="label_original_status">원본 트윗</string>
<string name="ask">물어보기</string>
<string name="no_close_after_status_updated">\"작성\"창을 트윗을 보내고도 유지한다</string>
<string name="no_close_after_status_updated_summary">트윗 입력창에 작은 특전이 있습니다.</string>

View File

@ -142,7 +142,7 @@
<string name="default_ringtone">Nada dering lalai</string>
<string name="profile_images">Imej profil</string>
<string name="theme_color">Warna tema</string>
<string name="original_status">Tweet asal</string>
<string name="label_original_status">Tweet asal</string>
<string name="default_account">Akaun lalai</string>
<string name="select_user">Pilih pengguna</string>
<string name="theme">Tema</string>

View File

@ -425,7 +425,7 @@
<string name="unsubscribe">Uitschrijven</string>
<string name="background_toast_notification">Achtergrond Toast-melding</string>
<string name="background_toast_notification_summary">Laat Toast-melding zien wanneer er naar de achtergrond geschakeld wordt</string>
<string name="original_status">Originele tweet</string>
<string name="label_original_status">Originele tweet</string>
<string name="ask">Vraag</string>
<string name="no_close_after_status_updated">Hou \"Componeren\" open na versturen van een tweet</string>
<string name="no_close_after_status_updated_summary">Een klein cadeau voor chatterbox</string>
@ -804,7 +804,7 @@
<string name="sign_in_with_ellip">Inloggen met&#8230;</string>
<string name="edit_description">Bewerk omschrijving</string>
<string name="no_status_content_text">Geen inhoud</string>
<string name="status_not_available_text">Tweet niet beschikbaar</string>
<string name="label_status_not_available">Tweet niet beschikbaar</string>
<string name="share_link">Link delen</string>
<string name="hide_retweets">Retweets verbergen</string>
<string name="hide_quotes">Quotes verbergen</string>

View File

@ -408,7 +408,7 @@
<string name="unsubscribe">Avslutt abonnement</string>
<string name="background_toast_notification">Varsler i bakgrunnen</string>
<string name="background_toast_notification_summary">Vis popup-varsler når appen kjører i bakgrunnen</string>
<string name="original_status">Opprinnelig tweet</string>
<string name="label_original_status">Opprinnelig tweet</string>
<string name="ask">Spør</string>
<string name="no_close_after_status_updated">Behold «Skriv»-vinduet etter sendte tweets</string>
<string name="no_close_after_status_updated_summary">En liten gave for Chatterbox</string>

View File

@ -376,7 +376,7 @@
<string name="unsubscribe">Wypisz</string>
<string name="background_toast_notification">Powiadomienia Toast</string>
<string name="background_toast_notification_summary">Pokazuje powiadomienia Toas gdy aplikacja działa w tle</string>
<string name="original_status">Oryginalny tweet</string>
<string name="label_original_status">Oryginalny tweet</string>
<string name="ask">Zapytaj</string>
<string name="no_close_after_status_updated">Zostaw okno \"Tweetnij\" otwarte po wysłaniu tweeta</string>
<string name="no_close_after_status_updated_summary">Drobny prezent dla gaduł</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">Sair</string>
<string name="background_toast_notification">Notificação toast em segundo plano</string>
<string name="background_toast_notification_summary">Exibir notificações toast quando estiver rodando em segundo plano</string>
<string name="original_status">Tweet original</string>
<string name="label_original_status">Tweet original</string>
<string name="ask">Perguntar</string>
<string name="no_close_after_status_updated">Manter a tela de edição aberta após o tweet ser enviado</string>
<string name="no_close_after_status_updated_summary">Um presentinho para os tagarelas</string>

View File

@ -427,7 +427,7 @@
<string name="unsubscribe">Отписаться</string>
<string name="background_toast_notification">Фоновое всплывающее уведомление</string>
<string name="background_toast_notification_summary">Показывать всплывающее уведомление когда свернуто в фон</string>
<string name="original_status">Исходный твит</string>
<string name="label_original_status">Исходный твит</string>
<string name="ask">Спросить</string>
<string name="no_close_after_status_updated">Оставлять \"Написать\" открытым после отправки твита</string>
<string name="no_close_after_status_updated_summary">Небольшой подарок для любителей поболтать</string>
@ -802,7 +802,7 @@
<string name="sign_in_with_ellip">Вход с помощью...</string>
<string name="edit_description">Изменить описание</string>
<string name="no_status_content_text">Нет содержимого</string>
<string name="status_not_available_text">Твит недоступен</string>
<string name="label_status_not_available">Твит недоступен</string>
<string name="share_link">Поделиться</string>
<string name="hide_retweets">Скрыть ретвиты</string>
<string name="hide_quotes">Скрыть цитируемые</string>

View File

@ -429,7 +429,7 @@
<string name="unsubscribe">เลิกรับข่าวสาร</string>
<string name="background_toast_notification">แจ้งเตือน Toast ของพื้นหลัง</string>
<string name="background_toast_notification_summary">แสดงการแจ้งเตือน Toast เมื่อเปลี่ยนเป็นเบื้องหลัง</string>
<string name="original_status">ทวีตเดิม</string>
<string name="label_original_status">ทวีตเดิม</string>
<string name="ask">ถาม</string>
<string name="no_close_after_status_updated">เปิดหน้า \"ประพันธ์\" หลังจากส่งทวีตแล้ว</string>
<string name="no_close_after_status_updated_summary">ของขวัญเล็กๆ น้อยๆ จาก chatterbox</string>
@ -804,7 +804,7 @@
<string name="sign_in_with_ellip">ลงชื่อเข้าใช้ด้วย...</string>
<string name="edit_description">แก้ไขคำอธิบาย</string>
<string name="no_status_content_text">ไม่มีเนื้อหา</string>
<string name="status_not_available_text">ทวิตไม่พร้อมใช้งาน</string>
<string name="label_status_not_available">ทวิตไม่พร้อมใช้งาน</string>
<string name="share_link">แชร์ลิงค์</string>
<string name="hide_retweets">ซ่อนรีทวิต</string>
<string name="hide_quotes">ซ่อนคำอ้างอิง</string>

View File

@ -428,7 +428,7 @@
<string name="unsubscribe">Abonelikten çık</string>
<string name="background_toast_notification">Arkaplan Toast bildirimi</string>
<string name="background_toast_notification_summary">Arkaplanda çalışırken Tost mesajlarını göster</string>
<string name="original_status">Orijinal tweet</string>
<string name="label_original_status">Orijinal tweet</string>
<string name="ask">Sor</string>
<string name="no_close_after_status_updated">Tweeti gönderdikten sonra \"Yeni tweet\"i açık tut</string>
<string name="no_close_after_status_updated_summary">Geveze için küçük bir hediye</string>
@ -803,7 +803,7 @@
<string name="sign_in_with_ellip">Şununla giriş yap&#8230;</string>
<string name="edit_description">ıklama düzenle</string>
<string name="no_status_content_text">İçerik yok</string>
<string name="status_not_available_text">Tweet mevcut değil</string>
<string name="label_status_not_available">Tweet mevcut değil</string>
<string name="share_link">Bağlantı paylaş</string>
<string name="hide_retweets">Retweet\'leri gizle</string>
<string name="hide_quotes">Alıntıları gizle</string>

View File

@ -429,7 +429,7 @@
<string name="unsubscribe">Відписатися</string>
<string name="background_toast_notification">Спливаюче фонове сповіщення</string>
<string name="background_toast_notification_summary">Показувати спливаючі сповіщення при переключенні у фоновий режим</string>
<string name="original_status">Первісний твіт</string>
<string name="label_original_status">Первісний твіт</string>
<string name="ask">Запитати</string>
<string name="no_close_after_status_updated">Не закривати вікно створення твіту після надсилання</string>
<string name="no_close_after_status_updated_summary">Маленький подарунок для базіки</string>
@ -804,7 +804,7 @@
<string name="sign_in_with_ellip">Увійти через&#8230;</string>
<string name="edit_description">Редагувати опис</string>
<string name="no_status_content_text">Немає вмісту</string>
<string name="status_not_available_text">Твіт недоступний</string>
<string name="label_status_not_available">Твіт недоступний</string>
<string name="share_link">Поділитися посиланням</string>
<string name="hide_retweets">Сховати ретвіти</string>
<string name="hide_quotes">Сховати цитати</string>

View File

@ -433,7 +433,7 @@
<string name="unsubscribe">退订</string>
<string name="background_toast_notification">背景Toast通知</string>
<string name="background_toast_notification_summary">切换到后台后显示Toast通知</string>
<string name="original_status">原推文</string>
<string name="label_original_status">原推文</string>
<string name="ask">询问</string>
<string name="no_close_after_status_updated">发送推文之后仍然保持“撰写”开启</string>
<string name="no_close_after_status_updated_summary">话痨专属</string>
@ -810,7 +810,7 @@
<string name="sign_in_with_ellip">登录方式&#8230;</string>
<string name="edit_description">编辑描述</string>
<string name="no_status_content_text">没有内容</string>
<string name="status_not_available_text">推文不可用</string>
<string name="label_status_not_available">推文不可用</string>
<string name="share_link">分享链接</string>
<string name="hide_retweets">隐藏转推</string>
<string name="hide_quotes">隐藏引用</string>

View File

@ -432,7 +432,7 @@
<string name="unsubscribe">退訂</string>
<string name="background_toast_notification">背景 Toast 通知</string>
<string name="background_toast_notification_summary">切換到背景後顯示 Toast 通知</string>
<string name="original_status">原推文</string>
<string name="label_original_status">原推文</string>
<string name="ask">詢問</string>
<string name="no_close_after_status_updated">發送推文之後仍然保持\"撰寫\"開啟</string>
<string name="no_close_after_status_updated_summary">給不二的小禮物</string>
@ -803,7 +803,7 @@
<string name="sign_in_with_ellip">登入方式&#8230;</string>
<string name="edit_description">編輯簡介</string>
<string name="no_status_content_text">沒有內容</string>
<string name="status_not_available_text">推文不可用</string>
<string name="label_status_not_available">推文不可用</string>
<string name="share_link">分享連結</string>
<string name="hide_retweets">隱藏轉推</string>
<string name="hide_quotes">隱藏引用</string>

View File

@ -440,7 +440,7 @@
<string name="unsubscribe">Unsubscribe</string>
<string name="background_toast_notification">Background Toast notification</string>
<string name="background_toast_notification_summary">Show Toast notification when switched to background</string>
<string name="original_status">Original tweet</string>
<string name="label_original_status">Original tweet</string>
<string name="ask">Ask</string>
<string name="no_close_after_status_updated">Keep \"Compose\" open after tweet sent</string>
<string name="no_close_after_status_updated_summary">A little gift for chatterbox</string>
@ -817,7 +817,7 @@
<string name="sign_in_with_ellip">Sign in with…</string>
<string name="edit_description">Edit description</string>
<string name="no_status_content_text">No content</string>
<string name="status_not_available_text">Tweet not available</string>
<string name="label_status_not_available">Tweet not available</string>
<string name="share_link">Share link</string>
<string name="hide_retweets">Hide retweets</string>
<string name="hide_quotes">Hide quotes</string>
@ -908,4 +908,6 @@
<string name="summary_interactions_not_available">Only available with official keys</string>
<string name="label_password_sign_in">Password sign in</string>
<string name="message_compose_write_storage_permission_not_granted">Permission is required to delete taken photo/video</string>
<string name="message_video_too_short">Video too short</string>
<string name="message_video_too_long">Video too long</string>
</resources>