Fix lint warnings (#4019)
Clears the baseline of issues in our code, and resolves most of the straightforward warnings from the report
This commit is contained in:
parent
7dfc8790c7
commit
f99cb6d1d5
35 changed files with 154 additions and 252 deletions
|
@ -1,17 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.1.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.1)" variant="all" version="8.1.1">
|
||||
|
||||
<issue
|
||||
id="MissingPermission"
|
||||
message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
|
||||
errorLine1=" notificationManager.notify(notificationId, builder.build())"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt"
|
||||
line="78"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="CheckResult"
|
||||
message="The result of `placeholder` is not used"
|
||||
|
@ -72,8 +61,6 @@
|
|||
<location
|
||||
file="$GRADLE_USER_HOME/caches/modules-2/files-2.1/org.pageseeder.diffx/pso-diffx/1.1.1/b655ebc87588a857a4f3d88cf98bcefa87a6105b/pso-diffx-1.1.1.jar"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseAppTint"
|
||||
message="Must use `app:tint` instead of `android:tint`"
|
||||
errorLine1=" android:tint="?android:attr/textColorTertiary""
|
||||
|
@ -84,72 +71,6 @@
|
|||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatLoadingForDrawables"
|
||||
message="Use `AppCompatResources.getDrawable()`"
|
||||
errorLine1=" return ctx.getDrawable(R.drawable.avatar_default)!!"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/keylesspalace/tusky/MainActivity.kt"
|
||||
line="533"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatTextViewDrawableXml"
|
||||
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
|
||||
errorLine1=" android:drawableStart="@drawable/ic_briefcase""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/activity_account.xml"
|
||||
line="282"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatTextViewDrawableXml"
|
||||
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
|
||||
errorLine1=" android:drawableStart="@drawable/ic_plus_24dp""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/activity_tab_preference.xml"
|
||||
line="69"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatTextViewDrawableXml"
|
||||
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
|
||||
errorLine1=" android:drawableStart="@drawable/ic_person_add_24dp""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_follow.xml"
|
||||
line="17"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatTextViewDrawableXml"
|
||||
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
|
||||
errorLine1=" android:drawableStart="@drawable/ic_reblog_18dp""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/item_status.xml"
|
||||
line="20"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UseCompatTextViewDrawableXml"
|
||||
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
|
||||
errorLine1=" android:drawableStart="@drawable/ic_poll_24dp""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/view_poll_preview.xml"
|
||||
line="15"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="PluralsCandidate"
|
||||
message="Formatting %d followed by words ("posts"): This should probably be a plural rather than a string"
|
||||
|
@ -436,28 +357,6 @@
|
|||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Recycle"
|
||||
message="This `InputStream` should be freed up after use with `#close()`"
|
||||
errorLine1=" from = contentResolver.openInputStream(this)"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/keylesspalace/tusky/util/IOUtils.kt"
|
||||
line="45"
|
||||
column="32"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Recycle"
|
||||
message="This `InputStream` should be freed up after use with `#close()`"
|
||||
errorLine1=" val stream = contentResolver.openInputStream(media.uri)"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt"
|
||||
line="274"
|
||||
column="42"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 24"
|
||||
|
@ -734,15 +633,6 @@
|
|||
</issue>
|
||||
|
||||
<issue
|
||||
id="KeyboardInaccessibleWidget"
|
||||
message="'clickable' attribute found, please also add 'focusable'"
|
||||
errorLine1=" android:clickable="true""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/material_drawer_header.xml"
|
||||
line="8"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SetTextI18n"
|
||||
|
|
|
@ -62,6 +62,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
private static final String TAG = "BaseActivity";
|
||||
|
||||
@Inject
|
||||
@NonNull
|
||||
public AccountManager accountManager;
|
||||
|
||||
private static final int REQUESTER_NONE = Integer.MAX_VALUE;
|
||||
|
@ -164,13 +165,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
return style;
|
||||
}
|
||||
|
||||
public void startActivityWithSlideInAnimation(Intent intent) {
|
||||
public void startActivityWithSlideInAnimation(@NonNull Intent intent) {
|
||||
super.startActivity(intent);
|
||||
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
getOnBackPressedDispatcher().onBackPressed();
|
||||
return true;
|
||||
|
@ -198,7 +199,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
}
|
||||
}
|
||||
|
||||
protected void showErrorDialog(View anyView, @StringRes int descriptionId, @StringRes int actionId, View.OnClickListener listener) {
|
||||
protected void showErrorDialog(@Nullable View anyView, @StringRes int descriptionId, @StringRes int actionId, @Nullable View.OnClickListener listener) {
|
||||
if (anyView != null) {
|
||||
Snackbar bar = Snackbar.make(anyView, getString(descriptionId), Snackbar.LENGTH_SHORT);
|
||||
bar.setAction(actionId, listener);
|
||||
|
@ -206,7 +207,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
}
|
||||
}
|
||||
|
||||
public void showAccountChooserDialog(CharSequence dialogTitle, boolean showActiveAccount, AccountSelectionListener listener) {
|
||||
public void showAccountChooserDialog(@Nullable CharSequence dialogTitle, boolean showActiveAccount, @NonNull AccountSelectionListener listener) {
|
||||
List<AccountEntity> accounts = accountManager.getAllAccountsOrderedByActive();
|
||||
AccountEntity activeAccount = accountManager.getActiveAccount();
|
||||
|
||||
|
@ -273,7 +274,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
}
|
||||
}
|
||||
|
||||
public void requestPermissions(String[] permissions, PermissionRequester requester) {
|
||||
public void requestPermissions(@NonNull String[] permissions, @NonNull PermissionRequester requester) {
|
||||
ArrayList<String> permissionsToRequest = new ArrayList<>();
|
||||
for(String permission: permissions) {
|
||||
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.graphics.drawable.Animatable
|
|||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
|
@ -180,6 +181,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
/** Adapter for the different timeline tabs */
|
||||
private lateinit var tabAdapter: MainPagerAdapter
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -355,7 +357,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
)
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= 33 &&
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
||||
|
@ -530,7 +535,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
override fun placeholder(ctx: Context, tag: String?): Drawable {
|
||||
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) {
|
||||
return ctx.getDrawable(R.drawable.avatar_default)!!
|
||||
return AppCompatResources.getDrawable(ctx, R.drawable.avatar_default)!!
|
||||
}
|
||||
|
||||
return super.placeholder(ctx, tag)
|
||||
|
@ -914,6 +919,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
updateShortcut(this, accountManager.activeAccount!!)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
|
||||
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
||||
val animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)
|
||||
|
|
|
@ -130,7 +130,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
private final Drawable mediaPreviewUnloaded;
|
||||
|
||||
protected StatusBaseViewHolder(View itemView) {
|
||||
protected StatusBaseViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
displayName = itemView.findViewById(R.id.status_display_name);
|
||||
username = itemView.findViewById(R.id.status_username);
|
||||
|
@ -191,14 +191,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
TouchDelegateHelper.expandTouchSizeToFillRow((ViewGroup) itemView, CollectionsKt.listOfNotNull(replyButton, reblogButton, favouriteButton, bookmarkButton, moreButton));
|
||||
}
|
||||
|
||||
protected void setDisplayName(String name, List<Emoji> customEmojis, StatusDisplayOptions statusDisplayOptions) {
|
||||
protected void setDisplayName(@NonNull String name, @Nullable List<Emoji> customEmojis, @NonNull StatusDisplayOptions statusDisplayOptions) {
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(
|
||||
name, customEmojis, displayName, statusDisplayOptions.animateEmojis()
|
||||
);
|
||||
displayName.setText(emojifiedName);
|
||||
}
|
||||
|
||||
protected void setUsername(String name) {
|
||||
protected void setUsername(@Nullable String name) {
|
||||
Context context = username.getContext();
|
||||
String usernameText = context.getString(R.string.post_username_format, name);
|
||||
username.setText(usernameText);
|
||||
|
@ -210,7 +210,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
protected void setSpoilerAndContent(@NonNull StatusViewData.Concrete status,
|
||||
@NonNull StatusDisplayOptions statusDisplayOptions,
|
||||
final StatusActionListener listener) {
|
||||
final @NonNull StatusActionListener listener) {
|
||||
|
||||
Status actionable = status.getActionable();
|
||||
String spoilerText = actionable.getSpoilerText();
|
||||
|
@ -340,7 +340,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
Collections.singletonList(new CompositeWithOpaqueBackground(avatar)));
|
||||
}
|
||||
|
||||
protected void setMetaData(StatusViewData.Concrete statusViewData, StatusDisplayOptions statusDisplayOptions, StatusActionListener listener) {
|
||||
protected void setMetaData(@NonNull StatusViewData.Concrete statusViewData, @NonNull StatusDisplayOptions statusDisplayOptions, @NonNull StatusActionListener listener) {
|
||||
|
||||
Status status = statusViewData.getActionable();
|
||||
Date createdAt = status.getCreatedAt();
|
||||
|
@ -491,9 +491,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
|
||||
protected void setMediaPreviews(
|
||||
final List<Attachment> attachments,
|
||||
final @NonNull List<Attachment> attachments,
|
||||
boolean sensitive,
|
||||
final StatusActionListener listener,
|
||||
final @NonNull StatusActionListener listener,
|
||||
boolean showingContent,
|
||||
boolean useBlurhash
|
||||
) {
|
||||
|
@ -584,8 +584,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
mediaLabels[index].setText(label);
|
||||
}
|
||||
|
||||
protected void setMediaLabel(List<Attachment> attachments, boolean sensitive,
|
||||
final StatusActionListener listener, boolean showingContent) {
|
||||
protected void setMediaLabel(@NonNull List<Attachment> attachments, boolean sensitive,
|
||||
final @NonNull StatusActionListener listener, boolean showingContent) {
|
||||
Context context = itemView.getContext();
|
||||
for (int i = 0; i < mediaLabels.length; i++) {
|
||||
TextView mediaLabel = mediaLabels[i];
|
||||
|
@ -606,7 +606,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
private void setAttachmentClickListener(View view, StatusActionListener listener,
|
||||
private void setAttachmentClickListener(View view, @NonNull StatusActionListener listener,
|
||||
int index, Attachment attachment, boolean animateTransition) {
|
||||
view.setOnClickListener(v -> {
|
||||
int position = getBindingAdapterPosition();
|
||||
|
@ -630,10 +630,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
sensitiveMediaShow.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
protected void setupButtons(final StatusActionListener listener,
|
||||
final String accountId,
|
||||
final String statusContent,
|
||||
StatusDisplayOptions statusDisplayOptions) {
|
||||
protected void setupButtons(final @NonNull StatusActionListener listener,
|
||||
final @NonNull String accountId,
|
||||
final @Nullable String statusContent,
|
||||
@NonNull StatusDisplayOptions statusDisplayOptions) {
|
||||
View.OnClickListener profileButtonClickListener = button -> listener.onViewAccount(accountId);
|
||||
|
||||
avatar.setOnClickListener(profileButtonClickListener);
|
||||
|
@ -752,8 +752,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
popup.show();
|
||||
}
|
||||
|
||||
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
StatusDisplayOptions statusDisplayOptions) {
|
||||
public void setupWithStatus(@NonNull StatusViewData.Concrete status, final @NonNull StatusActionListener listener,
|
||||
@NonNull StatusDisplayOptions statusDisplayOptions) {
|
||||
this.setupWithStatus(status, listener, statusDisplayOptions, null);
|
||||
}
|
||||
|
||||
|
@ -843,7 +843,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
filteredPlaceholderShowButton.setOnClickListener(view -> listener.clearWarningAction(getBindingAdapterPosition()));
|
||||
}
|
||||
|
||||
protected static boolean hasPreviewableAttachment(List<Attachment> attachments) {
|
||||
protected static boolean hasPreviewableAttachment(@NonNull List<Attachment> attachments) {
|
||||
for (Attachment attachment : attachments) {
|
||||
if (attachment.getType() == Attachment.Type.AUDIO || attachment.getType() == Attachment.Type.UNKNOWN) {
|
||||
return false;
|
||||
|
@ -918,7 +918,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
protected static CharSequence getVisibilityDescription(Context context, Status.Visibility visibility) {
|
||||
@NonNull
|
||||
protected static CharSequence getVisibilityDescription(@NonNull Context context, @Nullable Status.Visibility visibility) {
|
||||
|
||||
if (visibility == null) {
|
||||
return "";
|
||||
|
@ -967,7 +968,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
protected CharSequence getFavsText(Context context, int count) {
|
||||
@NonNull
|
||||
protected CharSequence getFavsText(@NonNull Context context, int count) {
|
||||
if (count > 0) {
|
||||
String countString = numberFormat.format(count);
|
||||
return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.favs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
|
||||
|
@ -976,7 +978,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
protected CharSequence getReblogsText(Context context, int count) {
|
||||
@NonNull
|
||||
protected CharSequence getReblogsText(@NonNull Context context, int count) {
|
||||
if (count > 0) {
|
||||
String countString = numberFormat.format(count);
|
||||
return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.reblogs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
|
||||
|
@ -1077,11 +1080,11 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
}
|
||||
|
||||
protected void setupCard(
|
||||
final StatusViewData.Concrete status,
|
||||
final @NonNull StatusViewData.Concrete status,
|
||||
boolean expanded,
|
||||
final CardViewMode cardViewMode,
|
||||
final StatusDisplayOptions statusDisplayOptions,
|
||||
final StatusActionListener listener
|
||||
final @NonNull CardViewMode cardViewMode,
|
||||
final @NonNull StatusDisplayOptions statusDisplayOptions,
|
||||
final @NonNull StatusActionListener listener
|
||||
) {
|
||||
if (cardView == null) {
|
||||
return;
|
||||
|
|
|
@ -35,7 +35,7 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
|||
|
||||
private static final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT);
|
||||
|
||||
public StatusDetailedViewHolder(View view) {
|
||||
public StatusDetailedViewHolder(@NonNull View view) {
|
||||
super(view);
|
||||
reblogs = view.findViewById(R.id.status_reblogs);
|
||||
favourites = view.findViewById(R.id.status_favourites);
|
||||
|
@ -43,7 +43,7 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void setMetaData(StatusViewData.Concrete statusViewData, StatusDisplayOptions statusDisplayOptions, StatusActionListener listener) {
|
||||
protected void setMetaData(@NonNull StatusViewData.Concrete statusViewData, @NonNull StatusDisplayOptions statusDisplayOptions, @NonNull StatusActionListener listener) {
|
||||
|
||||
Status status = statusViewData.getActionable();
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||
private final TextView favouritedCountLabel;
|
||||
private final TextView reblogsCountLabel;
|
||||
|
||||
public StatusViewHolder(View itemView) {
|
||||
public StatusViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
statusInfo = itemView.findViewById(R.id.status_info);
|
||||
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.components.announcements
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.ContextThemeWrapper
|
||||
|
@ -55,6 +56,7 @@ class AnnouncementAdapter(
|
|||
return BindingHolder(binding)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemAnnouncementBinding>, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ class ComposeViewModel @Inject constructor(
|
|||
val mediaUris: MutableList<String> = mutableListOf()
|
||||
val mediaDescriptions: MutableList<String?> = mutableListOf()
|
||||
val mediaFocus: MutableList<Attachment.Focus?> = mutableListOf()
|
||||
media.value.forEach { item ->
|
||||
for (item in media.value) {
|
||||
mediaUris.add(item.uri.toString())
|
||||
mediaDescriptions.add(item.description)
|
||||
mediaFocus.add(item.focus)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.components.compose
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.media.MediaMetadataRetriever
|
||||
|
@ -246,6 +247,7 @@ class MediaUploader @Inject constructor(
|
|||
|
||||
private val contentResolver = context.contentResolver
|
||||
|
||||
@SuppressLint("Recycle") // stream is closed in ProgressRequestBody
|
||||
private suspend fun upload(media: QueuedMedia): Flow<UploadEvent> {
|
||||
return callbackFlow {
|
||||
var mimeType = contentResolver.getType(media.uri)
|
||||
|
|
|
@ -149,7 +149,7 @@ public class NotificationHelper {
|
|||
* @return the new notification
|
||||
*/
|
||||
@NonNull
|
||||
public static android.app.Notification make(final Context context, NotificationManager notificationManager, Notification body, AccountEntity account, boolean isFirstOfBatch) {
|
||||
public static android.app.Notification make(final @NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Notification body, @NonNull AccountEntity account, boolean isFirstOfBatch) {
|
||||
body = body.rewriteToStatusTypeIfNeeded(account.getAccountId());
|
||||
String mastodonNotificationId = body.getId();
|
||||
int accountId = (int) account.getId();
|
||||
|
@ -270,7 +270,7 @@ public class NotificationHelper {
|
|||
* @param notificationManager the system's NotificationManager
|
||||
* @param account the account for which the notification should be shown
|
||||
*/
|
||||
public static void updateSummaryNotifications(Context context, NotificationManager notificationManager, AccountEntity account) {
|
||||
public static void updateSummaryNotifications(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull AccountEntity account) {
|
||||
// Map from the channel ID to a list of notifications in that channel. Those are the
|
||||
// notifications that will be summarised.
|
||||
Map<String, List<StatusBarNotification>> channelGroups = new HashMap<>();
|
||||
|
@ -608,7 +608,7 @@ public class NotificationHelper {
|
|||
|
||||
}
|
||||
|
||||
public static void enablePullNotifications(Context context) {
|
||||
public static void enablePullNotifications(@NonNull Context context) {
|
||||
WorkManager workManager = WorkManager.getInstance(context);
|
||||
workManager.cancelAllWorkByTag(NOTIFICATION_PULL_TAG);
|
||||
|
||||
|
@ -636,7 +636,7 @@ public class NotificationHelper {
|
|||
Log.d(TAG, "enabled notification checks with "+ PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS + "ms interval");
|
||||
}
|
||||
|
||||
public static void disablePullNotifications(Context context) {
|
||||
public static void disablePullNotifications(@NonNull Context context) {
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(NOTIFICATION_PULL_TAG);
|
||||
Log.d(TAG, "disabled notification checks");
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ public class NotificationHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean filterNotification(NotificationManager notificationManager, AccountEntity account, @NonNull Notification notification) {
|
||||
public static boolean filterNotification(@NonNull NotificationManager notificationManager, @NonNull AccountEntity account, @NonNull Notification notification) {
|
||||
return filterNotification(notificationManager, account, notification.getType());
|
||||
}
|
||||
|
||||
|
|
|
@ -474,7 +474,7 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
private fun requestDownloadAllMedia(status: Status) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
(activity as BaseActivity).requestPermissions(permissions) { _: Array<String?>?, grantResults: IntArray ->
|
||||
(activity as BaseActivity).requestPermissions(permissions) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status)
|
||||
} else {
|
||||
|
|
|
@ -12,12 +12,11 @@
|
|||
*
|
||||
* 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.interfaces
|
||||
|
||||
package com.keylesspalace.tusky.interfaces;
|
||||
|
||||
public interface AccountActionListener {
|
||||
void onViewAccount(String id);
|
||||
void onMute(final boolean mute, final String id, final int position, final boolean notifications);
|
||||
void onBlock(final boolean block, final String id, final int position);
|
||||
void onRespondToFollowRequest(final boolean accept, final String id, final int position);
|
||||
interface AccountActionListener {
|
||||
fun onViewAccount(id: String)
|
||||
fun onMute(mute: Boolean, id: String, position: Int, notifications: Boolean)
|
||||
fun onBlock(block: Boolean, id: String, position: Int)
|
||||
fun onRespondToFollowRequest(accept: Boolean, id: String, position: Int)
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package com.keylesspalace.tusky.interfaces;
|
||||
|
||||
public interface PermissionRequester {
|
||||
void onRequestPermissionsResult(String[] permissions, int[] grantResults);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.keylesspalace.tusky.interfaces
|
||||
|
||||
fun interface PermissionRequester {
|
||||
fun onRequestPermissionsResult(permissions: Array<String>, grantResults: IntArray)
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* 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.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okio.BufferedSink;
|
||||
|
||||
public final class ProgressRequestBody extends RequestBody {
|
||||
private final InputStream content;
|
||||
private final long contentLength;
|
||||
private final UploadCallback uploadListener;
|
||||
private final MediaType mediaType;
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 2048;
|
||||
|
||||
public interface UploadCallback {
|
||||
void onProgressUpdate(int percentage);
|
||||
}
|
||||
|
||||
public ProgressRequestBody(final InputStream content, long contentLength, final MediaType mediaType, final UploadCallback listener) {
|
||||
this.content = content;
|
||||
this.contentLength = contentLength;
|
||||
this.mediaType = mediaType;
|
||||
this.uploadListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
||||
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
long uploaded = 0;
|
||||
|
||||
try {
|
||||
int read;
|
||||
while ((read = content.read(buffer)) != -1) {
|
||||
uploadListener.onProgressUpdate((int)(100 * uploaded / contentLength));
|
||||
|
||||
uploaded += read;
|
||||
sink.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
uploadListener.onProgressUpdate((int)(100 * uploaded / contentLength));
|
||||
} finally {
|
||||
content.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* 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.network
|
||||
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
class ProgressRequestBody(private val content: InputStream, private val contentLength: Long, private val mediaType: MediaType, private val uploadListener: UploadCallback) : RequestBody() {
|
||||
fun interface UploadCallback {
|
||||
fun onProgressUpdate(percentage: Int)
|
||||
}
|
||||
|
||||
override fun contentType(): MediaType {
|
||||
return mediaType
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return contentLength
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
var uploaded: Long = 0
|
||||
|
||||
content.use { content ->
|
||||
var read: Int
|
||||
while (content.read(buffer).also { read = it } != -1) {
|
||||
uploadListener.onProgressUpdate((100 * uploaded / contentLength).toInt())
|
||||
uploaded += read.toLong()
|
||||
sink.write(buffer, 0, read)
|
||||
}
|
||||
uploadListener.onProgressUpdate((100 * uploaded / contentLength).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_BUFFER_SIZE = 2048
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.receiver
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -39,6 +40,7 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() {
|
|||
@Inject
|
||||
lateinit var accountManager: AccountManager
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
AndroidInjection.inject(this, context)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import java.io.Closeable
|
||||
|
@ -34,6 +35,7 @@ fun Closeable?.closeQuietly() {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle") // The linter can't tell that the stream gets closed by a helper method
|
||||
fun Uri.copyToFile(
|
||||
contentResolver: ContentResolver,
|
||||
file: File
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
android:layout_alignTop="@+id/account_header_info"
|
||||
android:background="?attr/colorPrimaryDark"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/label_header"
|
||||
app:layout_collapseMode="parallax"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -279,8 +280,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:drawableStart="@drawable/ic_briefcase"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_briefcase"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Account has moved" />
|
||||
|
@ -471,6 +472,7 @@
|
|||
android:layout_height="@dimen/account_activity_avatar_size"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="3dp"
|
||||
android:contentDescription="@string/label_avatar"
|
||||
app:layout_anchor="@+id/accountHeaderInfoContainer"
|
||||
app:layout_anchorGravity="top"
|
||||
app:layout_scrollFlags="scroll"
|
||||
|
|
|
@ -311,7 +311,7 @@
|
|||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_toggle_visibility"
|
||||
android:padding="4dp"
|
||||
android:tint="?android:attr/textColorTertiary"
|
||||
app:tint="?android:attr/textColorTertiary"
|
||||
app:tooltipText="@string/action_toggle_visibility"
|
||||
tools:src="@drawable/ic_public_24dp" />
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
android:layout_height="48dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:drawableStart="@drawable/ic_plus_24dp"
|
||||
android:drawablePadding="12dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -75,7 +74,8 @@
|
|||
android:paddingEnd="8dp"
|
||||
android:text="@string/action_add_tab"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
android:textSize="?attr/status_text_large" />
|
||||
android:textSize="?attr/status_text_large"
|
||||
app:drawableStartCompat="@drawable/ic_plus_24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/filter_add_description"
|
||||
android:inputType="text"
|
||||
android:importantForAutofill="no"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
<CheckBox
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<!-- todo add padding -->
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:contentDescription="@string/label_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
|
||||
<Button
|
||||
android:id="@+id/buttonBack"
|
||||
tools:ignore="BackButton"
|
||||
style="@style/TuskyButton.Outlined"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<com.keylesspalace.tusky.components.account.media.SquareImageView
|
||||
android:id="@+id/accountMediaImageView"
|
||||
|
@ -11,6 +12,7 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/accountMediaImageViewOverlay"
|
||||
tools:ignore="ContentDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableStart="@drawable/ic_person_add_24dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
|
@ -22,6 +21,7 @@
|
|||
android:paddingStart="28dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_person_add_24dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Someone followed you" />
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
android:id="@+id/notification_reporter_avatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="@string/action_view_profile"
|
||||
app:layout_constraintRight_toRightOf="@id/notification_reportee_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/notification_reportee_avatar"
|
||||
/>
|
||||
|
@ -60,6 +61,7 @@
|
|||
android:lineSpacingMultiplier="1.1"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:ignore="NegativeMargin"
|
||||
tools:text="30 minutes ago - 2 posts" />
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="@dimen/status_reblogged_bar_padding_top"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:drawableStart="@drawable/ic_reblog_18dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:gravity="center_vertical"
|
||||
android:importantForAccessibility="no"
|
||||
android:paddingStart="38dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_reblog_18dp"
|
||||
app:layout_constraintLeft_toRightOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -318,6 +318,7 @@
|
|||
android:layout_marginStart="-14dp"
|
||||
android:contentDescription="@string/action_reply"
|
||||
android:importantForAccessibility="no"
|
||||
tools:ignore="NegativeMargin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/status_inset"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/status_filtered_placeholder"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_filter_label"
|
||||
|
@ -16,6 +16,7 @@
|
|||
android:textSize="?attr/status_text_medium"
|
||||
android:textAlignment="center"
|
||||
android:text="Filter: MyFilter"
|
||||
tools:ignore="HardcodedText"
|
||||
/>
|
||||
|
||||
<Button
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
android:id="@+id/notification_notification_avatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="@string/action_view_profile"
|
||||
app:layout_constraintBottom_toBottomOf="@id/notification_status_avatar"
|
||||
app:layout_constraintEnd_toEndOf="@id/notification_status_avatar" />
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:src="@drawable/ic_drag_indicator_24dp"
|
||||
tools:ignore="ContentDescription"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/textView"/>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/material_drawer_account_header_height"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_poll_24dp"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/create_poll_title"
|
||||
android:textStyle="bold" />
|
||||
android:textStyle="bold"
|
||||
app:drawableStartCompat="@drawable/ic_poll_24dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/pollPreviewOptions"
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
|
||||
<item
|
||||
android:id="@+id/action_mute_hashtag"
|
||||
android:title="Mute"
|
||||
android:title="@string/action_mute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_mute_24dp" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_unmute_hashtag"
|
||||
android:title="Unmute"
|
||||
android:title="@string/action_unmute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_unmute_24dp" />
|
||||
|
|
|
@ -241,6 +241,7 @@
|
|||
<string name="label_quick_reply">Reply…</string>
|
||||
<string name="label_avatar">Avatar</string>
|
||||
<string name="label_header">Header</string>
|
||||
<string name="label_image">Image</string>
|
||||
|
||||
<string name="link_whats_an_instance">What\'s an instance?</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue