Merge remote-tracking branch 'tuskyapp/master'
This commit is contained in:
commit
aa48555902
|
@ -113,7 +113,7 @@ dependencies {
|
|||
implementation 'androidx.browser:browser:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha05'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha10'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.1.0-alpha04'
|
||||
|
|
|
@ -103,7 +103,6 @@
|
|||
<activity android:name=".ViewTagActivity" />
|
||||
<activity
|
||||
android:name=".ViewMediaActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@style/TuskyBaseTheme" />
|
||||
<activity
|
||||
android:name=".AccountActivity"
|
||||
|
|
|
@ -227,20 +227,28 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
|
||||
// Setup the toolbar.
|
||||
setSupportActionBar(accountToolbar)
|
||||
supportActionBar?.title = null
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
supportActionBar?.run {
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
ThemeUtils.setDrawableTint(this, accountToolbar.navigationIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
||||
ThemeUtils.setDrawableTint(this, accountToolbar.overflowIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
||||
|
||||
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
|
||||
accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
||||
|
||||
@AttrRes
|
||||
var priorAttribute = R.attr.account_toolbar_icon_tint_uncollapsed
|
||||
|
||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
||||
|
||||
if(verticalOffset == oldOffset) {
|
||||
return
|
||||
}
|
||||
oldOffset = verticalOffset
|
||||
|
||||
@AttrRes val attribute = if (titleVisibleHeight + verticalOffset < 0) {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(true)
|
||||
|
||||
|
@ -265,7 +273,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
accountFloatingActionButton.hide()
|
||||
}
|
||||
}
|
||||
oldOffset = verticalOffset
|
||||
|
||||
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
|
||||
|
||||
|
|
|
@ -50,12 +50,8 @@ import java.util.List;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import retrofit2.Call;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity implements Injectable {
|
||||
|
||||
protected List<Call> callList;
|
||||
|
||||
@Inject
|
||||
public ThemeUtils themeUtils;
|
||||
@Inject
|
||||
|
@ -95,7 +91,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
redirectIfNotLoggedIn();
|
||||
}
|
||||
|
||||
callList = new ArrayList<>();
|
||||
requesters = new HashMap<>();
|
||||
}
|
||||
|
||||
|
@ -164,14 +159,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
for (Call call : callList) {
|
||||
call.cancel();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void showAccountChooserDialog(CharSequence dialogTitle, boolean showActiveAccount, AccountSelectionListener listener) {
|
||||
List<AccountEntity> accounts = accountManager.getAllAccountsOrderedByActive();
|
||||
AccountEntity activeAccount = accountManager.getActiveAccount();
|
||||
|
|
|
@ -182,7 +182,7 @@ public final class ComposeActivity
|
|||
ComposeAutoCompleteAdapter.AutocompletionProvider,
|
||||
OnEmojiSelectedListener,
|
||||
Injectable, InputConnectionCompat.OnCommitContentListener,
|
||||
DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {
|
||||
TimePickerDialog.OnTimeSetListener {
|
||||
|
||||
private static final String TAG = "ComposeActivity"; // logging tag
|
||||
static final int STATUS_CHARACTER_LIMIT = 500;
|
||||
|
@ -244,8 +244,8 @@ public final class ComposeActivity
|
|||
private ImageButton contentWarningButton;
|
||||
private ImageButton emojiButton;
|
||||
private ImageButton hideMediaToggle;
|
||||
private TextView actionAddPoll;
|
||||
private ImageButton scheduleButton;
|
||||
private TextView actionAddPoll;
|
||||
private Button atButton;
|
||||
private Button hashButton;
|
||||
|
||||
|
@ -1867,6 +1867,7 @@ public final class ComposeActivity
|
|||
color = ContextCompat.getColor(this, R.color.tusky_blue);
|
||||
} else {
|
||||
contentWarningBar.setVisibility(View.GONE);
|
||||
textEditor.requestFocus();
|
||||
color = ThemeUtils.getColor(this, android.R.attr.textColorTertiary);
|
||||
}
|
||||
contentWarningButton.getDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
|
@ -2098,6 +2099,10 @@ public final class ComposeActivity
|
|||
updateVisibleCharactersLeft();
|
||||
}
|
||||
|
||||
if (!new VersionUtils(instance.getVersion()).supportsScheduledToots()) {
|
||||
scheduleButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (instance.getPollLimits() != null) {
|
||||
maxPollOptions = instance.getPollLimits().getMaxOptions();
|
||||
maxPollOptionLength = instance.getPollLimits().getMaxOptionChars();
|
||||
|
@ -2203,13 +2208,6 @@ public final class ComposeActivity
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
|
||||
scheduleView.onDateSet(year, month, dayOfMonth);
|
||||
updateScheduleButton();
|
||||
scheduleBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
|
||||
scheduleView.onTimeSet(hourOfDay, minute);
|
||||
|
|
|
@ -8,7 +8,6 @@ import androidx.appcompat.widget.Toolbar
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.gson.Gson
|
||||
import com.keylesspalace.tusky.adapter.ScheduledTootAdapter
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.StatusScheduledEvent
|
||||
|
@ -44,8 +43,6 @@ class ScheduledTootActivity : BaseActivity(), ScheduledTootAdapter.ScheduledToot
|
|||
@Inject
|
||||
lateinit var eventHub: EventHub
|
||||
|
||||
val gson = Gson()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_scheduled_toot)
|
||||
|
|
|
@ -812,7 +812,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
String description = context.getString(R.string.description_status,
|
||||
status.getUserFullName(),
|
||||
getContentWarningDescription(context, status),
|
||||
(!status.isSensitive() || status.isExpanded() ? status.getContent() : ""),
|
||||
(TextUtils.isEmpty(status.getSpoilerText()) || !status.isSensitive() || status.isExpanded() ? status.getContent() : ""),
|
||||
getCreatedAtDescription(status.getCreatedAt()),
|
||||
getReblogDescription(context, status),
|
||||
status.getNickname(),
|
||||
|
|
|
@ -26,8 +26,8 @@ data class NewStatus(
|
|||
val visibility: String,
|
||||
val sensitive: Boolean,
|
||||
@SerializedName("media_ids") val mediaIds: List<String>?,
|
||||
val poll: NewPoll?,
|
||||
@SerializedName("scheduled_at") val scheduledAt: String?,
|
||||
val poll: NewPoll?,
|
||||
@SerializedName("quote_id") val quoteId: String?
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* Copyright 2019 kyori19
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.entity
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* Copyright 2019 kyori19
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.entity
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
|
|
@ -57,7 +57,8 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
lateinit var api: MastodonApi
|
||||
|
||||
private lateinit var type: Type
|
||||
private lateinit var id: String
|
||||
private var id: String? = null
|
||||
|
||||
private lateinit var scrollListener: EndlessOnScrollListener
|
||||
private lateinit var adapter: AccountAdapter
|
||||
private var fetching = false
|
||||
|
@ -66,7 +67,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
type = arguments?.getSerializable(ARG_TYPE) as Type
|
||||
id = arguments?.getString(ARG_ID)!!
|
||||
id = arguments?.getString(ARG_ID)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
|
@ -251,36 +252,52 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
Log.e(TAG, "Failed to $verb account id $accountId.")
|
||||
}
|
||||
|
||||
private fun getFetchCallByListType(type: Type, fromId: String?): Single<Response<List<Account>>> {
|
||||
private fun getFetchCallByListType(fromId: String?): Single<Response<List<Account>>> {
|
||||
return when (type) {
|
||||
Type.FOLLOWS -> api.accountFollowing(id, fromId)
|
||||
Type.FOLLOWERS -> api.accountFollowers(id, fromId)
|
||||
Type.FOLLOWS -> {
|
||||
val accountId = requireId(type, id)
|
||||
api.accountFollowing(accountId, fromId)
|
||||
}
|
||||
Type.FOLLOWERS -> {
|
||||
val accountId = requireId(type, id)
|
||||
api.accountFollowers(accountId, fromId)
|
||||
}
|
||||
Type.BLOCKS -> api.blocks(fromId)
|
||||
Type.MUTES -> api.mutes(fromId)
|
||||
Type.FOLLOW_REQUESTS -> api.followRequests(fromId)
|
||||
Type.REBLOGGED -> api.statusRebloggedBy(id, fromId)
|
||||
Type.FAVOURITED -> api.statusFavouritedBy(id, fromId)
|
||||
Type.REBLOGGED -> {
|
||||
val statusId = requireId(type, id)
|
||||
api.statusRebloggedBy(statusId, fromId)
|
||||
}
|
||||
Type.FAVOURITED -> {
|
||||
val statusId = requireId(type, id)
|
||||
api.statusFavouritedBy(statusId, fromId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchAccounts(id: String? = null) {
|
||||
private fun requireId(type: Type, id: String?): String {
|
||||
return requireNotNull(id) { "id must not be null for type "+type.name }
|
||||
}
|
||||
|
||||
private fun fetchAccounts(fromId: String? = null) {
|
||||
if (fetching) {
|
||||
return
|
||||
}
|
||||
fetching = true
|
||||
|
||||
if (id != null) {
|
||||
if (fromId != null) {
|
||||
recyclerView.post { adapter.setBottomLoading(true) }
|
||||
}
|
||||
|
||||
getFetchCallByListType(type, id)
|
||||
getFetchCallByListType(fromId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe({ response ->
|
||||
val accountList = response.body()
|
||||
|
||||
if (response.isSuccessful && accountList != null) {
|
||||
val linkHeader = response.headers().get("Link")
|
||||
val linkHeader = response.headers()["Link"]
|
||||
onFetchAccountsSuccess(accountList, linkHeader)
|
||||
} else {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package com.keylesspalace.tusky.fragment;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.keylesspalace.tusky.ComposeActivity;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DatePickerFragment extends DialogFragment {
|
||||
|
||||
public static final String PICKER_TIME_YEAR = "picker_time_year";
|
||||
public static final String PICKER_TIME_MONTH = "picker_time_month";
|
||||
public static final String PICKER_TIME_DAY = "picker_time_day";
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Bundle args = getArguments();
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
|
||||
if (args != null) {
|
||||
calendar.set(args.getInt(PICKER_TIME_YEAR),
|
||||
args.getInt(PICKER_TIME_MONTH),
|
||||
args.getInt(PICKER_TIME_DAY));
|
||||
}
|
||||
|
||||
return new DatePickerDialog(getContext(),
|
||||
android.R.style.Theme_DeviceDefault_Dialog,
|
||||
(ComposeActivity) getActivity(),
|
||||
calendar.get(Calendar.YEAR),
|
||||
calendar.get(Calendar.MONTH),
|
||||
calendar.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
|
@ -52,6 +53,7 @@ import com.keylesspalace.tusky.db.AccountManager;
|
|||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Filter;
|
||||
import com.keylesspalace.tusky.entity.PollOption;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.network.TimelineCases;
|
||||
|
@ -474,7 +476,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
|||
});
|
||||
}
|
||||
|
||||
void reloadFilters(boolean forceRefresh) {
|
||||
@VisibleForTesting
|
||||
public void reloadFilters(boolean forceRefresh) {
|
||||
if (filters != null && !forceRefresh) {
|
||||
applyFilters(forceRefresh);
|
||||
return;
|
||||
|
@ -498,7 +501,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
|||
});
|
||||
}
|
||||
|
||||
protected boolean filterIsRelevant(Filter filter) {
|
||||
protected boolean filterIsRelevant(@NonNull Filter filter) {
|
||||
// Called when building local filter expression
|
||||
// Override to select relevant filters for your fragment
|
||||
return false;
|
||||
|
@ -509,7 +512,17 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
|||
// Override to refresh your fragment
|
||||
}
|
||||
|
||||
boolean shouldFilterStatus(Status status) {
|
||||
@VisibleForTesting
|
||||
public boolean shouldFilterStatus(Status status) {
|
||||
|
||||
if(filterRemoveRegex && status.getPoll() != null) {
|
||||
for(PollOption option: status.getPoll().getOptions()) {
|
||||
if(filterRemoveRegexMatcher.reset(option.getTitle()).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (filterRemoveRegex && (filterRemoveRegexMatcher.reset(status.getActionableStatus().getContent()).find()
|
||||
|| (!status.getSpoilerText().isEmpty() && filterRemoveRegexMatcher.reset(status.getActionableStatus().getSpoilerText()).find())));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* Copyright 2019 kyori19
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.fragment;
|
||||
|
||||
import android.app.Dialog;
|
||||
|
|
|
@ -76,7 +76,6 @@ import com.keylesspalace.tusky.viewdata.StatusViewData;
|
|||
import net.accelf.yuito.TimelineStreamingListener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -356,12 +355,12 @@ public class TimelineFragment extends SFragment implements
|
|||
}
|
||||
|
||||
private void updateCurrent() {
|
||||
String topId;
|
||||
if (this.statuses.isEmpty()) {
|
||||
topId = null;
|
||||
} else {
|
||||
topId = CollectionsKt.first(this.statuses, Either::isRight).asRight().getId();
|
||||
return;
|
||||
}
|
||||
|
||||
String topId = CollectionsKt.first(this.statuses, Either::isRight).asRight().getId();
|
||||
|
||||
this.timelineRepo.getStatuses(topId, null, null, LOAD_AT_ONCE,
|
||||
TimelineRequestMode.NETWORK)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -373,7 +372,7 @@ public class TimelineFragment extends SFragment implements
|
|||
if (!statuses.isEmpty()) {
|
||||
filterStatuses(statuses);
|
||||
|
||||
if (!this.statuses.isEmpty() && topId != null) {
|
||||
if (!this.statuses.isEmpty()) {
|
||||
// clear old cached statuses
|
||||
Iterator<Either<Placeholder, Status>> iterator = this.statuses.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
@ -449,7 +448,7 @@ public class TimelineFragment extends SFragment implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean filterIsRelevant(Filter filter) {
|
||||
protected boolean filterIsRelevant(@NonNull Filter filter) {
|
||||
return filterContextMatchesKind(kind, filter.getContext());
|
||||
}
|
||||
|
||||
|
|
|
@ -691,7 +691,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean filterIsRelevant(Filter filter) {
|
||||
protected boolean filterIsRelevant(@NonNull Filter filter) {
|
||||
return filter.getContext().contains(Filter.THREAD);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
// Start/pause/resume video playback as fragment is shown/hidden
|
||||
super.setUserVisibleHint(isVisibleToUser)
|
||||
if (videoPlayer == null) {
|
||||
if (videoView == null) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,10 +58,10 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
if (mediaActivity.isToolbarVisible) {
|
||||
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
|
||||
}
|
||||
videoPlayer.start()
|
||||
videoView.start()
|
||||
} else {
|
||||
handler.removeCallbacks(hideToolbar)
|
||||
videoPlayer.pause()
|
||||
videoView.pause()
|
||||
mediaController.hide()
|
||||
}
|
||||
}
|
||||
|
@ -69,18 +69,31 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun setupMediaView(url: String, previewUrl: String?) {
|
||||
descriptionView = mediaDescription
|
||||
val videoView = videoPlayer
|
||||
|
||||
videoView.transitionName = url
|
||||
videoView.setVideoPath(url)
|
||||
mediaController = MediaController(mediaActivity)
|
||||
mediaController.setMediaPlayer(videoPlayer)
|
||||
videoPlayer.setMediaController(mediaController)
|
||||
mediaController.setMediaPlayer(videoView)
|
||||
videoView.setMediaController(mediaController)
|
||||
videoView.requestFocus()
|
||||
videoView.setOnTouchListener { _, _ ->
|
||||
mediaActivity.onPhotoTap()
|
||||
false
|
||||
}
|
||||
videoView.setOnPreparedListener { mp ->
|
||||
val containerWidth = videoContainer.measuredWidth.toFloat()
|
||||
val containerHeight = videoContainer.measuredHeight.toFloat()
|
||||
val videoWidth = mp.videoWidth.toFloat()
|
||||
val videoHeight = mp.videoHeight.toFloat()
|
||||
|
||||
if(containerWidth/containerHeight > videoWidth/videoHeight) {
|
||||
videoView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
videoView.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
} else {
|
||||
videoView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
|
||||
progressBar.hide()
|
||||
mp.isLooping = true
|
||||
if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
|
||||
|
@ -117,7 +130,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
override fun onToolbarVisibilityChange(visible: Boolean) {
|
||||
if (videoPlayer == null || !userVisibleHint) {
|
||||
if (videoView == null || !userVisibleHint) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -141,8 +141,8 @@ class SendTootService : Service(), Injectable {
|
|||
tootToSend.visibility,
|
||||
tootToSend.sensitive,
|
||||
tootToSend.mediaIds,
|
||||
tootToSend.poll,
|
||||
tootToSend.scheduledAt,
|
||||
tootToSend.poll,
|
||||
tootToSend.quoteId
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* Copyright 2019 kyori19
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
@ -21,7 +36,7 @@ public class VersionUtils {
|
|||
}
|
||||
|
||||
public boolean supportsScheduledToots() {
|
||||
return major >= 2 && minor >= 7 && patch >= 0;
|
||||
return (major == 2) ? ( (minor == 7) ? (patch >= 0) : (minor > 7) ) : (major > 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/* Copyright 2019 kyori19
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view;
|
||||
|
||||
import android.content.Context;
|
||||
|
@ -10,8 +25,10 @@ import android.widget.TextView;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.google.android.material.datepicker.CalendarConstraints;
|
||||
import com.google.android.material.datepicker.DateValidatorPointForward;
|
||||
import com.google.android.material.datepicker.MaterialDatePicker;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.fragment.DatePickerFragment;
|
||||
import com.keylesspalace.tusky.fragment.TimePickerFragment;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
@ -29,8 +46,7 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
private SimpleDateFormat iso8601;
|
||||
|
||||
private Button resetScheduleButton;
|
||||
private TextView scheduledDateView;
|
||||
private TextView scheduledTimeView;
|
||||
private TextView scheduledDateTimeView;
|
||||
|
||||
private Calendar scheduleDateTime;
|
||||
|
||||
|
@ -58,11 +74,9 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
iso8601.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
resetScheduleButton = findViewById(R.id.resetScheduleButton);
|
||||
scheduledDateView = findViewById(R.id.scheduledDate);
|
||||
scheduledTimeView = findViewById(R.id.scheduledTime);
|
||||
scheduledDateTimeView = findViewById(R.id.scheduledDateTime);
|
||||
|
||||
scheduledDateView.setOnClickListener(v -> openPickDateDialog());
|
||||
scheduledTimeView.setOnClickListener(v -> openPickTimeDialog());
|
||||
scheduledDateTimeView.setOnClickListener(v -> openPickDateDialog());
|
||||
|
||||
scheduleDateTime = null;
|
||||
|
||||
|
@ -73,16 +87,16 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
|
||||
private void setScheduledDateTime() {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduledDateView.setText(R.string.hint_configure_scheduled_toot);
|
||||
scheduledTimeView.setText(R.string.hint_configure_scheduled_toot);
|
||||
scheduledDateTimeView.setText(R.string.hint_configure_scheduled_toot);
|
||||
} else {
|
||||
scheduledDateView.setText(dateFormat.format(scheduleDateTime.getTime()));
|
||||
scheduledTimeView.setText(timeFormat.format(scheduleDateTime.getTime()));
|
||||
scheduledDateTimeView.setText(String.format("%s %s",
|
||||
dateFormat.format(scheduleDateTime.getTime()),
|
||||
timeFormat.format(scheduleDateTime.getTime())));
|
||||
}
|
||||
}
|
||||
|
||||
private void setEditIcons() {
|
||||
final int size = scheduledDateView.getLineHeight();
|
||||
final int size = scheduledDateTimeView.getLineHeight();
|
||||
|
||||
Drawable icon = getContext().getDrawable(R.drawable.ic_create_24dp);
|
||||
if (icon == null) {
|
||||
|
@ -91,8 +105,7 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
|
||||
icon.setBounds(0, 0, size, size);
|
||||
|
||||
scheduledDateView.setCompoundDrawables(null, null, icon, null);
|
||||
scheduledTimeView.setCompoundDrawables(null, null, icon, null);
|
||||
scheduledDateTimeView.setCompoundDrawables(null, null, icon, null);
|
||||
}
|
||||
|
||||
public void setResetOnClickListener(OnClickListener listener) {
|
||||
|
@ -105,16 +118,20 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
}
|
||||
|
||||
private void openPickDateDialog() {
|
||||
DatePickerFragment picker = new DatePickerFragment();
|
||||
if (scheduleDateTime != null) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(DatePickerFragment.PICKER_TIME_YEAR, scheduleDateTime.get(Calendar.YEAR));
|
||||
args.putInt(DatePickerFragment.PICKER_TIME_MONTH, scheduleDateTime.get(Calendar.MONTH));
|
||||
args.putInt(DatePickerFragment.PICKER_TIME_DAY, scheduleDateTime.get(Calendar.DAY_OF_MONTH));
|
||||
picker.setArguments(args);
|
||||
long yesterday = Calendar.getInstance().getTimeInMillis() - 24 * 60 * 60 * 1000;
|
||||
CalendarConstraints calendarConstraints = new CalendarConstraints.Builder()
|
||||
.setValidator(new DateValidatorPointForward(yesterday))
|
||||
.build();
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(),
|
||||
"date_picker");
|
||||
MaterialDatePicker<Long> picker = MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection(scheduleDateTime.getTimeInMillis())
|
||||
.setCalendarConstraints(calendarConstraints)
|
||||
.build();
|
||||
picker.addOnPositiveButtonClickListener(this::onDateSet);
|
||||
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "date_picker");
|
||||
}
|
||||
|
||||
private void openPickTimeDialog() {
|
||||
|
@ -125,8 +142,7 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
args.putInt(TimePickerFragment.PICKER_TIME_MINUTE, scheduleDateTime.get(Calendar.MINUTE));
|
||||
picker.setArguments(args);
|
||||
}
|
||||
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(),
|
||||
"time_picker");
|
||||
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "time_picker");
|
||||
}
|
||||
|
||||
public void setDateTime(String scheduledAt) {
|
||||
|
@ -143,12 +159,14 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
setScheduledDateTime();
|
||||
}
|
||||
|
||||
public void onDateSet(int year, int month, int dayOfMonth) {
|
||||
private void onDateSet(long selection) {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
scheduleDateTime.set(year, month, dayOfMonth);
|
||||
setScheduledDateTime();
|
||||
Calendar newDate = Calendar.getInstance(TimeZone.getDefault());
|
||||
newDate.setTimeInMillis(selection);
|
||||
scheduleDateTime.set(newDate.get(Calendar.YEAR), newDate.get(Calendar.MONTH), newDate.get(Calendar.DATE));
|
||||
openPickTimeDialog();
|
||||
}
|
||||
|
||||
public void onTimeSet(int hourOfDay, int minute) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||
</vector>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
android:id="@+id/accountAppBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:elevation="@dimen/actionbar_elevation">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
|
@ -232,7 +233,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textColor="@color/account_tab_font_color"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
|
@ -293,7 +293,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textColor="@color/account_tab_font_color"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
|
@ -327,7 +326,6 @@
|
|||
style="@style/TuskyTabAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:background="?android:colorBackground"
|
||||
app:tabGravity="center"
|
||||
app:tabMode="scrollable"
|
||||
|
|
|
@ -1,46 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/videoContainer"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<VideoView
|
||||
android:id="@+id/videoPlayer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_gravity="center"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mediaDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:background="#60000000"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:padding="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#eee"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Some media description" />
|
||||
|
||||
<VideoView
|
||||
android:id="@+id/videoView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_gravity="center" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -14,33 +14,18 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scheduledDate"
|
||||
android:id="@+id/scheduledDateTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:drawablePadding="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="2020/01/01" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scheduledTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:drawablePadding="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scheduledDate"
|
||||
tools:text="00:00:00" />
|
||||
tools:text="2020/01/01 00:00:00" />
|
||||
|
||||
</merge>
|
|
@ -510,4 +510,14 @@
|
|||
<string name="poll_duration_6_hours">6 hores</string>
|
||||
<string name="edit_poll">Modificar</string>
|
||||
|
||||
</resources>
|
||||
<string name="action_add_poll">Afegeix una enquesta</string>
|
||||
<string name="create_poll_title">Enquesta</string>
|
||||
<string name="poll_duration_5_min">5 minuts</string>
|
||||
<string name="poll_duration_30_min">30 minuts</string>
|
||||
<string name="poll_duration_1_day">1 dia</string>
|
||||
<string name="poll_duration_3_days">3 dies</string>
|
||||
<string name="poll_duration_7_days">7 dies</string>
|
||||
<string name="add_poll_choice">Afegeix una tria</string>
|
||||
<string name="poll_allow_multiple_choices">Múltiples tries</string>
|
||||
<string name="poll_new_choice_hint">Tria %d</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources>
|
||||
<string name="error_generic">خطایی رخ داد</string>
|
||||
<string name="error_generic">خطایی رخ داد.</string>
|
||||
<string name="error_empty">این نمیتواند خالی باشد.</string>
|
||||
<string name="error_invalid_domain">دامنه وارد شده نامعتبر است</string>
|
||||
<string name="error_failed_app_registration">احراز هویت با سرور ناموفق بود</string>
|
||||
<string name="error_no_web_browser_found">مرورگری برای استفاده یافت نشد</string>
|
||||
<string name="error_failed_app_registration">احراز هویت با این نمونه ناموفق بود.</string>
|
||||
<string name="error_no_web_browser_found">مرورگری برای استفاده یافت نشد.</string>
|
||||
<string name="error_authorization_unknown">خطای احراز هویت ناشناس رخ داده است.</string>
|
||||
<string name="error_authorization_denied">هویت احراز نشد</string>
|
||||
<string name="error_retrieving_oauth_token">دریافت توکن ورود ناموفق بود</string>
|
||||
<string name="error_authorization_denied">هویت احراز نشد.</string>
|
||||
<string name="error_retrieving_oauth_token">دریافت توکن ورود ناموفق بود.</string>
|
||||
<string name="error_compose_character_limit">وضعیت خیلی طولانی است!</string>
|
||||
<string name="error_image_upload_size">پرونده باید کمتر از ۸ مگابایت باشد</string>
|
||||
<string name="error_video_upload_size">پرونده ویدئویی باید کمتر از ۴۰ مگابایت باشد</string>
|
||||
<string name="error_media_upload_type">این نوع از پرونده نمیتواند بارگذاری شود</string>
|
||||
<string name="error_media_upload_opening">پرونده باز نشد</string>
|
||||
<string name="error_media_upload_permission">مجوز برای خواندن رسانه نیاز است</string>
|
||||
<string name="error_media_download_permission">اجازه ذخیره رسانه نیاز است</string>
|
||||
<string name="error_image_upload_size">پرونده باید کمتر از ۸ مگابایت باشد.</string>
|
||||
<string name="error_video_upload_size">پروندههای ویدئویی باید کمتر از ۴۰ مگابایت باشد.</string>
|
||||
<string name="error_media_upload_type">این نوع از پرونده نمیتواند بارگذاری شود.</string>
|
||||
<string name="error_media_upload_opening">پرونده باز نشد.</string>
|
||||
<string name="error_media_upload_permission">مجوز برای خواندن رسانه نیاز است.</string>
|
||||
<string name="error_media_download_permission">اجازه ذخیره رسانه نیاز است.</string>
|
||||
<string name="error_media_upload_image_or_video">تصاویر و فیلمها هر دو نمیتوانند به یک وضعیت ضمیمه شوند.</string>
|
||||
<string name="error_media_upload_sending">بارگذاری ناموفق بود</string>
|
||||
<string name="error_sender_account_gone">خطا در ارسال بوق</string>
|
||||
<string name="error_media_upload_sending">بارگذاری ناموفق بود.</string>
|
||||
<string name="error_sender_account_gone">خطا در ارسال بوق.</string>
|
||||
<string name="title_home">خانه</string>
|
||||
<string name="title_notifications">اعلانها</string>
|
||||
<string name="title_public_local">محلی</string>
|
||||
|
@ -25,9 +25,9 @@
|
|||
<string name="title_view_thread">بوق</string>
|
||||
<string name="title_statuses">پستها</string>
|
||||
<string name="title_statuses_with_replies">با پاسخ</string>
|
||||
<string name="title_follows">دنبال شوندهها</string>
|
||||
<string name="title_followers">دنبال کنندهها</string>
|
||||
<string name="title_favourites">علاقهمندیها</string>
|
||||
<string name="title_follows">دنبالشوندهها</string>
|
||||
<string name="title_followers">دنبالکنندهها</string>
|
||||
<string name="title_favourites">پسندها</string>
|
||||
<string name="title_mutes">کاربرهای بیصدا</string>
|
||||
<string name="title_blocks">کاربرهای مسدود شده</string>
|
||||
<string name="title_follow_requests">درخواستهای دنبال کردن</string>
|
||||
|
@ -40,21 +40,21 @@
|
|||
<string name="status_sensitive_media_directions">برای نمایش کلیک کن</string>
|
||||
<string name="status_content_warning_show_more">نمایش بیشتر</string>
|
||||
<string name="status_content_warning_show_less">نمایش کمتر</string>
|
||||
<string name="status_content_show_more">بازکردن</string>
|
||||
<string name="status_content_show_more">گسترش دادن</string>
|
||||
<string name="status_content_show_less">بستن</string>
|
||||
<string name="footer_empty">چیزی اینجا نیست.برای تازه سازی، به پایین بکشید</string>
|
||||
<string name="footer_empty">چیزی اینجا نیست. برای تازهسازی، به پایین بکشید!</string>
|
||||
<string name="notification_reblog_format">%s بوق شما را تقویت کرد</string>
|
||||
<string name="notification_favourite_format">%s بوق شما را پسندید</string>
|
||||
<string name="notification_follow_format">%s شما را دنبال میکند</string>
|
||||
<string name="report_username_format">گزارش @%s</string>
|
||||
<string name="report_comment_hint">پیامهای اضافی</string>
|
||||
<string name="report_comment_hint">پیامهای اضافی؟</string>
|
||||
<string name="action_quick_reply">پاسخ سریع</string>
|
||||
<string name="action_reply">پاسخ</string>
|
||||
<string name="action_reblog">تقویت</string>
|
||||
<string name="action_favourite">مورد علاقه</string>
|
||||
<string name="action_favourite">پسند</string>
|
||||
<string name="action_more">بیشتر</string>
|
||||
<string name="action_compose">ایجاد</string>
|
||||
<string name="action_login">با mastodon وارد شو</string>
|
||||
<string name="action_login">با ماستودون وارد شو</string>
|
||||
<string name="action_logout">خروج</string>
|
||||
<string name="action_logout_confirm">آیا از خارج شدن از این حساب %1s اطمینان دارید؟</string>
|
||||
<string name="action_follow">دنبال کن</string>
|
||||
|
@ -68,11 +68,11 @@
|
|||
<string name="action_send">بوق</string>
|
||||
<string name="action_send_public">بوق!</string>
|
||||
<string name="action_retry">تلاش مجدد</string>
|
||||
<string name="action_close">بستن</string>
|
||||
<string name="action_close">بببند</string>
|
||||
<string name="action_view_profile">نمایه</string>
|
||||
<string name="action_view_preferences">ترجیحات</string>
|
||||
<string name="action_view_account_preferences">ترجیحات حساب</string>
|
||||
<string name="action_view_favourites">علاقهمندیها</string>
|
||||
<string name="action_view_favourites">پسندها</string>
|
||||
<string name="action_view_mutes">کاربران بیصدا</string>
|
||||
<string name="action_view_blocks">کاربران مسدود شده</string>
|
||||
<string name="action_view_follow_requests">درخواستهای دنبال کردن</string>
|
||||
|
@ -89,18 +89,18 @@
|
|||
<string name="action_save">ذخیره</string>
|
||||
<string name="action_edit_profile">ویرایش نمایه</string>
|
||||
<string name="action_edit_own_profile">ویرایش</string>
|
||||
<string name="action_undo">بازگشت</string>
|
||||
<string name="action_undo">بازگرداندن</string>
|
||||
<string name="action_accept">پذیرش</string>
|
||||
<string name="action_reject">رد</string>
|
||||
<string name="action_search">جستجو</string>
|
||||
<string name="action_access_saved_toot">پیشنویس</string>
|
||||
<string name="action_toggle_visibility">نمایش بوق</string>
|
||||
<string name="action_content_warning">هشدار محتوی</string>
|
||||
<string name="action_content_warning">هشدار محتوا</string>
|
||||
<string name="action_emoji_keyboard">صفحه کلیک شکلک</string>
|
||||
<string name="download_image">درحال دریافت %1$s</string>
|
||||
<string name="action_copy_link">کپی لینک</string>
|
||||
<string name="send_status_link_to">همرسانی نشانی بوق با</string>
|
||||
<string name="send_status_content_to">همرسانی بوق با</string>
|
||||
<string name="action_copy_link">رونوشت پیوند</string>
|
||||
<string name="send_status_link_to">همرسانی نشانی بوق با…</string>
|
||||
<string name="send_status_content_to">همرسانی بوق با…</string>
|
||||
<string name="send_media_to">همرسانی رسانه با…</string>
|
||||
<string name="confirmation_reported">فرستاده شد!</string>
|
||||
<string name="confirmation_unblocked">کاربر رفع انسداد شد</string>
|
||||
|
@ -109,23 +109,29 @@
|
|||
<string name="status_sent_long">پاسخ با موفق فرستاده شد.</string>
|
||||
<string name="hint_domain">کدام نمونه؟</string>
|
||||
<string name="hint_compose">چه خبر؟</string>
|
||||
<string name="hint_content_warning">هشدار محتوی</string>
|
||||
<string name="hint_content_warning">هشدار محتوا</string>
|
||||
<string name="hint_display_name">نام نمایشی</string>
|
||||
<string name="hint_note">بیوگرافی</string>
|
||||
<string name="hint_search">جستجو</string>
|
||||
<string name="hint_search">جستجو…</string>
|
||||
<string name="search_no_results">نتیجهای نیست</string>
|
||||
<string name="label_quick_reply">پاسخ …</string>
|
||||
<string name="label_avatar">آواتار</string>
|
||||
<string name="label_header">سرتیتر</string>
|
||||
<string name="label_header">سرایند</string>
|
||||
<string name="link_whats_an_instance">یک نمونه چیست؟</string>
|
||||
<string name="login_connection">در حال اتصال …</string>
|
||||
<string name="dialog_whats_an_instance">آدرس یا دامنه هر نمونه را میتوانید وارد کنید، مثلا mastodon.social, icosahedron.website, social.tchncs.de, و <a href=\"https://instances.social\" >بیشتر!\n\n اگر شما هنوز حساب کاربری ندارید، میتوانید نام نمونه مورد نظر را وارد کنید از اینجا بپیوندید و حساب کاربری ایجاد کنید\n\n نمونه جایی است که حساب کاربری شما میزبان آن است اما شما به راحتی میتوانید با افراد دیگر در نمونههای دیگر ارتباط برقرار کنید و آنها را دنبال کنید شما درست مثل اینکه در یکجا باشید.\n\n برای اطلاعات بیشتر به اینجا مراجعه کنید <a href="https://joinmastodon.org">joinmastodon.org</a>.</string>
|
||||
<string name="dialog_whats_an_instance">آدرس یا دامنه هر نمونه را میتوانید وارد کنید، مثلا mastodon.social، icosahedron.website، social.tchncs.de، و <a href="https://instances.social">بیشتر!</a>
|
||||
\n
|
||||
\n اگر شما هنوز حساب کاربری ندارید، میتوانید نام نمونه مورد نظر را وارد کنید از اینجا بپیوندید و حساب کاربری ایجاد کنید.
|
||||
\n
|
||||
\n نمونه جایی است که حساب کاربری شما میزبان آن است اما شما به راحتی میتوانید با افراد دیگر در نمونههای دیگر ارتباط برقرار کنید و آنها را دنبال کنید شما درست مثل اینکه در یکجا باشید.
|
||||
\n
|
||||
\n برای اطلاعات بیشتر به اینجا مراجعه کنید <a href="https://joinmastodon.org">joinmastodon.org</a>. </string>
|
||||
<string name="dialog_title_finishing_media_upload">پایان بارگذاری رسانه</string>
|
||||
<string name="dialog_message_uploading_media">در حال بارگذاری</string>
|
||||
<string name="dialog_message_uploading_media">در حال بارگذاری…</string>
|
||||
<string name="dialog_download_image">بارگیری</string>
|
||||
<string name="dialog_message_cancel_follow_request">درخواست دنبال کردن را لغو میکنید؟</string>
|
||||
<string name="dialog_unfollow_warning">لغو دنبال کردن این حساب</string>
|
||||
<string name="dialog_delete_toot_warning">حذف این بوق</string>
|
||||
<string name="dialog_unfollow_warning">لغو دنبال کردن این حساب؟</string>
|
||||
<string name="dialog_delete_toot_warning">حذف این بوق؟</string>
|
||||
<string name="visibility_public">عمومی:پست به خط زمانی عمومی</string>
|
||||
<string name="visibility_unlisted">خارج از لیست: در خط زمانی عمومی نشان نده</string>
|
||||
<string name="visibility_private">تنها دنبالکنندگان:پست فقط به دنبالکنندگان</string>
|
||||
|
@ -140,13 +146,13 @@
|
|||
<string name="pref_title_notification_filter_mentions">صدازدهها</string>
|
||||
<string name="pref_title_notification_filter_follows">دنبالشده</string>
|
||||
<string name="pref_title_notification_filter_reblogs">پستهای تقویت شده من</string>
|
||||
<string name="pref_title_notification_filter_favourites">پستهای مورد علاقه من</string>
|
||||
<string name="pref_title_notification_filter_favourites">پستهای پسندیده شدهٔ من</string>
|
||||
<string name="pref_title_appearance_settings">ظاهر</string>
|
||||
<string name="pref_title_app_theme">تم برنامه</string>
|
||||
<string name="pref_title_timelines">خطهای زمانی</string>
|
||||
<string name="app_them_dark">روشن</string>
|
||||
<string name="app_theme_light">سیاه</string>
|
||||
<string name="app_theme_black">تیره</string>
|
||||
<string name="app_them_dark">تاریک</string>
|
||||
<string name="app_theme_light">روشن</string>
|
||||
<string name="app_theme_black">سیاه</string>
|
||||
<string name="app_theme_auto">خودکار برحسب غروب خورشید</string>
|
||||
<string name="pref_title_browser_settings">مرورگر</string>
|
||||
<string name="pref_title_custom_tabs">استفاده از زبانههای سفارشی کروم</string>
|
||||
|
@ -163,25 +169,25 @@
|
|||
<string name="pref_title_http_proxy_port">درگاه پراکسی HTTP</string>
|
||||
<string name="pref_default_post_privacy">حریم خصوصی پیشفرض پست</string>
|
||||
<string name="pref_default_media_sensitivity">همواره رسانه را به عنوان حساس نشانهگذاری کن</string>
|
||||
<string name="pref_publishing">انتشار</string>
|
||||
<string name="pref_publishing">انتشار (همگادم با کارساز)</string>
|
||||
<string name="pref_failed_to_sync">ناتوانی در همگامسازی تنظیمات</string>
|
||||
<string name="post_privacy_public">عمومی</string>
|
||||
<string name="post_privacy_unlisted">لیستنشده</string>
|
||||
<string name="post_privacy_unlisted">فهرست نشده</string>
|
||||
<string name="post_privacy_followers_only">فقط دنبالکنندگان</string>
|
||||
<string name="pref_status_text_size">اندازه متن وضعیت</string>
|
||||
<string name="status_text_size_smallest">کوچکترین</string>
|
||||
<string name="status_text_size_smallest">بسیار کوچک</string>
|
||||
<string name="status_text_size_small">کوچک</string>
|
||||
<string name="status_text_size_medium">متوسط</string>
|
||||
<string name="status_text_size_large">بزرگ</string>
|
||||
<string name="status_text_size_largest">بزرگترین</string>
|
||||
<string name="notification_mention_name">صدا زدن جدید</string>
|
||||
<string name="notification_mention_descriptions">هشدار در مورد صدازدن جدید</string>
|
||||
<string name="status_text_size_largest">بسیار بزرگ</string>
|
||||
<string name="notification_mention_name">اشارههای جدید</string>
|
||||
<string name="notification_mention_descriptions">اعلان در مورد اشارههای جدید</string>
|
||||
<string name="notification_follow_name">دنبالکنندههای جدید</string>
|
||||
<string name="notification_follow_description">هشدارها درمورد دنبالکنندگان جدید</string>
|
||||
<string name="notification_boost_name">تقویت</string>
|
||||
<string name="notification_boost_description">وقتی بوقهای شما تقویت شد هشدار بده</string>
|
||||
<string name="notification_favourite_name">مورد علاقهها</string>
|
||||
<string name="notification_favourite_description">هشدارها، زمانی که بوقهای شما مورد علاقه قرار گرفت</string>
|
||||
<string name="notification_follow_description">اعلان درمورد دنبالکنندگان جدید</string>
|
||||
<string name="notification_boost_name">تقویتها</string>
|
||||
<string name="notification_boost_description">وقتی بوقهای شما تقویت شد اعلان بده</string>
|
||||
<string name="notification_favourite_name">پسندها</string>
|
||||
<string name="notification_favourite_description">اعلانها وقتی که بوقهای شما پسندیده شوند</string>
|
||||
<string name="notification_mention_format">%s شما را صدا زد</string>
|
||||
<string name="notification_summary_large">%1$s, %2$s, %3$s و %4$d دیگر</string>
|
||||
<string name="notification_summary_medium">%1$s, %2$s, و %3$s</string>
|
||||
|
@ -212,20 +218,20 @@
|
|||
<string name="state_follow_requested">درخواست دنبالکردن فرستاده شد</string>
|
||||
<string name="no_content">بدون محتوی</string>
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">در %dy</string>
|
||||
<string name="abbreviated_in_days">در %dd</string>
|
||||
<string name="abbreviated_in_hours">در % dh</string>
|
||||
<string name="abbreviated_in_minutes">در %dm</string>
|
||||
<string name="abbreviated_in_seconds">در %ds</string>
|
||||
<string name="abbreviated_in_years">در %dسال</string>
|
||||
<string name="abbreviated_in_days">در %dر</string>
|
||||
<string name="abbreviated_in_hours">در % dس</string>
|
||||
<string name="abbreviated_in_minutes">در %dد</string>
|
||||
<string name="abbreviated_in_seconds">در %dث</string>
|
||||
<string name="follows_you">شما را دنبال میکند</string>
|
||||
<string name="pref_title_alway_show_sensitive_media">همیشه مطالب حساس را نشان بده</string>
|
||||
<string name="pref_title_alway_show_sensitive_media">همیشه مطلب حساس را نشان بده</string>
|
||||
<string name="title_media">رسانه</string>
|
||||
<string name="replying_to">پاسخ دادن به @%s</string>
|
||||
<string name="load_more_placeholder_text">بارگیری بیشتر</string>
|
||||
<string name="add_account_name">افزودن حساب</string>
|
||||
<string name="add_account_description">افزودن حساب ماستدون جدید</string>
|
||||
<string name="action_lists">لیستها</string>
|
||||
<string name="title_lists">لیستها</string>
|
||||
<string name="action_lists">سیاههها</string>
|
||||
<string name="title_lists">سیاههها</string>
|
||||
<string name="title_list_timeline">لیست خط زمانی</string>
|
||||
<string name="compose_active_account_description">پست با حساب %1$s</string>
|
||||
<string name="error_failed_set_caption">ناتوان در تنظیم عنوان</string>
|
||||
|
@ -234,8 +240,8 @@
|
|||
<string name="action_remove">حذف</string>
|
||||
<string name="lock_account_label">قفل حساب</string>
|
||||
<string name="lock_account_label_description">به شما امکان میدهد بصورت دستی دنبالکنندگان را تایید کنید</string>
|
||||
<string name="compose_save_draft">ذخیره به عنوان پیشنویس</string>
|
||||
<string name="send_toot_notification_title">فرستادن بوق</string>
|
||||
<string name="compose_save_draft">ذخیره به عنوان پیشنویس؟</string>
|
||||
<string name="send_toot_notification_title">فرستادن بوق…</string>
|
||||
<string name="send_toot_notification_error_title">خطای فرستادن بوق</string>
|
||||
<string name="send_toot_notification_channel_name">در حال فرستادن بوقها</string>
|
||||
<string name="send_toot_notification_cancel_title">فرستادن لغو شد</string>
|
||||
|
@ -246,8 +252,8 @@
|
|||
<string name="emoji_style">قالب شکلک</string>
|
||||
<string name="system_default">پیشفرض سیستم</string>
|
||||
<string name="download_fonts">شما ابتدا باید این شکلکها را دریافت کنید</string>
|
||||
<string name="performing_lookup_title">اجرای جستجو</string>
|
||||
<string name="expand_collapse_all_statuses">باز/بستن همه وضعیتها</string>
|
||||
<string name="performing_lookup_title">اجرای جستجو…</string>
|
||||
<string name="expand_collapse_all_statuses">گستردن/بستن همه وضعیتها</string>
|
||||
<string name="action_open_toot">بوق را باز کن</string>
|
||||
<string name="restart_required">برنامه به شروع مجدد نیاز دارد</string>
|
||||
<string name="restart_emoji">شما برای اعمال این تغییرات به شروع مجدد برنامه نیاز دارید</string>
|
||||
|
@ -271,4 +277,186 @@
|
|||
<string name="label_remote_account">اطلاعات زیر ممکن است به طور ناقص نمایه کاربر را نشان دهد. برای دیدن نمایه کامل در مرورگر، لمس کنید.</string>
|
||||
<string name="unpin_action">برداشتن سنجاق</string>
|
||||
<string name="pin_action">سنجاق کردن</string>
|
||||
<string name="error_network">یک خطای شبکه رخ داد! لطفا اتصال خود را بررسی و دوباره تلاش کنید!</string>
|
||||
<string name="title_direct_messages">پیامهای مستقیم</string>
|
||||
<string name="title_tab_preferences">زبانهها</string>
|
||||
<string name="title_tag">#%ث</string>
|
||||
<string name="title_statuses_pinned">سنجاقشدهها</string>
|
||||
<string name="title_domain_mutes">دامنههای پنهان</string>
|
||||
<string name="status_username_format">\@%s</string>
|
||||
<string name="message_empty">چیزی اینجا نیست.</string>
|
||||
<string name="action_unreblog">حذف بازبوق</string>
|
||||
<string name="action_unfavourite">حذف پسند</string>
|
||||
<string name="action_delete_and_redraft">پاک کردن و بازنویسی</string>
|
||||
<string name="action_view_domain_mutes">دامنههای پنهان</string>
|
||||
<string name="action_add_poll">افزودن نظرسنجی</string>
|
||||
<string name="action_mute_domain">بیصدا کردن %s</string>
|
||||
<string name="action_add_tab">افزوده زبانه</string>
|
||||
<string name="action_links">پیوندها</string>
|
||||
<string name="action_mentions">اشارهها</string>
|
||||
<string name="action_hashtags">هشتگها</string>
|
||||
<string name="action_open_reblogger">بازکردن حساب بازبوق کننده</string>
|
||||
<string name="action_open_reblogged_by">نمایش بازبوقها</string>
|
||||
<string name="action_open_faved_by">نمایش پسندیدهها</string>
|
||||
|
||||
<string name="title_hashtags_dialog">هشتگها</string>
|
||||
<string name="title_mentions_dialog">اشارهها</string>
|
||||
<string name="title_links_dialog">پیوندها</string>
|
||||
<string name="action_open_media_n">بازکردن رسانه #%d</string>
|
||||
|
||||
<string name="action_open_as">باز کردن به عنوان %s</string>
|
||||
<string name="action_share_as">همرسانی به عنوان …</string>
|
||||
<string name="download_media">بارگیری رسانه</string>
|
||||
<string name="downloading_media">در حال بارگیری رسانه</string>
|
||||
|
||||
<string name="confirmation_domain_unmuted">%s ناپنهان</string>
|
||||
|
||||
<string name="dialog_redraft_toot_warning">میخواهید این بوق را پاک و بازنویسی کنید؟</string>
|
||||
<string name="mute_domain_warning_dialog_ok">پنهان کردن تمام دامنه</string>
|
||||
|
||||
<string name="pref_title_notification_filter_poll">نظرسنجیهای پایان یافته</string>
|
||||
<string name="pref_title_timeline_filters">صافیها</string>
|
||||
|
||||
<string name="app_theme_system">استفاده از طرح سیستم</string>
|
||||
|
||||
<string name="pref_title_language">زبان</string>
|
||||
<string name="pref_title_bot_overlay">نمایش نشان برای باتها</string>
|
||||
<string name="pref_title_animate_gif_avatars">پویانمایی آواتار gif</string>
|
||||
|
||||
<string name="notification_poll_name">نظرسنجیها</string>
|
||||
<string name="notification_poll_description">اعلانها درباره نظرسنجیهایی که پایان یافتهاند</string>
|
||||
|
||||
|
||||
<string name="about_tusky_version">Tusky (تاسکی) %s</string>
|
||||
<string name="abbreviated_years_ago">%dسال</string>
|
||||
<string name="abbreviated_days_ago">%dر</string>
|
||||
<string name="abbreviated_hours_ago">%dس</string>
|
||||
<string name="abbreviated_minutes_ago">%dد</string>
|
||||
<string name="abbreviated_seconds_ago">%dث</string>
|
||||
|
||||
<string name="pref_title_alway_open_spoiler">همواره بوقهایی که دارای محتوای حساس هستند را گسترش بده</string>
|
||||
<string name="pref_title_public_filter_keywords">خط زمانی عمومی</string>
|
||||
<string name="pref_title_thread_filter_keywords">گفتگوها</string>
|
||||
<string name="filter_addition_dialog_title">افزودن صافی</string>
|
||||
<string name="filter_edit_dialog_title">ویرایش صافی</string>
|
||||
<string name="filter_dialog_remove_button">پاک کردن</string>
|
||||
<string name="filter_dialog_update_button">بهروزرسانی</string>
|
||||
<string name="filter_dialog_whole_word">تمام کلمه</string>
|
||||
<string name="error_create_list">ناتوانی در ایجاد سیاهه</string>
|
||||
<string name="error_rename_list">ناتوانی در تغییر نام سیاهه</string>
|
||||
<string name="error_delete_list">ناتوانی در حذف سیاهه</string>
|
||||
<string name="action_create_list">ایجاد یک سیاهه</string>
|
||||
<string name="action_rename_list">تغییر نام سیاهه</string>
|
||||
<string name="action_delete_list">حذف سیاهه</string>
|
||||
<string name="action_edit_list">ویرایش سیاهه</string>
|
||||
<string name="hint_search_people_list">جستجو بین افرادی که دنبال میکنید</string>
|
||||
<string name="action_add_to_list">افزودن حساب به سیاهه</string>
|
||||
<string name="action_remove_from_list">حذف حساب از سیاهه</string>
|
||||
|
||||
<string name="caption_notoemoji">مجموعه شکلکهای جاری گوگل</string>
|
||||
|
||||
<string name="license_cc_by_4">CC-BY 4.0</string>
|
||||
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
|
||||
|
||||
<plurals name="favs">
|
||||
<item quantity="one"><b>%1$s</b> پسند</item>
|
||||
<item quantity="other"><b>%1$s</b> پسند</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="reblogs">
|
||||
<item quantity="one"><b>%s</b> بازبوق</item>
|
||||
<item quantity="other"><b>%s</b> بازبوق</item>
|
||||
</plurals>
|
||||
|
||||
<string name="title_reblogged_by">بازبوق شده توسط</string>
|
||||
<string name="title_favourited_by">پسنندهشده توسط</string>
|
||||
|
||||
<string name="conversation_1_recipients">%1$s</string>
|
||||
<string name="conversation_2_recipients">%1$s و %2$s</string>
|
||||
<string name="conversation_more_recipients">%1$s، %2$s و %3$d بیشتر</string>
|
||||
<string name="max_tab_number_reached">به بیشینه %1$d زبانه رسید</string>
|
||||
|
||||
|
||||
<string name="description_status_media">رسانه: %s</string>
|
||||
<string name="description_status_cw">هشدار محتوا: %s</string>
|
||||
<string name="description_status_media_no_description_placeholder">بدون توضیحات</string>
|
||||
<string name="description_status_reblogged">بازبوقیده</string>
|
||||
<string name="description_status_favourited">پسندیده</string>
|
||||
<string name="description_visiblity_public">عمومی</string>
|
||||
<string name="description_visiblity_unlisted">فهرستنشده</string>
|
||||
<string name="description_visiblity_private">دنبالکنندگان</string>
|
||||
<string name="description_visiblity_direct">مستقیم</string>
|
||||
<string name="description_poll">نظرسنجی با انتخابها: %1$s، %2$s، %3$s، %4$s؛ %5$s</string>
|
||||
|
||||
<string name="hint_list_name">نام سیاهه</string>
|
||||
|
||||
<string name="edit_hashtag_title">ویرایش هشتگ</string>
|
||||
<string name="edit_hashtag_hint">هشتگ بدون #</string>
|
||||
<string name="hashtag">هشتگ</string>
|
||||
<string name="notifications_clear">پاک کردن</string>
|
||||
<string name="notifications_apply_filter">صافی</string>
|
||||
<string name="filter_apply">اعمال</string>
|
||||
|
||||
<string name="compose_shortcut_long_label">ایجاد بوق</string>
|
||||
<string name="compose_shortcut_short_label">ایجاد</string>
|
||||
|
||||
<string name="notification_clear_text">مطمئنید که به طور دائمی میخواهید اعلانها را پاک کنید؟</string>
|
||||
<string name="compose_preview_image_description">کنشها برای تصاویر %s</string>
|
||||
|
||||
<plurals name="poll_info_votes">
|
||||
<item quantity="one">%s رای</item>
|
||||
<item quantity="other">%s رای</item>
|
||||
</plurals>
|
||||
<string name="poll_info_time_relative">%s باقی مانده</string>
|
||||
<string name="poll_info_time_absolute">پایان یافته در %s</string>
|
||||
<string name="poll_info_closed">بسته شده</string>
|
||||
<string name="poll_vote">رای</string>
|
||||
|
||||
<string name="poll_ended_voted">یک نظرسنجی که در آن رای دادهاید پایان یافته است</string>
|
||||
<string name="poll_ended_created">یک نظرسنجی ساختهاید پایان یافته است</string>
|
||||
|
||||
<plurals name="poll_timespan_days">
|
||||
<item quantity="one">%d روز</item>
|
||||
<item quantity="other">%d روز</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_hours">
|
||||
<item quantity="one">%d ساعت</item>
|
||||
<item quantity="other">%d ساعت</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_minutes">
|
||||
<item quantity="one">%d دقیقه</item>
|
||||
<item quantity="other">%d دقیقه</item>
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_seconds">
|
||||
<item quantity="one">%d ثانیه</item>
|
||||
<item quantity="other">%d ثانیه</item>
|
||||
</plurals>
|
||||
|
||||
<string name="button_continue">ادامه</string>
|
||||
<string name="button_back">قبل</string>
|
||||
<string name="button_done">اتمام</string>
|
||||
<string name="report_sent_success">با موفقیت گزارش شد @%s</string>
|
||||
<string name="hint_additional_info">نظرات بیشتر</string>
|
||||
<string name="report_remote_instance">هدایت به %s</string>
|
||||
<string name="failed_report">ناتوانی در گزارش</string>
|
||||
<string name="failed_fetch_statuses">ناتوانی در دریافت وضعیتها</string>
|
||||
<string name="title_accounts">حسابها</string>
|
||||
<string name="failed_search">ناتوانی در جستجو</string>
|
||||
|
||||
<string name="pref_title_show_notifications_filter">نمایش فیلتر اعلانات</string>
|
||||
|
||||
|
||||
<string name="create_poll_title">نظرسنجی</string>
|
||||
<string name="poll_duration_5_min">۵ دقیقه</string>
|
||||
<string name="poll_duration_30_min">۳۰ دقیقه</string>
|
||||
<string name="poll_duration_1_hour">۱ ساعت</string>
|
||||
<string name="poll_duration_6_hours">۶ ساعت</string>
|
||||
<string name="poll_duration_1_day">۱ روز</string>
|
||||
<string name="poll_duration_3_days">۳ روز</string>
|
||||
<string name="poll_duration_7_days">۷ روز</string>
|
||||
<string name="add_poll_choice">افزودن گزینه</string>
|
||||
<string name="poll_allow_multiple_choices">گزینههای چندگانه</string>
|
||||
<string name="poll_new_choice_hint">گزینه %d</string>
|
||||
<string name="edit_poll">ویرایش</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<string name="status_content_show_less">Összecsukás</string>
|
||||
<string name="message_empty">Nincs itt semmi.</string>
|
||||
<string name="footer_empty">Üres tartalom. Húzd le a frissítéshez!</string>
|
||||
<string name="notification_reblog_format">%s megolta a tülködet</string>
|
||||
<string name="notification_reblog_format">%s megtolta a tülködet</string>
|
||||
<string name="notification_favourite_format">%s kedvencnek jelölte tülködet</string>
|
||||
<string name="notification_follow_format">%s követ</string>
|
||||
<string name="report_username_format">\@%s bejelentése</string>
|
||||
|
@ -453,4 +453,18 @@
|
|||
<string name="title_accounts">Fiókok</string>
|
||||
<string name="failed_search">Sikertelen keresés</string>
|
||||
|
||||
</resources>
|
||||
<string name="action_add_poll">Szavazás hozzáadása</string>
|
||||
<string name="create_poll_title">Szavazás</string>
|
||||
<string name="poll_duration_5_min">5 perc</string>
|
||||
<string name="poll_duration_30_min">30 perc</string>
|
||||
<string name="poll_duration_1_hour">1 óra</string>
|
||||
<string name="poll_duration_6_hours">6 óra</string>
|
||||
<string name="poll_duration_1_day">1 nap</string>
|
||||
<string name="poll_duration_3_days">3 nap</string>
|
||||
<string name="poll_duration_7_days">7 nap</string>
|
||||
<string name="add_poll_choice">Válasz hozzáadása</string>
|
||||
<string name="poll_allow_multiple_choices">Több lehetőség</string>
|
||||
<string name="poll_new_choice_hint">Válasz %d</string>
|
||||
<string name="edit_poll">Szerkesztés</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -125,6 +125,8 @@
|
|||
<string name="action_toggle_visibility">Toot visibility</string>
|
||||
<string name="action_content_warning">Content warning</string>
|
||||
<string name="action_emoji_keyboard">Emoji keyboard</string>
|
||||
<string name="action_schedule_toot">Schedule Toot</string>
|
||||
<string name="action_reset_schedule">Reset</string>
|
||||
<string name="action_add_tab">Add Tab</string>
|
||||
<string name="action_links">Links</string>
|
||||
<string name="action_mentions">Mentions</string>
|
||||
|
@ -134,8 +136,6 @@
|
|||
<string name="action_open_faved_by">Show favorites</string>
|
||||
<string name="action_quote">Quote</string>
|
||||
<string name="action_authorize">Authorize Now!</string>
|
||||
<string name="action_schedule_toot">Schedule Toot</string>
|
||||
<string name="action_reset_schedule">Reset</string>
|
||||
|
||||
<string name="title_hashtags_dialog">Hashtags</string>
|
||||
<string name="title_mentions_dialog">Mentions</string>
|
||||
|
@ -164,6 +164,7 @@
|
|||
|
||||
<string name="hint_domain">Which instance?</string>
|
||||
<string name="hint_compose">What\'s happening?</string>
|
||||
<string name="hint_configure_scheduled_toot">Tap here to configure scheduled toot.</string>
|
||||
<string name="hint_content_warning">Content warning</string>
|
||||
<string name="hint_display_name">Display name</string>
|
||||
<string name="hint_note">Bio</string>
|
||||
|
@ -171,7 +172,6 @@
|
|||
<string name="hint_toot_area">Quick Toot Area</string>
|
||||
<string name="hint_default_text">Default Hashtag</string>
|
||||
<string name="hint_access_token">Access Token</string>
|
||||
<string name="hint_configure_scheduled_toot">Tap here to configure scheduled toot.</string>
|
||||
|
||||
<string name="search_no_results">No results</string>
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ class BottomSheetActivityTest {
|
|||
mastodonApi = api
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
bottomSheet = mock(BottomSheetBehavior::class.java) as BottomSheetBehavior<LinearLayout>
|
||||
callList = arrayListOf()
|
||||
}
|
||||
|
||||
override fun openLink(url: String) {
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
package com.keylesspalace.tusky
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.SpannedString
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.entity.Poll
|
||||
import com.keylesspalace.tusky.entity.PollOption
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import okhttp3.Request
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.annotation.Config
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
|
||||
@Config(application = FakeTuskyApplication::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FilterTest {
|
||||
|
||||
private val fragment = FakeFragment()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
|
||||
val controller = Robolectric.buildActivity(FakeActivity::class.java)
|
||||
val activity = controller.get()
|
||||
|
||||
activity.accountManager = mock()
|
||||
activity.themeUtils = mock()
|
||||
val apiMock = Mockito.mock(MastodonApi::class.java)
|
||||
Mockito.`when`(apiMock.getFilters()).thenReturn(object: Call<List<Filter>> {
|
||||
override fun isExecuted(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun clone(): Call<List<Filter>> {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun isCanceled(): Boolean {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun cancel() {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun execute(): Response<List<Filter>> {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun request(): Request {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
|
||||
override fun enqueue(callback: Callback<List<Filter>>) {
|
||||
callback.onResponse(
|
||||
this,
|
||||
Response.success(
|
||||
listOf(
|
||||
Filter(
|
||||
id = "123",
|
||||
phrase = "badWord",
|
||||
context = listOf(Filter.HOME),
|
||||
expiresAt = null,
|
||||
irreversible = false,
|
||||
wholeWord = false
|
||||
),
|
||||
Filter(
|
||||
id = "123",
|
||||
phrase = "badWholeWord",
|
||||
context = listOf(Filter.HOME, Filter.PUBLIC),
|
||||
expiresAt = null,
|
||||
irreversible = false,
|
||||
wholeWord = true
|
||||
),
|
||||
Filter(
|
||||
id = "123",
|
||||
phrase = "wrongContext",
|
||||
context = listOf(Filter.PUBLIC),
|
||||
expiresAt = null,
|
||||
irreversible = false,
|
||||
wholeWord = true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
activity.mastodonApi = apiMock
|
||||
|
||||
|
||||
controller.create().start()
|
||||
|
||||
fragment.mastodonApi = apiMock
|
||||
|
||||
|
||||
activity.supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.activity_main, fragment, "fragment")
|
||||
.commit()
|
||||
|
||||
fragment.reloadFilters(false)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldNotFilter() {
|
||||
assertFalse(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "should not be filtered")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldNotFilter_whenContextDoesNotMatch() {
|
||||
assertFalse(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "one two wrongContext three")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilter_whenContentMatchesBadWord() {
|
||||
assertTrue(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "one two badWord three")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilter_whenContentMatchesBadWordPart() {
|
||||
assertTrue(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "one two badWordPart three")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilter_whenContentMatchesBadWholeWord() {
|
||||
assertTrue(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "one two badWholeWord three")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldNotFilter_whenContentDoesNotMAtchWholeWord() {
|
||||
assertFalse(fragment.shouldFilterStatus(
|
||||
mockStatus(content = "one two badWholeWordTest three")
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilter_whenSpoilerTextDoesMatch() {
|
||||
assertTrue(fragment.shouldFilterStatus(
|
||||
mockStatus(
|
||||
content = "should not be filtered",
|
||||
spoilerText = "badWord should be filtered"
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFilter_whenPollTextDoesMatch() {
|
||||
assertTrue(fragment.shouldFilterStatus(
|
||||
mockStatus(
|
||||
content = "should not be filtered",
|
||||
spoilerText = "should not be filtered",
|
||||
pollOptions = listOf("should not be filtered", "badWord")
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
private fun mockStatus(
|
||||
content: String = "",
|
||||
spoilerText: String = "",
|
||||
pollOptions: List<String>? = null
|
||||
): Status {
|
||||
return Status(
|
||||
id = "123",
|
||||
url = "https://mastodon.social/@Tusky/100571663297225812",
|
||||
account = mock(),
|
||||
inReplyToId = null,
|
||||
inReplyToAccountId = null,
|
||||
reblog = null,
|
||||
content = SpannedString(content),
|
||||
createdAt = Date(),
|
||||
emojis = emptyList(),
|
||||
reblogsCount = 0,
|
||||
favouritesCount = 0,
|
||||
reblogged = false,
|
||||
favourited = false,
|
||||
sensitive = false,
|
||||
spoilerText = spoilerText,
|
||||
visibility = Status.Visibility.PUBLIC,
|
||||
attachments = arrayListOf(),
|
||||
mentions = emptyArray(),
|
||||
application = null,
|
||||
pinned = false,
|
||||
poll = if (pollOptions != null) {
|
||||
Poll(
|
||||
id = "1234",
|
||||
expiresAt = null,
|
||||
expired = false,
|
||||
multiple = false,
|
||||
votesCount = 0,
|
||||
options = pollOptions.map {
|
||||
PollOption(it, 0)
|
||||
},
|
||||
voted = false
|
||||
)
|
||||
} else null,
|
||||
card = null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FakeActivity: BottomSheetActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
|
||||
class FakeFragment: SFragment() {
|
||||
override fun removeItem(position: Int) {
|
||||
}
|
||||
|
||||
override fun onReblog(reblog: Boolean, position: Int) {
|
||||
}
|
||||
|
||||
override fun filterIsRelevant(filter: Filter): Boolean {
|
||||
return filter.context.contains(Filter.HOME)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
Tusky v9.1
|
||||
|
||||
This release ensures compatibility with Mastodon 3 and improves performance and stability.
|
|
@ -0,0 +1,11 @@
|
|||
تاسکی نسخه ۶٫۰
|
||||
|
||||
- صافیهای خطزمانی به ترجیحات حساب منتقل شده و با کارساز همگام خواهد شد
|
||||
- اکنون میتوانید هشتگ سفارشی را به عنوان یک زبانه در رابط اصلی داشته باشید
|
||||
- سیاههها میتوانند ویرایش شوند
|
||||
- امنیتی: حذف پشتیبانی برای TLS 1.0 و TLS 1.1، و افزودن پیشتیبانی برای TLS 1.3 روی اندروید ۶ به بالا
|
||||
- نمای ایجاد با شروع نگارش، شکلکهای سفارشی پیشنهاد میدهد
|
||||
- سبک جدید «پیروی از سبک سیستم» در تنظیمات
|
||||
- بهبود دسترسپذییری خطزمانی
|
||||
- تاسکی اکنون اعلانهای ناشناس را نادیده میگید و دیگر فرونمیپاشد
|
||||
-
|
|
@ -0,0 +1,7 @@
|
|||
تاسکی نسخه ۷٫۰
|
||||
|
||||
- پشتیبانی از نمایش نظرسنجیها، رایها و اعلانهای مربوط به نظرسنجی
|
||||
- دکمه جدید برای از صافی گذراندن زبانه اعلان و برای پاک کردن همه اعلانها
|
||||
- امکان پاک کردن و بازنویسی بوقتان
|
||||
- علامت جدید روی تصویر نمایه که نشان میدهد یک حساب، بات است یا نه (قابل غیرفعالسازی در ترجیحات)
|
||||
- ترجمههای جدید: زبانهای نروژی و اسلونیایی.
|
|
@ -0,0 +1,9 @@
|
|||
تاسکی نسخه ۹٫۰
|
||||
|
||||
- میتوانید داخل تاسکی، نظرسنجی ایجاد کنید
|
||||
- بهبود جستجو
|
||||
- گزینه جدید در ترجیحات حساب برای اینکه همواره بوقهای دارای محتوای حساس کامل نمایش داده شوند
|
||||
- آواتارها در کشوی ناوبری اکنون به صورت مربع دارای لبههای گرد دیده میشوند
|
||||
- اکنون امکان گزارش کاربرانی که هیچ بوقی ندارند فراهم شده است
|
||||
- تاسکی اکنون از اتصال غیر ایمن در اندروید ۶ سربازمیزند
|
||||
- به همراه بسیاری بهبودها و رفع اشکالات
|
|
@ -0,0 +1,3 @@
|
|||
تاسکی نسخه ۹٫۱
|
||||
|
||||
این نسخه، سازگاری با ماستودون ۳ را تامین کرده و دارای بهبود عملکرد و پایداری است.
|
|
@ -0,0 +1,12 @@
|
|||
تاسکی یک کارخواه سبک برای ماستودون (کارساز شبکه احتماعی آزاد و متنباز) است.
|
||||
|
||||
• دارای طراحی متریال
|
||||
• اکثر APIهای ماستودون پیادهسازی شده است
|
||||
• پشتیبانی از چند حساب
|
||||
• پشتیبانی از سبک تیره و روشن با امکان تنظیم تغییر خودکار بر اساس ساعت روز
|
||||
• پشتیبانی از پیشنویس - امکان نوشتن بوقها و ذخیرهشان برای بعد
|
||||
• امکان انتخاب از بین انواع شکلک
|
||||
• بهینهسازیشده برای صفحههایی با اندازههای مختلف
|
||||
• کاملا آزاد و متنباز - بدون وابستگیهای غیرآزاد به خدمات گوگل
|
||||
|
||||
برای دریافت اطلاعات بیشتر درباره ماستودون https://joinmastodon.org را ببینید
|
|
@ -0,0 +1 @@
|
|||
یک کارخواه چندحسابی برای شبکه اجتماعی ماستودون
|
|
@ -0,0 +1 @@
|
|||
تاسکی
|
|
@ -0,0 +1,9 @@
|
|||
Tusky v9.0
|
||||
|
||||
- Indíthatsz szavazást Tusky-ból
|
||||
- Fejlettebb keresés
|
||||
- Új opció a fiók beállításainál a tartalom-figyelmeztetések alapértelmezett mutatására
|
||||
- A navigációs fióknál az avataroknak lekerekített négyzet alakja van
|
||||
- Be lehet jelenteni olyan fiókokat is, akik még sosem posztoltak
|
||||
- A Tusky nem fog csatlakozni titkosítatlan kapcsolaton Android 6+-on
|
||||
- Egy rakás kisebb fejlesztés és bugfix
|
|
@ -0,0 +1,3 @@
|
|||
Tusky v9.1
|
||||
|
||||
Denne versjonen sørger for kompatibilitet med Mastodon 3, og forbedrer ytelse og stabilitet.
|
|
@ -0,0 +1,9 @@
|
|||
Tusky v9.0
|
||||
|
||||
- Agora você pode criar enquetes no Tusky
|
||||
- Pesquisa melhorada
|
||||
- Nova opção nas preferências da conta: "Sempre expandir toots com Aviso de Conteúdo"
|
||||
- Avatares em formato quadrado arredondado
|
||||
- Agora é possível denunciar usuários mesmo sem toots
|
||||
- Tusky se recusará a se conectar através de conexões de texto claro em Android 6+
|
||||
- Muitas outras pequenas melhorias e correções de bugs
|
Loading…
Reference in New Issue