Compare commits

...

87 Commits

Author SHA1 Message Date
sk
f0b9006c55 bump version 2022-12-01 16:17:51 +01:00
sk
4bc14ef797 remove long strings 2022-12-01 16:11:23 +01:00
Rebecca Södergren
47d2cee3f1 Translated using Weblate (Swedish)
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/sv/
2022-12-01 15:10:42 +00:00
Rebecca Södergren
088f53f5a9 Translated using Weblate (Swedish)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sv/
2022-12-01 15:10:42 +00:00
kaea
fee660bf6c Translated using Weblate (Polish)
Currently translated at 97.4% (38 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-01 15:10:41 +00:00
Linerly
e97ecb89a9 Translated using Weblate (Indonesian)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-01 15:10:41 +00:00
sk
1ab6a4532b remove long string 2022-12-01 16:10:15 +01:00
sk
b43ddd0d8b Merge remote-tracking branch 'weblate/main' 2022-12-01 16:07:08 +01:00
sk
d0c4c2d594 Merge branch 'feature/more-html-tags' 2022-12-01 16:06:40 +01:00
sk
e0ae079ea0 make sub/sup text smaller 2022-12-01 16:06:29 +01:00
Linerly
dfddbd15a9 Translated using Weblate (Indonesian)
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-01 11:03:20 +00:00
qqherb
29242c45a1 Translated using Weblate (Chinese (Simplified))
Currently translated at 40.0% (2 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2022-12-01 11:03:20 +00:00
Choukajohn
7ff0e59f4d Translated using Weblate (French)
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-01 11:03:20 +00:00
khant
2d0fe57a47 Translated using Weblate (Burmese)
Currently translated at 82.0% (32 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/my/
2022-12-01 11:03:20 +00:00
kaea
ba2b87749b Translated using Weblate (Polish)
Currently translated at 66.6% (26 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-01 11:03:20 +00:00
Linerly
0806af1261 Translated using Weblate (Indonesian)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-01 11:03:20 +00:00
crse
09e92f3a18 Translated using Weblate (Indonesian)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-01 11:03:19 +00:00
Choukajohn
eb5d0bb795 Translated using Weblate (French)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-01 11:03:19 +00:00
hegondev
a8afba4067 Translated using Weblate (Spanish)
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-01 11:03:19 +00:00
qqherb
7d66141c37 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (39 of 39 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-01 11:03:19 +00:00
khant
b6f3ea2eec Added translation using Weblate (Burmese) 2022-12-01 06:01:03 +00:00
sk
2570445133 create empty strings_sk files for all languages 2022-12-01 00:23:25 +01:00
sk
acbd22cf22 add weblate project
closes #18
2022-12-01 00:20:12 +01:00
sk22
ef251b040a Translated using Weblate (German)
Currently translated at 100.0% (5 of 5 strings)

Translation: megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2022-11-30 23:04:59 +00:00
sk
6c5bb69ba9 move custom strings into separate files 2022-11-30 23:16:14 +01:00
sk
7599406449 Merge branch 'feature/more-html-tags' 2022-11-30 16:15:38 +01:00
sk
670e4c8538 support headings and reduce empty line height 2022-11-30 16:15:31 +01:00
sk
30458b115c Merge branch 'feature/more-html-tags' 2022-11-30 11:46:16 +01:00
sk
da8933ec58 Merge branch 'fix/markdown-lists' into feature/more-html-tags 2022-11-30 11:45:48 +01:00
sk
dc8ac51c83 fix inline rendering of block elements 2022-11-30 11:43:34 +01:00
sk
c4747fdc72 add support for headings 2022-11-30 11:41:10 +01:00
sk
58ba748ade add blockquote support 2022-11-30 11:20:58 +01:00
sk
c0d51ad58a Merge branch 'feature/more-html-tags' 2022-11-30 11:08:55 +01:00
sk
a0da73f76f add support for html code tag 2022-11-30 11:05:00 +01:00
sk
34b8888c8f add support for html code tag 2022-11-30 11:04:19 +01:00
sk
74ad40f67c Merge branch 'fix/markdown-lists' into feature/more-html-tags 2022-11-30 10:55:26 +01:00
sk
a7a29db8d5 improve list rendering implementation 2022-11-30 10:51:25 +01:00
sk
86a938d31d Merge branch 'feature/more-html-tags' 2022-11-30 00:38:08 +01:00
sk
d8d0830631 add support for more html tags
closes #16
2022-11-30 00:36:42 +01:00
sk
bba3b7476a Merge branch 'fix/markdown-lists' 2022-11-30 00:17:40 +01:00
sk
4880c642fe render markdown lists correctly
closes mastodon#152
2022-11-30 00:11:42 +01:00
sk
76ad896461 update readme 2022-11-29 23:09:19 +01:00
sk22
b49159f9e0 Update issue templates 2022-11-29 23:06:04 +01:00
sk
5f3645f716 bump version 2022-11-29 19:25:18 +01:00
sk
5af96597d5 add option to hide/show federated timeline 2022-11-29 19:24:02 +01:00
sk
c2baf4e05f Revert "Revert "add release without federated timeline""
This reverts commit 851bf94c90.
2022-11-29 18:29:13 +01:00
sk
9e7923bc50 bump version 2022-11-29 00:25:18 +01:00
sk
851bf94c90 Revert "add release without federated timeline"
This reverts commit 5cce8ca72c.
2022-11-29 00:24:36 +01:00
sk
ae80b7d098 minify play release 2022-11-28 20:59:13 +01:00
sk
e6bb319d8b add changelog and update readme 2022-11-28 18:43:57 +01:00
sk
3101f1ad17 bump version 2022-11-28 18:30:49 +01:00
sk
4416dfcae3 Merge branch 'feature/clickable-reply-line-compose' into fork 2022-11-28 18:28:31 +01:00
sk
924b974b4b make reply line open post 2022-11-28 18:26:21 +01:00
sk
5d1dc97ac3 change contribute link 2022-11-28 17:54:02 +01:00
sk
5cce8ca72c add release without federated timeline 2022-11-28 17:50:26 +01:00
sk
f2a0680af0 Merge branch 'fork' of github.com:sk22/megalodon into fork 2022-11-28 12:07:38 +01:00
sk
6203ded864 Merge branch 'main' into fork 2022-11-28 12:07:35 +01:00
sk22
17f1eb88e4 Merge pull request #110 from Surendrajat/rip-appcenter
Get rid of Microsoft appcenter related stuff
2022-11-28 10:11:18 +01:00
sk
9a52cc033a set custom redirect uri
closes #89
2022-11-28 10:00:57 +01:00
Grishka
4b16262a1a Sync last seen notification ID with server 2022-11-27 13:39:50 +03:00
Grishka
10e7cbf022 Merge branch 'add/verified-profile-fields' 2022-11-27 13:15:13 +03:00
Grishka
531b8ead04 Make verified fields more like on iOS 2022-11-27 13:14:31 +03:00
Grishka
4b2c94ab52 Implement bookmarks and add favorites list
Closes #22, at last
2022-11-27 12:43:07 +03:00
Grishka
a98becf2f4 Update splash logo to purple 2022-11-27 11:40:55 +03:00
Grishka
54f9eace67 Fix #422 2022-11-27 10:02:47 +03:00
Grishka
e4c9eb089a Hide posts when muting, blocking or unfollowing an account 2022-11-26 23:09:46 +03:00
Grishka
0e635aec23 Allow copying the username in profile 2022-11-26 20:33:02 +03:00
Grishka
dc90c09cea Shorten interaction counters 2022-11-26 20:24:27 +03:00
Grishka
06cb335a0a Add tooltips to some icon buttons
closes #423
2022-11-26 20:21:48 +03:00
Grishka
5a681d3557 Fix #403 2022-11-26 20:16:43 +03:00
Grishka
4200486aeb fix 2022-11-26 20:14:30 +03:00
Grishka
62411a563f Fix poll expiration
fixes #238, fixes #417
2022-11-26 20:13:46 +03:00
Grishka
2cabe94ba0 Fix #398 2022-11-26 20:02:30 +03:00
Grishka
4a6baae97a Make URLs clickable in instance rules
closes #389
2022-11-26 19:29:05 +03:00
Grishka
bb12a66781 Fix #313 2022-11-26 18:40:17 +03:00
surendrajat
efa9f524f9 Get rid of Microsoft appcenter related stuff 2022-11-26 13:37:03 +05:30
sk
be0c7777b7 fix typo 2022-11-26 01:01:22 +01:00
sk
92652f6fbd update fastlane descriptions 2022-11-26 01:00:22 +01:00
sk
4ee781dfd5 update fastlane descriptions 2022-11-26 00:04:05 +01:00
sk
bf8ac4bc69 set archive name 2022-11-25 23:37:12 +01:00
sk
a8e7840c04 remove unused script 2022-11-25 23:30:36 +01:00
sk
c91ebda1ff set gradle project name 2022-11-25 23:23:52 +01:00
sk
e7dc5030d5 update fastlane config 2022-11-25 22:09:53 +01:00
sk
4cf55e23ba update readme 2022-11-25 22:08:19 +01:00
sk
8159af0b58 update fastlane config 2022-11-25 22:06:18 +01:00
Jeff Bowen
5d056d5bea AboutViewHolder: Set the background of the field item to green when verified 2022-11-16 19:01:32 -05:00
Jeff Bowen
f500cc7ebf IsoInstantTypeAdapter: Enable parsing of 'offset' date times in profile response 2022-11-16 19:00:11 -05:00
168 changed files with 1772 additions and 841 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots and screen recordings**
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
**Version**
Megalodon version: [e.g. v1.1.4+fork.#]
**Additional context**
- Does this issue also occur with the respective upstream release? (Please test using the respective `upstream-xxxxxx.apk` provided in [Releases](https://github.com/sk22/megalodon/releases)) No / Yes (`mastodon#…`)
> In this case, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead. If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
**Crash log**
If you know your way around Android development tools, please consider attaching a crash log, if possible.

View File

@@ -0,0 +1,20 @@
---
name: Feature/UI request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
If applicable: a clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,10 @@
---
name: It's something else…
about: Issues that can't be categorized as feature requests or bug reports
title: ''
labels: ''
assignees: ''
---

View File

@@ -2,9 +2,11 @@
# Megalodon
[![Translation status](https://translate.codeberg.org/widgets/megalodon/-/svg-badge.svg)](https://translate.codeberg.org/engage/megalodon/)
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly wont ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
**Warning! [The latest version's integrated updater is broken](https://github.com/sk22/megalodon/issues/106) I'll publish a fixed version ASAP! If you're not updating through Izzy's F-Droid repository (more sources to come, hopefully!), you'll have to download the upcoming release manually. Sorry about that!**
**Warning! [A previous version's integrated updater was broken](https://github.com/sk22/megalodon/issues/106) I already published a fixed version! If you're not updating through Izzy's F-Droid repository (more sources to come, hopefully!), you'll have to download the current release manually. Sorry about that!**
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmastodon-android-fork%2Freleases%2Flatest&style=for-the-badge)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
@@ -23,7 +25,7 @@ The Mastodon documentation has some more information about [Unlisted posting](ht
### **Federated timeline**
**This allows you to chronologically see all Public posts from people on all other Fediverse instances your home instance is connected to.**
**This allows you to chronologically see all Public posts from people on all other Fediverse neighborhoods your home instance is connected to.**
Despite being one of the main features of federated social media, the Federated timeline wasnt included in the official Mastodon app supposedly, because this conflicts with Googles safety requirements for apps on the Play Store.
@@ -49,7 +51,9 @@ To bookmark a post, press the button between the Favorite and Share buttons on t
## Installation
**Press the download button above to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.**
**In short: Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.**
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmastodon-android-fork%2Freleases%2Flatest&style=for-the-badge)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
To install this app on your Android device, download the [latest release from GitHub](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk) and open it. You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
@@ -66,8 +70,6 @@ Megalodon makes use of [Mastodon for Android](https://github.com/mastodon/mastod
All downloads can be found on the [Releases](https://github.com/sk22/megalodon/releases) page.
**Warning! [The latest version's integrated updater is broken](https://github.com/sk22/megalodon/issues/106) I'll publish a fixed version ASAP! If you're not updating through Izzy's F-Droid repository (more sources to come, hopefully!), you'll have to download the upcoming release manually. Sorry about that!**
**`megalodon.apk`**
Variant with an integrated updater. If you download Megalodon from here (and not from an app store), just download the regular `megalodon.apk`.
@@ -80,6 +82,19 @@ This is an **unmodified version** of the official [Mastodon for Android](https:/
Variant without the integrated updater. This is the variant to be published to F-Droid.org where an integrated updater is not necessary. -->
---
## Contribution
### Translation
As with the source code, the translation is sourced from the official project, which you can contribute to on the official “**Mastodon for Android**” Crowdin project: https://crowdin.com/project/mastodon-for-android
There's also a handful of custom strings exclusive to this projects that would need to be translated. You can help translate **Megalodon** on Weblate: https://translate.codeberg.org/projects/megalodon/
[![Translation status](https://translate.codeberg.org/widgets/megalodon/-/multi-auto.svg)](https://translate.codeberg.org/engage/megalodon/)
---
@@ -106,7 +121,7 @@ Variant without the integrated updater. This is the variant to be published to F
* [Add notifications tab for posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/posts-notifications-tab)
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-reply-visibility)
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
* [Long-click to copy username from profile](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/copy-username)
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
### Behavior

View File

@@ -6,7 +6,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

View File

@@ -1,3 +0,0 @@
#!/bin/sh
git rev-parse --short --verify upstream/master

View File

@@ -1,16 +1,16 @@
plugins {
id 'com.android.application'
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
android {
compileSdk 33
defaultConfig {
archivesBaseName = "megalodon"
applicationId "org.joinmastodon.android.sk"
minSdk 23
targetSdk 33
versionCode 50
versionName "1.1.4+fork.50"
versionCode 55
versionName "1.1.4+fork.55"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",
@@ -29,19 +29,15 @@ android {
versionNameSuffix '-debug'
applicationIdSuffix '.debug'
}
appcenterPrivateBeta{
initWith release
minifyEnabled false
shrinkResources false
versionNameSuffix "-priv-beta"
}
appcenterPublicBeta{
initWith release
versionNameSuffix "-beta"
}
githubRelease{
initWith release
}
playRelease{
initWith release
minifyEnabled true
shrinkResources true
versionNameSuffix '-play'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
@@ -49,12 +45,6 @@ android {
coreLibraryDesugaringEnabled true
}
sourceSets{
appcenterPrivateBeta{
setRoot "src/appcenter"
}
appcenterPublicBeta{
setRoot "src/appcenter"
}
githubRelease{
setRoot "src/github"
}
@@ -86,12 +76,6 @@ dependencies {
annotationProcessor 'org.parceler:parceler:1.1.12'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
def appCenterSdkVersion = "4.4.2"
appcenterPrivateBetaImplementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
appcenterPrivateBetaImplementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
appcenterPublicBetaImplementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
appcenterPublicBetaImplementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
androidTestImplementation 'androidx.test:core:1.4.1-alpha05'
androidTestImplementation 'androidx.test.ext:junit:1.1.4-alpha05'
androidTestImplementation 'androidx.test:runner:1.5.0-alpha02'

View File

@@ -40,12 +40,6 @@
@com.squareup.otto.Subscribe <methods>;
}
-keep class com.microsoft.appcenter.** {
*;
}
-keep class org.joinmastodon.android.AppCenterWrapper { *; }
-keepattributes LineNumberTable
# Parceler library

View File

@@ -1,23 +0,0 @@
package org.joinmastodon.android;
import android.app.Application;
import android.util.Log;
import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.crashes.Crashes;
import com.microsoft.appcenter.distribute.Distribute;
import com.microsoft.appcenter.distribute.UpdateTrack;
public class AppCenterWrapper{
private static final String TAG="AppCenterWrapper";
public static void init(Application app){
if(AppCenter.isConfigured())
return;
Log.i(TAG, "initializing AppCenter SDK, build type is "+BuildConfig.BUILD_TYPE);
if(BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta"))
Distribute.setUpdateTrack(UpdateTrack.PRIVATE);
AppCenter.start(app, BuildConfig.appCenterKey, Distribute.class, Crashes.class);
}
}

View File

@@ -8,14 +8,13 @@
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<application
android:name=".MastodonApp"
android:allowBackup="true"
android:label="@string/app_name"
android:label="@string/sk_app_name"
android:supportsRtl="true"
android:localeConfig="@xml/locales_config"
android:icon="@mipmap/ic_launcher"
@@ -34,7 +33,7 @@
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="mastodon-android-auth" android:host="callback"/>
<data android:scheme="megalodon-android-auth" android:host="callback"/>
</intent-filter>
</activity>
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">

View File

@@ -10,6 +10,7 @@ public class GlobalUserPreferences{
public static boolean showReplies;
public static boolean showBoosts;
public static boolean loadNewPosts;
public static boolean showFederatedTimeline;
public static boolean showInteractionCounts;
public static boolean alwaysExpandContentWarnings;
public static boolean disableMarquee;
@@ -27,6 +28,7 @@ public class GlobalUserPreferences{
showReplies=prefs.getBoolean("showReplies", true);
showBoosts=prefs.getBoolean("showBoosts", true);
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
disableMarquee=prefs.getBoolean("disableMarquee", false);
@@ -40,6 +42,7 @@ public class GlobalUserPreferences{
.putBoolean("showReplies", showReplies)
.putBoolean("showBoosts", showBoosts)
.putBoolean("loadNewPosts", loadNewPosts)
.putBoolean("showFederatedTimeline", showFederatedTimeline)
.putBoolean("trueBlackTheme", trueBlackTheme)
.putBoolean("showInteractionCounts", showInteractionCounts)
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)

View File

@@ -1,10 +1,8 @@
package org.joinmastodon.android;
import android.Manifest;
import android.app.Application;
import android.app.Fragment;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
@@ -24,8 +22,6 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import org.parceler.Parcels;
import java.lang.reflect.InvocationTargetException;
import androidx.annotation.Nullable;
import me.grishka.appkit.FragmentStackActivity;
@@ -70,12 +66,7 @@ public class MainActivity extends FragmentStackActivity{
}
}
if(BuildConfig.BUILD_TYPE.startsWith("appcenter")){
// Call the appcenter SDK wrapper through reflection because it is only present in beta builds
try{
Class.forName("org.joinmastodon.android.AppCenterWrapper").getMethod("init", Application.class).invoke(null, getApplication());
}catch(ClassNotFoundException|NoSuchMethodException|IllegalAccessException|InvocationTargetException ignore){}
}else if(GithubSelfUpdater.needSelfUpdating()){
if(GithubSelfUpdater.needSelfUpdating()){
GithubSelfUpdater.getInstance().maybeCheckForUpdates();
}
}

View File

@@ -6,8 +6,6 @@ import android.content.Context;
import org.joinmastodon.android.api.PushSubscriptionManager;
import java.lang.reflect.InvocationTargetException;
import me.grishka.appkit.imageloader.ImageCache;
import me.grishka.appkit.utils.NetworkUtils;
import me.grishka.appkit.utils.V;

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.api;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import java.io.IOException;
@@ -26,7 +27,10 @@ public class JsonObjectRequestBody extends RequestBody{
public void writeTo(BufferedSink sink) throws IOException{
try{
OutputStreamWriter writer=new OutputStreamWriter(sink.outputStream(), StandardCharsets.UTF_8);
MastodonAPIController.gson.toJson(obj, writer);
if(obj instanceof JsonElement)
writer.write(obj.toString());
else
MastodonAPIController.gson.toJson(obj, writer);
writer.flush();
}catch(JsonIOException x){
throw new IOException(x);

View File

@@ -365,6 +365,8 @@ public class PushSubscriptionManager{
}
private static void registerAllAccountsForPush(boolean forceReRegister){
if(!arePushNotificationsAvailable())
return;
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
if(session.pushSubscription==null || forceReRegister)
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);

View File

@@ -63,36 +63,6 @@ public class StatusInteractionController{
E.post(new StatusCountersUpdatedEvent(status));
}
public void setBookmarked(Status status, boolean bookmarked){
if(!Looper.getMainLooper().isCurrentThread())
throw new IllegalStateException("Can only be called from main thread");
SetStatusBookmarked current=runningBookmarkRequests.remove(status.id);
if(current!=null){
current.cancel();
}
SetStatusBookmarked req=(SetStatusBookmarked) new SetStatusBookmarked(status.id, bookmarked)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Status result){
runningBookmarkRequests.remove(status.id);
E.post(new StatusCountersUpdatedEvent(result));
}
@Override
public void onError(ErrorResponse error){
runningBookmarkRequests.remove(status.id);
error.showToast(MastodonApp.context);
status.bookmarked=!bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningBookmarkRequests.put(status.id, req);
status.bookmarked=bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
}
public void setReblogged(Status status, boolean reblogged){
if(!Looper.getMainLooper().isCurrentThread())
throw new IllegalStateException("Can only be called from main thread");
@@ -130,4 +100,34 @@ public class StatusInteractionController{
status.reblogsCount--;
E.post(new StatusCountersUpdatedEvent(status));
}
public void setBookmarked(Status status, boolean bookmarked){
if(!Looper.getMainLooper().isCurrentThread())
throw new IllegalStateException("Can only be called from main thread");
SetStatusBookmarked current=runningBookmarkRequests.remove(status.id);
if(current!=null){
current.cancel();
}
SetStatusBookmarked req=(SetStatusBookmarked) new SetStatusBookmarked(status.id, bookmarked)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Status result){
runningBookmarkRequests.remove(status.id);
E.post(new StatusCountersUpdatedEvent(result));
}
@Override
public void onError(ErrorResponse error){
runningBookmarkRequests.remove(status.id);
error.showToast(MastodonApp.context);
status.bookmarked=!bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningBookmarkRequests.put(status.id, req);
status.bookmarked=bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
}
}

View File

@@ -25,10 +25,21 @@ public class IsoInstantTypeAdapter extends TypeAdapter<Instant>{
in.nextNull();
return null;
}
try{
return DateTimeFormatter.ISO_INSTANT.parse(in.nextString(), Instant::from);
}catch(DateTimeParseException x){
String nextString;
try {
nextString = in.nextString();
}catch(Exception e){
return null;
}
try{
return DateTimeFormatter.ISO_INSTANT.parse(nextString, Instant::from);
}catch(DateTimeParseException x){}
try{
return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(nextString, Instant::from);
}catch(DateTimeParseException x){}
return null;
}
}

View File

@@ -0,0 +1,42 @@
package org.joinmastodon.android.api.gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
public class JsonArrayBuilder{
private JsonArray arr=new JsonArray();
public JsonArrayBuilder add(JsonElement el){
arr.add(el);
return this;
}
public JsonArrayBuilder add(String el){
arr.add(el);
return this;
}
public JsonArrayBuilder add(Number el){
arr.add(el);
return this;
}
public JsonArrayBuilder add(boolean el){
arr.add(el);
return this;
}
public JsonArrayBuilder add(JsonObjectBuilder el){
arr.add(el.build());
return this;
}
public JsonArrayBuilder add(JsonArrayBuilder el){
arr.add(el.build());
return this;
}
public JsonArray build(){
return arr;
}
}

View File

@@ -0,0 +1,42 @@
package org.joinmastodon.android.api.gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class JsonObjectBuilder{
private JsonObject obj=new JsonObject();
public JsonObjectBuilder add(String key, JsonElement el){
obj.add(key, el);
return this;
}
public JsonObjectBuilder add(String key, String el){
obj.addProperty(key, el);
return this;
}
public JsonObjectBuilder add(String key, Number el){
obj.addProperty(key, el);
return this;
}
public JsonObjectBuilder add(String key, boolean el){
obj.addProperty(key, el);
return this;
}
public JsonObjectBuilder add(String key, JsonObjectBuilder el){
obj.add(key, el.build());
return this;
}
public JsonObjectBuilder add(String key, JsonArrayBuilder el){
obj.add(key, el.build());
return this;
}
public JsonObject build(){
return obj;
}
}

View File

@@ -1,52 +0,0 @@
package org.joinmastodon.android.api.requests.accounts;
import androidx.annotation.NonNull;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okhttp3.Response;
public class GetBookmarks extends MastodonAPIRequest<List<Status>>{
private String maxId;
public GetBookmarks(String maxID, String minID, int limit){
super(HttpMethod.GET, "/bookmarks", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(minID!=null)
addQueryParameter("min_id", minID);
if(limit>0)
addQueryParameter("limit", ""+limit);
}
@Override
public void validateAndPostprocessResponse(List<Status> respObj, Response httpResponse) throws IOException {
super.validateAndPostprocessResponse(respObj, httpResponse);
// <https://mastodon.social/api/v1/bookmarks?max_id=268962>; rel="next",
// <https://mastodon.social/api/v1/bookmarks?min_id=268981>; rel="prev"
String link=httpResponse.header("link");
// parsing link header by hand; using a library would be cleaner
// (also, the functionality should be part of the max id logics and implemented in MastodonAPIRequest)
if(link==null) return;
String maxIdEq="max_id=";
for(String s : link.split(",")) {
if(s.contains("rel=\"next\"")) {
int start=s.indexOf(maxIdEq)+maxIdEq.length();
int end=s.indexOf('>');
if(start<0 || start>end) return;
this.maxId=s.substring(start, end);
}
}
}
public String getMaxId() {
return maxId;
}
}

View File

@@ -1,49 +0,0 @@
package org.joinmastodon.android.api.requests.accounts;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
import java.io.IOException;
import java.util.List;
import okhttp3.Response;
public class GetFavourites extends MastodonAPIRequest<List<Status>>{
private String maxId;
public GetFavourites(String maxID, String minID, int limit){
super(HttpMethod.GET, "/favourites", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(minID!=null)
addQueryParameter("min_id", minID);
if(limit>0)
addQueryParameter("limit", ""+limit);
}
@Override
public void validateAndPostprocessResponse(List<Status> respObj, Response httpResponse) throws IOException {
super.validateAndPostprocessResponse(respObj, httpResponse);
// <https://mastodon.social/api/v1/bookmarks?max_id=268962>; rel="next",
// <https://mastodon.social/api/v1/bookmarks?min_id=268981>; rel="prev"
String link=httpResponse.header("link");
// parsing link header by hand; using a library would be cleaner
// (also, the functionality should be part of the max id logics and implemented in MastodonAPIRequest)
if(link==null) return;
String maxIdEq="max_id=";
for(String s : link.split(",")) {
if(s.contains("rel=\"next\"")) {
int start=s.indexOf(maxIdEq)+maxIdEq.length();
int end=s.indexOf('>');
if(start<0 || start>end) return;
this.maxId=s.substring(start, end);
}
}
}
public String getMaxId() {
return maxId;
}
}

View File

@@ -2,49 +2,15 @@ package org.joinmastodon.android.api.requests.accounts;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.FollowSuggestion;
import java.io.IOException;
import java.util.List;
import okhttp3.Response;
public class GetFollowRequests extends MastodonAPIRequest<List<Account>>{
private String maxId;
public GetFollowRequests(String maxID, String minID, int limit){
public class GetFollowRequests extends HeaderPaginationRequest<Account>{
public GetFollowRequests(String maxID, int limit){
super(HttpMethod.GET, "/follow_requests", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(minID!=null)
addQueryParameter("min_id", minID);
if(limit>0)
addQueryParameter("limit", ""+limit);
}
@Override
public void validateAndPostprocessResponse(List<Account> respObj, Response httpResponse) throws IOException {
super.validateAndPostprocessResponse(respObj, httpResponse);
// <https://mastodon.social/api/v1/follow_requests?max_id=268962>; rel="next",
// <https://mastodon.social/api/v1/follow_requests?min_id=268981>; rel="prev"
String link=httpResponse.header("link");
// parsing link header by hand; using a library would be cleaner
// (also, the functionality should be part of the max id logics and implemented in MastodonAPIRequest)
if(link==null) return;
String maxIdEq="max_id=";
for(String s : link.split(",")) {
if(s.contains("rel=\"next\"")) {
int start=s.indexOf(maxIdEq)+maxIdEq.length();
int end=s.indexOf('>');
if(start<0 || start>end) return;
this.maxId=s.substring(start, end);
}
}
}
public String getMaxId() {
return maxId;
}
}

View File

@@ -0,0 +1,21 @@
package org.joinmastodon.android.api.requests.markers;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.gson.JsonObjectBuilder;
import org.joinmastodon.android.model.Marker;
public class SaveMarkers extends MastodonAPIRequest<SaveMarkers.Response>{
public SaveMarkers(String lastSeenHomePostID, String lastSeenNotificationID){
super(HttpMethod.POST, "/markers", Response.class);
JsonObjectBuilder builder=new JsonObjectBuilder();
if(lastSeenHomePostID!=null)
builder.add("home", new JsonObjectBuilder().add("last_read_id", lastSeenHomePostID));
if(lastSeenNotificationID!=null)
builder.add("notifications", new JsonObjectBuilder().add("last_read_id", lastSeenNotificationID));
setRequestBody(builder.build());
}
public static class Response{
public Marker home, notifications;
}
}

View File

@@ -0,0 +1,16 @@
package org.joinmastodon.android.api.requests.statuses;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
import org.joinmastodon.android.model.Status;
public class GetBookmarkedStatuses extends HeaderPaginationRequest<Status>{
public GetBookmarkedStatuses(String maxID, int limit){
super(HttpMethod.GET, "/bookmarks", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(limit>0)
addQueryParameter("limit", limit+"");
}
}

View File

@@ -0,0 +1,16 @@
package org.joinmastodon.android.api.requests.statuses;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
import org.joinmastodon.android.model.Status;
public class GetFavoritedStatuses extends HeaderPaginationRequest<Status>{
public GetFavoritedStatuses(String maxID, int limit){
super(HttpMethod.GET, "/favourites", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(limit>0)
addQueryParameter("limit", limit+"");
}
}

View File

@@ -61,7 +61,7 @@ import me.grishka.appkit.api.ErrorResponse;
public class AccountSessionManager{
private static final String TAG="AccountSessionManager";
public static final String SCOPE="read write follow push";
public static final String REDIRECT_URI="mastodon-android-auth://callback";
public static final String REDIRECT_URI="megalodon-android-auth://callback";
private static final AccountSessionManager instance=new AccountSessionManager();
@@ -211,7 +211,7 @@ public class AccountSessionManager{
.path("/oauth/authorize")
.appendQueryParameter("response_type", "code")
.appendQueryParameter("client_id", result.clientId)
.appendQueryParameter("redirect_uri", "mastodon-android-auth://callback")
.appendQueryParameter("redirect_uri", "megalodon-android-auth://callback")
.appendQueryParameter("scope", SCOPE)
.build();

View File

@@ -0,0 +1,13 @@
package org.joinmastodon.android.events;
public class RemoveAccountPostsEvent{
public final String accountID;
public final String postsByAccountID;
public final boolean isUnfollow;
public RemoveAccountPostsEvent(String accountID, String postsByAccountID, boolean isUnfollow){
this.accountID=accountID;
this.postsByAccountID=postsByAccountID;
this.isUnfollow=isUnfollow;
}
}

View File

@@ -5,7 +5,7 @@ import org.joinmastodon.android.model.Status;
public class StatusCountersUpdatedEvent{
public String id;
public long favorites, reblogs, replies;
public boolean favorited, reblogged, pinned;
public boolean favorited, reblogged, bookmarked, pinned;
public StatusCountersUpdatedEvent(Status s){
id=s.id;
@@ -14,6 +14,7 @@ public class StatusCountersUpdatedEvent{
replies=s.repliesCount;
favorited=s.favourited;
reblogged=s.reblogged;
bookmarked=s.bookmarked;
pinned=s.pinned;
}
}

View File

@@ -3,9 +3,11 @@ package org.joinmastodon.android.events;
import org.joinmastodon.android.model.Status;
public class StatusCreatedEvent{
public Status status;
public final Status status;
public final String accountID;
public StatusCreatedEvent(Status status){
public StatusCreatedEvent(Status status, String accountID){
this.status=status;
this.accountID=accountID;
}
}

View File

@@ -7,6 +7,7 @@ import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.events.StatusUnpinnedEvent;
import org.joinmastodon.android.model.Account;
@@ -109,4 +110,9 @@ public class AccountTimelineFragment extends StatusListFragment{
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
@Override
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
// no-op
}
}

View File

@@ -444,7 +444,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
error.showToast(getActivity());
}
})
.wrapProgress(getActivity(), R.string.loading, false)
.wrapProgress(getActivity(), R.string.loading, true)
.exec(accountID);
}

View File

@@ -0,0 +1,37 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetBookmarkedStatuses;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.Status;
import me.grishka.appkit.api.SimpleCallback;
public class BookmarkedStatusListFragment extends StatusListFragment{
private String nextMaxID;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setTitle(R.string.bookmarks);
loadData();
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetBookmarkedStatuses(offset==0 ? null : nextMaxID, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
onDataLoaded(result, nextMaxID!=null);
}
})
.exec(accountID);
}
}

View File

@@ -1,53 +0,0 @@
package org.joinmastodon.android.fragments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetBookmarks;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Status;
import java.util.List;
import me.grishka.appkit.api.SimpleCallback;
public class BookmarksListFragment extends StatusListFragment{
private String accountID;
private Account self;
private String lastMaxId=null;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
self=session.self;
setTitle(R.string.bookmarks);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
@Override
protected void doLoadData(int offset, int count) {
GetBookmarks b=new GetBookmarks(offset>0 ? lastMaxId : null, null, count);
currentRequest=b.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, b.getMaxId()!=null);
lastMaxId=b.getMaxId();
}
})
.exec(accountID);
}
}

View File

@@ -28,7 +28,6 @@ import android.text.Layout;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -52,7 +51,6 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.twitter.twittertext.Regex;
import com.twitter.twittertext.TwitterTextEmojiRegex;
import org.joinmastodon.android.E;
@@ -102,7 +100,6 @@ import org.parceler.Parcels;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
@@ -133,21 +130,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
private static final String VALID_URL_PATTERN_STRING =
"(" + // $1 total match
"(" + Regex.URL_VALID_PRECEDING_CHARS + ")" + // $2 Preceding character
"(" + // $3 URL
"(https?://)" + // $4 Protocol (optional)
"(" + Regex.URL_VALID_DOMAIN + ")" + // $5 Domain(s)
"(?::(" + Regex.URL_VALID_PORT_NUMBER + "))?" + // $6 Port number (optional)
"(/" +
Regex.URL_VALID_PATH + "*+" +
")?" + // $7 URL Path and anchor
"(\\?" + Regex.URL_VALID_URL_QUERY_CHARS + "*" + // $8 Query String
Regex.URL_VALID_URL_QUERY_ENDING_CHARS + ")?" +
")" +
")";
private static final Pattern URL_PATTERN=Pattern.compile(VALID_URL_PATTERN_STRING, Pattern.CASE_INSENSITIVE);
@SuppressLint("NewApi") // this class actually exists on 6.0
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
@@ -505,7 +487,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
int visibilityNameRes = switch (statusVisibility) {
case PUBLIC -> R.string.visibility_public;
case UNLISTED -> R.string.visibility_unlisted;
case UNLISTED -> R.string.sk_visibility_unlisted;
case PRIVATE -> R.string.visibility_followers_only;
case DIRECT -> R.string.visibility_private;
};
@@ -521,6 +503,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
replyArrow.setBounds(0, 0, V.dp(20), V.dp(20));
replyText.setCompoundDrawables(replyArrow, null, visibilityIcon, null);
replyText.setOnClickListener(v->{
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("status", Parcels.wrap(replyTo));
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
Nav.go(getActivity(), ThreadFragment.class, args);
});
ArrayList<String> mentions=new ArrayList<>();
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
if(!replyTo.account.id.equals(ownID))
@@ -644,7 +633,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
String countableText=TwitterTextEmojiRegex.VALID_EMOJI_PATTERN.matcher(
MENTION_PATTERN.matcher(
URL_PATTERN.matcher(text).replaceAll("$2xxxxxxxxxxxxxxxxxxxxxxx")
HtmlParser.URL_PATTERN.matcher(text).replaceAll("$2xxxxxxxxxxxxxxxxxxxxxxx")
).replaceAll("$1@$3")
).replaceAll("x");
charCount=0;
@@ -738,7 +727,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
wm.removeView(sendingOverlay);
sendingOverlay=null;
if(editingStatus==null){
E.post(new StatusCreatedEvent(result));
E.post(new StatusCreatedEvent(result, accountID));
if(replyTo!=null){
replyTo.repliesCount++;
E.post(new StatusCountersUpdatedEvent(replyTo));

View File

@@ -0,0 +1,37 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetFavoritedStatuses;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.Status;
import me.grishka.appkit.api.SimpleCallback;
public class FavoritedStatusListFragment extends StatusListFragment{
private String nextMaxID;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setTitle(R.string.your_favorites);
loadData();
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetFavoritedStatuses(offset==0 ? null : nextMaxID, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
onDataLoaded(result, nextMaxID!=null);
}
})
.exec(accountID);
}
}

View File

@@ -1,46 +0,0 @@
package org.joinmastodon.android.fragments;
import android.os.Bundle;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetFavourites;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Status;
import java.util.List;
import me.grishka.appkit.api.SimpleCallback;
public class FavoritesListFragment extends StatusListFragment{
private String accountID;
private String lastMaxId=null;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
setTitle(R.string.favorited_posts);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
@Override
protected void doLoadData(int offset, int count) {
GetFavourites b=new GetFavourites(offset>0 ? lastMaxId : null, null, count);
currentRequest=b.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, b.getMaxId()!=null);
lastMaxId=b.getMaxId();
}
})
.exec(accountID);
}
}

View File

@@ -17,6 +17,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
@@ -50,7 +51,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
private String accountID;
private Map<String, Relationship> relationships=Collections.emptyMap();
private GetAccountRelationships relationshipsRequest;
private String lastMaxId=null;
private String nextMaxID;
public FollowRequestsListFragment(){
super(20);
@@ -66,7 +67,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
setTitle(R.string.follow_requests);
setTitle(R.string.sk_follow_requests);
}
@Override
@@ -75,10 +76,14 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
relationshipsRequest.cancel();
relationshipsRequest=null;
}
currentRequest=new GetFollowRequests(offset>0 ? lastMaxId : null, null, count)
currentRequest=new GetFollowRequests(offset==0 ? null : nextMaxID, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Account> result){
public void onSuccess(HeaderPaginationList<Account> result){
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
onDataLoaded(result.stream().map(AccountWrapper::new).collect(Collectors.toList()), false);
loadRelationships();
}

View File

@@ -57,7 +57,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
setTitle(R.string.app_name);
setTitle(R.string.sk_app_name);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);

View File

@@ -316,7 +316,7 @@ public class HomeTimelineFragment extends StatusListFragment{
private void updateToolbarLogo(){
toolbarLogo =new TextView(getActivity());
toolbarLogo.setText(getString(R.string.app_name).toLowerCase(Locale.getDefault()));
toolbarLogo.setText(getString(R.string.sk_app_name).toLowerCase(Locale.getDefault()));
toolbarLogo.setTextAppearance(R.style.app_title);
toolbarShowNewPostsBtn=new Button(getActivity());
@@ -432,4 +432,9 @@ public class HomeTimelineFragment extends StatusListFragment{
public void onSelfUpdateStateChanged(SelfUpdateStateChangedEvent ev){
updateUpdateState(ev.state);
}
@Override
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
return true;
}
}

View File

@@ -57,7 +57,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
if(args.containsKey("profileAccount")){
profileAccountId=args.getString("profileAccount");
profileDisplayUsername=args.getString("profileDisplayUsername");
setTitle(getString(R.string.lists_with_user, profileDisplayUsername));
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
// setHasOptionsMenu(true);
}
}

View File

@@ -18,6 +18,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
import org.joinmastodon.android.events.FollowRequestHandledEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.ui.SimpleViewHolder;
import org.joinmastodon.android.ui.tabs.TabLayout;
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
@@ -29,8 +30,6 @@ import androidx.viewpager2.widget.ViewPager2;
import com.squareup.otto.Subscribe;
import java.util.List;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -167,9 +166,9 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
}
public void refreshFollowRequestsBadge() {
new GetFollowRequests(null, null, 1).setCallback(new Callback<>() {
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
@Override
public void onSuccess(List<Account> accounts) {
public void onSuccess(HeaderPaginationList<Account> accounts) {
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
}

View File

@@ -8,9 +8,11 @@ import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.NotificationDeletedEvent;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.joinmastodon.android.model.Status;
@@ -27,6 +29,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
@@ -122,6 +125,10 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
.collect(Collectors.toSet());
loadRelationships(needRelationships);
maxID=result.maxID;
if(offset==0 && !result.items.isEmpty()){
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
}
}
});
}
@@ -192,14 +199,23 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
}
@Subscribe
public void onNotificationDeleted(NotificationDeletedEvent ev) {
Notification notification = getNotificationByID(ev.id);
if(notification==null)
public void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
if(!ev.accountID.equals(accountID) || ev.isUnfollow)
return;
data.remove(notification);
List<Notification> toRemove=Stream.concat(data.stream(), preloadedData.stream())
.filter(n->n.account!=null && n.account.id.equals(ev.postsByAccountID))
.collect(Collectors.toList());
for(Notification n:toRemove){
removeNotification(n);
}
}
private void removeNotification(Notification n){
data.remove(n);
preloadedData.remove(n);
int index=-1;
for(int i=0;i<displayItems.size();i++){
if(ev.id.equals(displayItems.get(i).parentID)){
if(n.id.equals(displayItems.get(i).parentID)){
index=i;
break;
}
@@ -208,11 +224,10 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
return;
int lastIndex;
for(lastIndex=index;lastIndex<displayItems.size();lastIndex++){
if(!displayItems.get(lastIndex).parentID.equals(ev.id))
if(!displayItems.get(lastIndex).parentID.equals(n.id))
break;
}
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
}

View File

@@ -183,7 +183,7 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
}
private abstract class BaseViewHolder extends BindableViewHolder<AccountField>{
private ShapeDrawable background=new ShapeDrawable();
protected ShapeDrawable background=new ShapeDrawable();
public BaseViewHolder(int layout){
super(getActivity(), layout, list);
@@ -220,6 +220,20 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
super.onBind(item);
title.setText(item.parsedName);
value.setText(item.parsedValue);
if(item.verifiedAt!=null){
background.getPaint().setColor(UiUtils.isDarkTheme() ? 0xFF49595a : 0xFFd7e3da);
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
value.setTextColor(textColor);
value.setLinkTextColor(textColor);
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_24_regular, getActivity().getTheme()).mutate();
check.setTint(textColor);
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
}else{
background.getPaint().setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
value.setCompoundDrawables(null, null, null, null);
}
}
@Override

View File

@@ -10,7 +10,6 @@ import android.app.Activity;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Outline;
@@ -19,8 +18,6 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ImageSpan;
@@ -252,7 +249,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
tab.setText(switch(position){
case 0 -> R.string.posts;
case 1 -> R.string.posts_and_replies;
case 2 -> R.string.pinned_posts;
case 2 -> R.string.sk_pinned_posts;
case 3 -> R.string.media;
case 4 -> R.string.profile_about;
default -> throw new IllegalStateException();
@@ -286,6 +283,18 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
username.setOnLongClickListener(v->{
String username=account.acct;
if(!username.contains("@")){
username+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
}
getActivity().getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, "@"+username));
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.TIRAMISU){ // Android 13+ SystemUI shows its own thing when you put things into the clipboard
Toast.makeText(getActivity(), R.string.text_copied, Toast.LENGTH_SHORT).show();
}
return true;
});
return sizeWrapper;
}
@@ -453,16 +462,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
// noinspection SetTextI18n
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
}
username.setOnLongClickListener(l->{
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE);
Vibrator v = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
ClipData clip = ClipData.newPlainText("Username", '@'+account.acct+'@'+AccountSessionManager.getInstance().getAccount(accountID).domain);
clipboard.setPrimaryClip(clip);
Toast.makeText(getActivity(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) v.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE));
else v.vibrate(50);
return true;
});
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
if(TextUtils.isEmpty(parsedBio)){
bio.setVisibility(View.GONE);
@@ -548,17 +547,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
if(relationship==null && !isOwnProfile)
return;
inflater.inflate(R.menu.profile, menu);
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.lists_with_user, account.getDisplayUsername()));
if(isOwnProfile){
for(int i=0;i<menu.size();i++){
MenuItem item=menu.getItem(i);
item.setVisible(item.getItemId()==R.id.share || item.getItemId()==R.id.bookmarks || item.getItemId()==R.id.manage_user_lists);
}
menu.findItem(R.id.favorites_list).setVisible(true);
if(isOwnProfile)
return;
}
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
@@ -582,16 +575,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, account.url);
startActivity(Intent.createChooser(intent, item.getTitle()));
}else if(id==R.id.bookmarks) {
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(account));
Nav.go(getActivity(), BookmarksListFragment.class, args);
}else if(id==R.id.favorites_list) {
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(account));
Nav.go(getActivity(), FavoritesListFragment.class, args);
}else if(id==R.id.mute){
confirmToggleMuted();
}else if(id==R.id.block){
@@ -623,6 +606,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
})
.wrapProgress(getActivity(), R.string.loading, false)
.exec(accountID);
}else if(id==R.id.bookmarks){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), BookmarkedStatusListFragment.class, args);
}else if(id==R.id.favorites){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FavoritedStatusListFragment.class, args);
}else if(id==R.id.manage_user_lists){
final Bundle args=new Bundle();
args.putString("account", accountID);
@@ -667,7 +658,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
notifyButton.setSelected(relationship.notifying);
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username));
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
}
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){

View File

@@ -15,7 +15,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageButton;
@@ -36,6 +35,7 @@ import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.PushSubscriptionManager;
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -69,6 +69,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
private NotificationPolicyItem notificationPolicyItem;
private String accountID;
private boolean needUpdateNotificationSettings;
private boolean needAppRestart;
private PushSubscription pushSubscription;
private ImageView themeTransitionWindowView;
@@ -94,7 +95,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new HeaderItem(R.string.settings_theme));
items.add(themeItem=new ThemeItem());
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
items.add(new SwitchItem(R.string.disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
@@ -108,28 +109,33 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.useCustomTabs=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
items.add(new SwitchItem(R.string.sk_settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
GlobalUserPreferences.showInteractionCounts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
GlobalUserPreferences.save();
}));
items.add(new HeaderItem(R.string.home_timeline));
items.add(new SwitchItem(R.string.settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
GlobalUserPreferences.showReplies=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
items.add(new SwitchItem(R.string.sk_settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
GlobalUserPreferences.showBoosts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
GlobalUserPreferences.loadNewPosts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
GlobalUserPreferences.showFederatedTimeline=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new HeaderItem(R.string.settings_notifications));
items.add(notificationPolicyItem=new NotificationPolicyItem());
@@ -146,14 +152,14 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new RedHeaderItem(R.string.settings_spicy));
if (GithubSelfUpdater.needSelfUpdating()) {
checkForUpdateItem = new TextItem(R.string.check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
items.add(checkForUpdateItem);
}
items.add(new TextItem(R.string.settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/mastodon/mastodon-android")));
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon")));
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
items.add(new FooterItem(getString(R.string.settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
}
@Override
@@ -198,9 +204,14 @@ public class SettingsFragment extends MastodonToolbarFragment{
@Override
public void onDestroy(){
super.onDestroy();
if(needUpdateNotificationSettings){
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
}
if(needAppRestart){
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
MastodonApp.context.startActivity(intent);
Runtime.getRuntime().exit(0);
}
}
@Override
@@ -376,7 +387,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
if (ev.state == GithubSelfUpdater.UpdateState.NO_UPDATE) {
Toast.makeText(getActivity(), R.string.no_update_available, Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity(), R.string.sk_no_update_available, Toast.LENGTH_SHORT).show();
}
}
@@ -752,10 +763,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
text.setText(getString(R.string.update_available, info.version));
text.setText(getString(R.string.sk_update_available, info.version));
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
}else{
text.setText(getString(R.string.update_ready, info.version));
text.setText(getString(R.string.sk_update_ready, info.version));
button.setText(R.string.install_update);
}
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){

View File

@@ -6,6 +6,7 @@ import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.events.StatusDeletedEvent;
@@ -18,6 +19,8 @@ import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
@@ -134,6 +137,40 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
return null;
}
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
return false;
}
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
List<Status> toRemove=Stream.concat(data.stream(), preloadedData.stream())
.filter(s->s.account.id.equals(ev.postsByAccountID) || (s.reblog!=null && s.reblog.account.id.equals(ev.postsByAccountID)))
.collect(Collectors.toList());
for(Status s:toRemove){
removeStatus(s);
}
}
protected void removeStatus(Status status){
data.remove(status);
preloadedData.remove(status);
int index=-1;
for(int i=0;i<displayItems.size();i++){
if(status.id.equals(displayItems.get(i).parentID)){
index=i;
break;
}
}
if(index==-1)
return;
int lastIndex;
for(lastIndex=index;lastIndex<displayItems.size();lastIndex++){
if(!displayItems.get(lastIndex).parentID.equals(status.id))
break;
}
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
public class EventListener{
@Subscribe
@@ -165,28 +202,13 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
Status status=getStatusByID(ev.id);
if(status==null)
return;
data.remove(status);
preloadedData.remove(status);
int index=-1;
for(int i=0;i<displayItems.size();i++){
if(ev.id.equals(displayItems.get(i).parentID)){
index=i;
break;
}
}
if(index==-1)
return;
int lastIndex;
for(lastIndex=index;lastIndex<displayItems.size();lastIndex++){
if(!displayItems.get(lastIndex).parentID.equals(ev.id))
break;
}
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
removeStatus(status);
}
@Subscribe
public void onStatusCreated(StatusCreatedEvent ev){
if(!ev.accountID.equals(accountID))
return;
StatusListFragment.this.onStatusCreated(ev);
}
@@ -206,5 +228,14 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
}
}
}
@Subscribe
public void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
if(!ev.accountID.equals(accountID))
return;
if(ev.isUnfollow && !shouldRemoveAccountPostsWhenUnfollowing())
return;
StatusListFragment.this.onRemoveAccountPostsEvent(ev);
}
}
}

View File

@@ -286,7 +286,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.lists_with_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
if(relationship.following){
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));

View File

@@ -1,6 +1,7 @@
package org.joinmastodon.android.fragments.discover;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
@@ -17,6 +18,8 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.fragments.ListTimelinesFragment;
@@ -29,6 +32,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import me.grishka.appkit.fragments.AppKitFragment;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.fragments.OnBackPressedListener;
@@ -58,6 +62,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
private String accountID;
private Runnable searchDebouncer=this::onSearchChangedDebounced;
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
@@ -75,10 +81,11 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
tabLayout=view.findViewById(R.id.tabbar);
pager=view.findViewById(R.id.pager);
tabViews=new FrameLayout[7];
tabViews=new FrameLayout[noFederated ? 6 : 7];
for(int i=0;i<tabViews.length;i++){
FrameLayout tabView=new FrameLayout(getActivity());
tabView.setId(switch(i){
int switchIndex = noFederated && i > 0 ? i + 1 : i;
tabView.setId(switch(switchIndex){
case 0 -> R.id.discover_local_timeline;
case 1 -> R.id.discover_federated_timeline;
case 2 -> R.id.discover_hashtags;
@@ -86,7 +93,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
case 4 -> R.id.discover_news;
case 5 -> R.id.discover_users;
case 6 -> R.id.discover_lists;
default -> throw new IllegalStateException("Unexpected value: "+i);
default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
});
tabView.setVisibility(View.GONE);
view.addView(tabView); // needed so the fragment manager will have somewhere to restore the tab fragment
@@ -131,34 +138,38 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
localTimelineFragment=new LocalTimelineFragment();
localTimelineFragment.setArguments(args);
federatedTimelineFragment=new FederatedTimelineFragment();
federatedTimelineFragment.setArguments(args);
listTimelinesFragment=new ListTimelinesFragment();
listTimelinesFragment.setArguments(args);
getChildFragmentManager().beginTransaction()
FragmentTransaction transaction = getChildFragmentManager().beginTransaction()
.add(R.id.discover_posts, postsFragment)
.add(R.id.discover_local_timeline, localTimelineFragment)
.add(R.id.discover_federated_timeline, federatedTimelineFragment)
.add(R.id.discover_hashtags, hashtagsFragment)
.add(R.id.discover_news, newsFragment)
.add(R.id.discover_users, accountsFragment)
.add(R.id.discover_lists, listTimelinesFragment)
.commit();
.add(R.id.discover_lists, listTimelinesFragment);
if (!noFederated) {
federatedTimelineFragment=new FederatedTimelineFragment();
federatedTimelineFragment.setArguments(args);
transaction.add(R.id.discover_federated_timeline, federatedTimelineFragment);
}
transaction.commit();
}
tabLayoutMediator=new TabLayoutMediator(tabLayout, pager, new TabLayoutMediator.TabConfigurationStrategy(){
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
if (noFederated && position > 0) position++;
tab.setText(switch(position){
case 0 -> R.string.local_timeline;
case 1 -> R.string.federated_timeline;
case 1 -> R.string.sk_federated_timeline;
case 2 -> R.string.hashtags;
case 3 -> R.string.posts;
case 4 -> R.string.news;
case 5 -> R.string.for_you;
case 6 -> R.string.list_timelines;
case 6 -> R.string.sk_list_timelines;
default -> throw new IllegalStateException("Unexpected value: "+position);
});
tab.view.textView.setAllCaps(true);
@@ -280,6 +291,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
private Fragment getFragmentForPage(int page){
if (noFederated && page > 0) page++;
return switch(page){
case 0 -> localTimelineFragment;
case 1 -> federatedTimelineFragment;

View File

@@ -14,6 +14,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
@@ -131,7 +132,10 @@ public class InstanceRulesFragment extends AppKitFragment{
@Override
public void onBind(Instance.Rule item){
title.setText(item.text);
if(item.parsedText==null){
item.parsedText=HtmlParser.parseLinks(item.text);
}
title.setText(item.parsedText);
}
}
}

View File

@@ -11,8 +11,10 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.fragments.MastodonToolbarFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Relationship;
@@ -130,6 +132,7 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
@Override
public void onSuccess(Relationship result){
Nav.finish(ReportDoneFragment.this);
E.post(new RemoveAccountPostsEvent(accountID, reportAccount.id, true));
}
@Override

View File

@@ -134,6 +134,8 @@ public class Instance extends BaseModel{
public static class Rule{
public String id;
public String text;
public transient CharSequence parsedText;
}
@Parcel

View File

@@ -0,0 +1,21 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import java.time.Instant;
@AllFieldsAreRequired
public class Marker extends BaseModel{
public String lastReadId;
public long version;
public Instant updatedAt;
@Override
public String toString(){
return "Marker{"+
"lastReadId='"+lastReadId+'\''+
", version="+version+
", updatedAt="+updatedAt+
'}';
}
}

View File

@@ -13,7 +13,7 @@ public class Poll extends BaseModel{
@RequiredField
public String id;
public Instant expiresAt;
public boolean expired;
private boolean expired;
public boolean multiple;
public int votersCount;
public boolean voted;
@@ -48,6 +48,10 @@ public class Poll extends BaseModel{
'}';
}
public boolean isExpired(){
return expired || (expiresAt!=null && expiresAt.isBefore(Instant.now()));
}
@Parcel
public static class Option{
public String title;

View File

@@ -130,6 +130,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
repliesCount=ev.replies;
favourited=ev.favorited;
reblogged=ev.reblogged;
bookmarked=ev.bookmarked;
pinned=ev.pinned;
}

View File

@@ -45,7 +45,7 @@ public class ImageDescriptionSheet extends BottomSheet{
}
TextView heading=new TextView(activity);
heading.setText(R.string.image_description);
heading.setText(R.string.sk_image_description);
heading.setAllCaps(true);
heading.setTypeface(null, Typeface.BOLD);
heading.setPadding(0, V.dp(24), 0, V.dp(8));

View File

@@ -100,7 +100,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
private void bindButton(TextView btn, long count){
if(GlobalUserPreferences.showInteractionCounts && count>0 && !item.hideCounts){
btn.setText(DecimalFormat.getIntegerInstance().format(count));
btn.setText(UiUtils.abbreviateNumber(count));
btn.setCompoundDrawablePadding(V.dp(8));
}else{
btn.setText("");
@@ -147,7 +147,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
if(id==R.id.favorite_btn)
return R.string.button_favorite;
if(id==R.id.bookmark_btn)
return R.string.button_bookmark;
return R.string.add_bookmark;
if(id==R.id.share_btn)
return R.string.button_share;
return 0;

View File

@@ -5,6 +5,7 @@ import android.app.ProgressDialog;
import android.graphics.Outline;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -209,6 +210,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
});
}else if(id==R.id.block_domain){
UiUtils.confirmToggleBlockDomain(activity, item.parentFragment.getAccountID(), account.getDomain(), relationship!=null && relationship.domainBlocking, ()->{});
}else if(id==R.id.bookmark){
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked);
}
return true;
});
@@ -226,6 +229,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
if(item.hasVisibilityToggle){
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
visibility.setContentDescription(item.parentFragment.getString(item.status.spoilerRevealed ? R.string.hide_content : R.string.reveal_content));
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
visibility.setTooltipText(visibility.getContentDescription());
}
}
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
if(TextUtils.isEmpty(item.extraText)){
@@ -306,6 +312,16 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
MenuItem block=menu.findItem(R.id.block);
MenuItem report=menu.findItem(R.id.report);
MenuItem follow=menu.findItem(R.id.follow);
MenuItem bookmark=menu.findItem(R.id.bookmark);
bookmark.setVisible(false);
/* disabled in megalodon: add/remove bookmark is already available through status footer
if(item.status!=null){
bookmark.setVisible(true);
bookmark.setTitle(item.status.bookmarked ? R.string.remove_bookmark : R.string.add_bookmark);
}else{
bookmark.setVisible(false);
}
*/
if(isOwnPost){
mute.setVisible(false);
block.setVisible(false);

View File

@@ -38,13 +38,13 @@ public class PollFooterStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(PollFooterStatusDisplayItem item){
String text=item.parentFragment.getResources().getQuantityString(R.plurals.x_voters, item.poll.votersCount, item.poll.votersCount);
if(item.poll.expiresAt!=null && !item.poll.expired){
if(item.poll.expiresAt!=null && !item.poll.isExpired()){
text+=" · "+UiUtils.formatTimeLeft(itemView.getContext(), item.poll.expiresAt);
}else if(item.poll.expired){
}else if(item.poll.isExpired()){
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
}
this.text.setText(text);
button.setVisibility(item.poll.expired || item.poll.voted || !item.poll.multiple ? View.GONE : View.VISIBLE);
button.setVisibility(item.poll.isExpired() || item.poll.voted || !item.poll.multiple ? View.GONE : View.VISIBLE);
button.setEnabled(item.poll.selectedOptions!=null && !item.poll.selectedOptions.isEmpty());
}
}

View File

@@ -33,7 +33,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
this.poll=poll;
text=HtmlParser.parseCustomEmoji(option.title, poll.emojis);
emojiHelper.setText(text);
showResults=poll.expired || poll.voted;
showResults=poll.isExpired() || poll.voted;
if(showResults && option.votesCount!=null && poll.votersCount>0){
votesFraction=(float)option.votesCount/(float)poll.votersCount;
int mostVotedCount=0;

View File

@@ -178,7 +178,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
toolbar=uiOverlay.findViewById(R.id.toolbar);
toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0));
imageDescriptionButton = toolbar.getMenu()
.add(R.string.image_description)
.add(R.string.sk_image_description)
.setIcon(R.drawable.ic_fluent_image_alt_text_24_regular)
.setVisible(attachments.get(pager.getCurrentItem()).description != null
&& !attachments.get(pager.getCurrentItem()).description.isEmpty())

View File

@@ -1,9 +1,29 @@
package org.joinmastodon.android.ui.text;
import android.graphics.Typeface;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontStyle;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
import android.util.TypedValue;
import android.widget.TextView;
import com.twitter.twittertext.Regex;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Mention;
@@ -12,11 +32,11 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Safelist;
import org.jsoup.select.NodeVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -26,8 +46,25 @@ import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import me.grishka.appkit.utils.V;
public class HtmlParser{
private static final String TAG="HtmlParser";
private static final String VALID_URL_PATTERN_STRING =
"(" + // $1 total match
"(" + Regex.URL_VALID_PRECEDING_CHARS + ")" + // $2 Preceding character
"(" + // $3 URL
"(https?://)" + // $4 Protocol (optional)
"(" + Regex.URL_VALID_DOMAIN + ")" + // $5 Domain(s)
"(?::(" + Regex.URL_VALID_PORT_NUMBER + "))?" + // $6 Port number (optional)
"(/" +
Regex.URL_VALID_PATH + "*+" +
")?" + // $7 URL Path and anchor
"(\\?" + Regex.URL_VALID_URL_QUERY_CHARS + "*" + // $8 Query String
Regex.URL_VALID_URL_QUERY_ENDING_CHARS + ")?" +
")" +
")";
public static final Pattern URL_PATTERN=Pattern.compile(VALID_URL_PATTERN_STRING, Pattern.CASE_INSENSITIVE);
private static Pattern EMOJI_CODE_PATTERN=Pattern.compile(":([\\w]+):");
private HtmlParser(){}
@@ -49,11 +86,17 @@ public class HtmlParser{
public Object span;
public int start;
public Element element;
public boolean more;
public SpanInfo(Object span, int start, Element element){
this(span, start, element, false);
}
public SpanInfo(Object span, int start, Element element, boolean more){
this.span=span;
this.start=start;
this.element=element;
this.more=more;
}
}
@@ -101,24 +144,59 @@ public class HtmlParser{
openSpans.add(new SpanInfo(new InvisibleSpan(), ssb.length(), el));
}
}
case "li" -> openSpans.add(new SpanInfo(new BulletSpan(V.dp(8)), ssb.length(), el));
case "em", "i" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.ITALIC), ssb.length(), el));
case "h1", "h2", "h3", "h4", "h5", "h6" -> {
// increase line height above heading (multiplying the margin)
if (node.previousSibling()!=null) ssb.setSpan(new RelativeSizeSpan(2), ssb.length() - 1, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (!node.nodeName().equals("h1")) {
openSpans.add(new SpanInfo(new StyleSpan(Typeface.BOLD), ssb.length(), el));
}
openSpans.add(new SpanInfo(new RelativeSizeSpan(switch(node.nodeName()) {
case "h1" -> 1.5f;
case "h2" -> 1.25f;
case "h3" -> 1.125f;
default -> 1;
}), ssb.length(), el, !node.nodeName().equals("h1")));
}
case "strong", "b" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.BOLD), ssb.length(), el));
case "u" -> openSpans.add(new SpanInfo(new UnderlineSpan(), ssb.length(), el));
case "s", "del" -> openSpans.add(new SpanInfo(new StrikethroughSpan(), ssb.length(), el));
case "sub", "sup" -> {
openSpans.add(new SpanInfo(node.nodeName().equals("sub") ? new SubscriptSpan() : new SuperscriptSpan(), ssb.length(), el));
openSpans.add(new SpanInfo(new RelativeSizeSpan(0.8f), ssb.length(), el, true));
}
case "code", "pre" -> openSpans.add(new SpanInfo(new TypefaceSpan("monospace"), ssb.length(), el));
case "blockquote" -> openSpans.add(new SpanInfo(new LeadingMarginSpan.Standard(V.dp(10)), ssb.length(), el));
}
}
}
final static List<String> blockElements = Arrays.asList("p", "ul", "ol", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6");
@Override
public void tail(@NonNull Node node, int depth){
if(node instanceof Element el){
processOpenSpan(el);
if("span".equals(el.nodeName()) && el.hasClass("ellipsis")){
ssb.append("", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}else if("p".equals(el.nodeName())){
if(node.nextSibling()!=null)
ssb.append("\n\n");
}else if(!openSpans.isEmpty()){
SpanInfo si=openSpans.get(openSpans.size()-1);
if(si.element==el){
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
openSpans.remove(openSpans.size()-1);
}
}else if(blockElements.contains(el.nodeName()) && node.nextSibling()!=null){
ssb.append("\n"); // line end
ssb.append("\n", new RelativeSizeSpan(0.75f), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // margin after block
}
}
}
private void processOpenSpan(Element el) {
if(!openSpans.isEmpty()){
SpanInfo si=openSpans.get(openSpans.size()-1);
if(si.element==el){
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
openSpans.remove(openSpans.size()-1);
if(si.more) processOpenSpan(el);
}
if("li".equals(el.nodeName()) && el.nextSibling()!=null) {
ssb.append('\n');
}
}
}
@@ -172,4 +250,18 @@ public class HtmlParser{
public static String strip(String html){
return Jsoup.clean(html, Safelist.none());
}
public static CharSequence parseLinks(String text){
Matcher matcher=URL_PATTERN.matcher(text);
if(!matcher.find()) // Return the original string if there are no URLs
return text;
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
do{
String url=matcher.group(3);
if(TextUtils.isEmpty(matcher.group(4)))
url="http://"+url;
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null), matcher.start(3), matcher.end(3), 0);
}while(matcher.find()); // Find more URLs
return ssb;
}
}

View File

@@ -36,7 +36,7 @@ public class DiscoverInfoBannerHelper{
case TRENDING_HASHTAGS -> R.string.trending_hashtags_info_banner;
case TRENDING_LINKS -> R.string.trending_links_info_banner;
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
case FEDERATED_TIMELINE -> R.string.federated_timeline_info_banner;
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
});
}
}

View File

@@ -48,6 +48,7 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.FollowRequestHandledEvent;
import org.joinmastodon.android.events.NotificationDeletedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusDeletedEvent;
import org.joinmastodon.android.events.StatusUnpinnedEvent;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
@@ -345,6 +346,9 @@ public class UiUtils{
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
if(!currentlyBlocked){
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
}
@Override
@@ -387,6 +391,9 @@ public class UiUtils{
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
if(!currentlyMuted){
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
}
@Override
@@ -403,7 +410,7 @@ public class UiUtils{
}
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback, boolean forRedraft){
showConfirmationAlert(activity, forRedraft ? R.string.confirm_delete_and_redraft_title : R.string.confirm_delete_title, forRedraft ? R.string.confirm_delete_and_redraft : R.string.confirm_delete, forRedraft ? R.string.delete_and_redraft : R.string.delete, ()->{
showConfirmationAlert(activity, forRedraft ? R.string.sk_confirm_delete_and_redraft_title : R.string.confirm_delete_title, forRedraft ? R.string.sk_confirm_delete_and_redraft : R.string.confirm_delete, forRedraft ? R.string.sk_delete_and_redraft : R.string.delete, ()->{
new DeleteStatus(status.id)
.setCallback(new Callback<>(){
@Override
@@ -425,9 +432,9 @@ public class UiUtils{
public static void confirmPinPost(Activity activity, String accountID, Status status, boolean pinned, Consumer<Status> resultCallback){
showConfirmationAlert(activity,
pinned ? R.string.confirm_pin_post_title : R.string.confirm_unpin_post_title,
pinned ? R.string.confirm_pin_post : R.string.confirm_unpin_post,
pinned ? R.string.pin_post : R.string.unpin_post,
pinned ? R.string.sk_confirm_pin_post_title : R.string.sk_confirm_unpin_post_title,
pinned ? R.string.sk_confirm_pin_post : R.string.sk_confirm_unpin_post,
pinned ? R.string.sk_pin_post : R.string.sk_unpin_post,
()->{
new SetStatusPinned(status.id, pinned)
.setCallback(new Callback<>() {
@@ -444,7 +451,7 @@ public class UiUtils{
error.showToast(activity);
}
})
.wrapProgress(activity, pinned ? R.string.pinning : R.string.unpinning, false)
.wrapProgress(activity, pinned ? R.string.sk_pinning : R.string.sk_unpinning, false)
.exec(accountID);
}
);
@@ -500,7 +507,7 @@ public class UiUtils{
public void onSuccess(Relationship result) {
resultCallback.accept(result);
progressCallback.accept(false);
Toast.makeText(activity, activity.getString(result.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username), Toast.LENGTH_SHORT).show();
Toast.makeText(activity, activity.getString(result.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username), Toast.LENGTH_SHORT).show();
}
@Override
@@ -524,6 +531,9 @@ public class UiUtils{
public void onSuccess(Relationship result){
resultCallback.accept(result);
progressCallback.accept(false);
if(!result.following){
E.post(new RemoveAccountPostsEvent(accountID, account.id, true));
}
}
@Override
@@ -660,8 +670,7 @@ public class UiUtils{
public static void openURL(Context context, @Nullable String accountID, String url){
Uri uri=Uri.parse(url);
String accountDomain=accountID != null ? AccountSessionManager.getInstance().getAccount(accountID).domain : null;
if(accountDomain!=null && "https".equals(uri.getScheme()) && accountDomain.equalsIgnoreCase(uri.getAuthority())){
if(accountID!=null && "https".equals(uri.getScheme()) && AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority())){
List<String> path=uri.getPathSegments();
// Match URLs like https://mastodon.social/@Gargron/108132679274083591
if(path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$")){

View File

@@ -8,7 +8,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.DragEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -22,7 +21,6 @@ import androidx.annotation.RequiresApi;
public class ComposeEditText extends EditText{
private SelectionListener selectionListener;
private MediaAcceptingInputConnection inputConnectionWrapper=new MediaAcceptingInputConnection();
public ComposeEditText(Context context){
super(context);
@@ -54,11 +52,10 @@ public class ComposeEditText extends EditText{
// Support receiving images from keyboards
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs){
final var ic = super.onCreateInputConnection(outAttrs);
final InputConnection ic=super.onCreateInputConnection(outAttrs);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N_MR1){
outAttrs.contentMimeTypes=selectionListener.onGetAllowedMediaMimeTypes();
inputConnectionWrapper.setTarget(ic);
return inputConnectionWrapper;
return new MediaAcceptingInputConnection(ic);
}
return ic;
}
@@ -106,8 +103,8 @@ public class ComposeEditText extends EditText{
}
private class MediaAcceptingInputConnection extends InputConnectionWrapper{
public MediaAcceptingInputConnection(){
super(null, true);
public MediaAcceptingInputConnection(InputConnection conn){
super(conn, false);
}
@RequiresApi(api=Build.VERSION_CODES.N_MR1)

View File

@@ -0,0 +1,70 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import me.grishka.appkit.utils.V;
public class SplashLogoView extends ImageView{
private Bitmap shadow;
private Paint paint=new Paint();
public SplashLogoView(Context context){
this(context, null);
}
public SplashLogoView(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public SplashLogoView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
@Override
protected void onDraw(Canvas canvas){
if(shadow!=null){
paint.setColor(0xBF000000);
canvas.drawBitmap(shadow, 0, 0, paint);
}
super.onDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
if(w!=oldw || h!=oldh)
updateShadow();
}
@Override
public void setImageDrawable(@Nullable Drawable drawable){
super.setImageDrawable(drawable);
updateShadow();
}
private void updateShadow(){
int w=getWidth();
int h=getHeight();
Drawable drawable=getDrawable();
if(w==0 || h==0 || drawable==null)
return;
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
Bitmap temp=Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
shadow=Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
Canvas c=new Canvas(temp);
c.translate(getWidth()/2f-drawable.getIntrinsicWidth()/2f, getHeight()/2f-drawable.getIntrinsicHeight()/2f);
drawable.draw(c);
c=new Canvas(shadow);
Paint paint=new Paint();
paint.setShadowLayer(V.dp(2), 0, 0, 0xff000000);
c.drawBitmap(temp, 0, 0, paint);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,3 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M4 6.748c0-1.243 1.007-2.25 2.25-2.25h9c1.243 0 2.25 1.007 2.25 2.25V21.25c0 0.268-0.143 0.517-0.376 0.65-0.233 0.134-0.52 0.133-0.751-0.002l-5.623-3.28-5.622 3.28c-0.232 0.135-0.519 0.136-0.752 0.002C4.144 21.767 4 21.52 4 21.25V6.748zM15.25 2C17.873 2 20 4.127 20 6.75v11.873c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75V6.751c0-1.796-1.455-3.25-3.25-3.25H6.637S6.75 2.942 7.434 2.42C8 2 8.602 2 8.602 2h6.648z" android:fillColor="@color/fluent_default_icon_tint"/>
<path android:pathData="M4 6.748c0-1.243 1.007-2.25 2.25-2.25h9c1.243 0 2.25 1.007 2.25 2.25V21.25c0 0.268-0.143 0.517-0.376 0.65-0.233 0.134-0.52 0.133-0.751-0.002l-5.623-3.28-5.622 3.28c-0.232 0.135-0.519 0.136-0.752 0.002C4.144 21.767 4 21.52 4 21.25V6.748zm2.25-0.75c-0.414 0-0.75 0.336-0.75 0.75v13.196l4.873-2.842c0.233-0.137 0.522-0.137 0.755 0l4.873 2.842V6.748c0-0.414-0.336-0.75-0.75-0.75h-9zm9-3.998C17.873 2 20 4.127 20 6.75v11.873c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75V6.751c0-1.796-1.455-3.25-3.25-3.25H6.637S6.75 2.942 7.434 2.42C8 2 8.602 2 8.602 2h6.648z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M4.53 12.97c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l4.5 4.5c0.293 0.293 0.767 0.293 1.06 0l11-11c0.293-0.293 0.293-0.767 0-1.06-0.293-0.293-0.767-0.293-1.06 0L8.5 16.94l-3.97-3.97z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="257dp"
android:height="67dp"
android:viewportWidth="313"
android:viewportHeight="81">
<path
android:pathData="M72.95,18.45C71.82,9.95 64.5,3.24 55.85,1.95C54.38,1.73 48.85,0.93 36.02,0.93H35.92C23.09,0.93 20.34,1.73 18.87,1.95C10.44,3.22 2.76,9.23 0.88,17.84C-0,22.08 -0.1,26.78 0.07,31.09C0.31,37.27 0.36,43.43 0.91,49.6C1.29,53.69 1.95,57.74 2.9,61.73C4.68,69.11 11.86,75.25 18.9,77.75C26.43,80.35 34.53,80.79 42.29,79C43.14,78.79 43.98,78.56 44.82,78.29C46.71,77.68 48.92,77 50.55,75.81C50.57,75.8 50.59,75.77 50.6,75.75C50.61,75.72 50.62,75.7 50.62,75.66V69.7C50.62,69.7 50.62,69.65 50.6,69.62C50.6,69.6 50.57,69.58 50.55,69.56C50.53,69.55 50.5,69.54 50.48,69.53C50.45,69.53 50.43,69.53 50.41,69.53C45.43,70.73 40.33,71.34 35.23,71.33C26.43,71.33 24.06,67.09 23.39,65.34C22.85,63.82 22.5,62.22 22.36,60.61C22.36,60.59 22.36,60.57 22.37,60.54C22.37,60.52 22.39,60.49 22.42,60.48C22.44,60.47 22.46,60.46 22.49,60.44H22.57C27.46,61.64 32.48,62.25 37.51,62.25C38.72,62.25 39.92,62.25 41.14,62.21C46.19,62.06 51.52,61.81 56.51,60.82C56.63,60.8 56.76,60.77 56.87,60.75C64.72,59.21 72.19,54.42 72.95,42.27C72.97,41.79 73.04,37.25 73.04,36.76C73.04,35.07 73.58,24.79 72.96,18.48L72.95,18.45Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="36.62"
android:startY="0.93"
android:endX="36.62"
android:endY="80.07"
android:type="linear">
<item android:offset="0" android:color="#FF6364FF"/>
<item android:offset="1" android:color="#FF563ACC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
android:fillColor="#ffffff"/>
</vector>

View File

@@ -35,16 +35,18 @@
android:id="@+id/reply_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="-12dp"
android:paddingHorizontal="16dp"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled"
tools:drawableEnd="@drawable/ic_fluent_earth_20_regular"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="6dp"
android:singleLine="true"
android:ellipsize="end"/>
android:ellipsize="end"
android:background="?android:selectableItemBackground"/>
<RelativeLayout
android:layout_width="match_parent"
@@ -185,7 +187,7 @@
android:layout_weight="1"
android:textSize="16sp"
android:singleLine="true"
android:text="@string/mark_media_as_sensitive" />
android:text="@string/sk_mark_media_as_sensitive" />
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -212,6 +214,7 @@
android:tint="@color/compose_button"
android:tintMode="src_in"
android:contentDescription="@string/add_media"
android:tooltipText="@string/add_media"
android:src="@drawable/ic_fluent_image_24_regular"/>
<ImageButton
@@ -224,6 +227,7 @@
android:tint="@color/compose_button"
android:tintMode="src_in"
android:contentDescription="@string/add_poll"
android:tooltipText="@string/add_poll"
android:src="@drawable/ic_fluent_poll_24_selector"/>
<ImageButton
@@ -236,6 +240,7 @@
android:tint="@color/compose_button"
android:tintMode="src_in"
android:contentDescription="@string/emoji"
android:tooltipText="@string/emoji"
android:src="@drawable/ic_fluent_emoji_24_selector"/>
<ImageButton
@@ -248,6 +253,7 @@
android:tint="@color/compose_button"
android:tintMode="src_in"
android:contentDescription="@string/content_warning"
android:tooltipText="@string/content_warning"
android:src="@drawable/ic_fluent_chat_warning_24_selector"/>
<ImageButton
@@ -260,6 +266,7 @@
android:tint="@color/compose_button"
android:tintMode="src_in"
android:contentDescription="@string/post_visibility"
android:tooltipText="@string/post_visibility"
android:src="@drawable/ic_fluent_earth_24_regular"/>
<Space

View File

@@ -73,11 +73,13 @@
android:transformPivotY="1px"
android:background="#478E6A"/>
<ImageView
<org.joinmastodon.android.ui.views.SplashLogoView
android:layout_width="261dp"
android:layout_height="67dp"
android:layout_height="71dp"
android:layout_gravity="center_horizontal|top"
android:layout_marginTop="24dp"
android:scaleType="center"
android:importantForAccessibility="no"
android:src="@drawable/splash_logo"/>
<LinearLayout

View File

@@ -174,7 +174,7 @@
style="?secondaryButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/reject_follow_request"
android:contentDescription="@string/sk_reject_follow_request"
android:drawableStart="@drawable/ic_fluent_dismiss_24_filled"
android:singleLine="true" />
@@ -204,7 +204,7 @@
android:id="@+id/accept_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/accept_follow_request"
android:contentDescription="@string/sk_accept_follow_request"
android:drawableStart="@drawable/ic_fluent_checkmark_24_filled"
android:singleLine="true" />

View File

@@ -14,7 +14,7 @@
android:tint="?android:textColorSecondary"
android:src="@drawable/ic_round_checkbox"/>
<TextView
<org.joinmastodon.android.ui.views.LinkedTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -23,7 +23,7 @@
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:textAppearance="@style/m3_body_medium"
tools:text="@string/update_available"/>
tools:text="@string/sk_update_available"/>
<FrameLayout
android:layout_width="wrap_content"

View File

@@ -5,7 +5,7 @@
android:title="@string/visibility_public"/>
<item android:id="@+id/vis_unlisted"
android:icon="@drawable/ic_fluent_people_community_24_regular"
android:title="@string/visibility_unlisted"/>
android:title="@string/sk_visibility_unlisted"/>
<item android:id="@+id/vis_followers"
android:icon="@drawable/ic_fluent_people_checkmark_24_regular"
android:title="@string/visibility_followers_only"/>

View File

@@ -4,5 +4,5 @@
android:id="@+id/follow_requests"
android:icon="@drawable/ic_follow_requests_24_badged"
android:showAsAction="always"
android:title="@string/follow_requests" />
android:title="@string/sk_follow_requests" />
</menu>

View File

@@ -2,13 +2,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/edit" android:title="@string/edit"/>
<item android:id="@+id/delete" android:title="@string/delete"/>
<item android:id="@+id/delete_and_redraft" android:title="@string/delete_and_redraft"/>
<item android:id="@+id/pin" android:title="@string/pin_post"/>
<item android:id="@+id/unpin" android:title="@string/unpin_post"/>
<item android:id="@+id/delete_and_redraft" android:title="@string/sk_delete_and_redraft"/>
<item android:id="@+id/pin" android:title="@string/sk_pin_post"/>
<item android:id="@+id/unpin" android:title="@string/sk_unpin_post"/>
<item android:id="@+id/mute" android:title="@string/mute_user"/>
<item android:id="@+id/block" android:title="@string/block_user"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
<item android:id="@+id/follow" android:title="@string/follow_user"/>
<item android:id="@+id/report" android:title="@string/report_user"/>
<item android:id="@+id/bookmark" android:title="@string/add_bookmark"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
</menu>

View File

@@ -6,13 +6,6 @@
<item android:id="@+id/report" android:title="@string/report_user"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user"/>
<item android:id="@+id/manage_user_lists" android:title="@string/lists_with_user"/>
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
<item android:id="@+id/favorites_list" android:title="@string/favorited_posts" android:visible="false"/>
<item
android:id="@+id/bookmarks"
android:showAsAction="always"
android:visible="false"
android:icon="@drawable/ic_fluent_bookmark_multiple_24_filled"
android:title="@string/bookmarks"/>
</menu>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular"/>
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
</menu>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="get_started">Пачаць</string>
<string name="log_in">Увайсці</string>
<string name="next">Далей</string>
<string name="error">Памылка</string>
<string name="ok">Добра</string>
<string name="notifications">Апавяшчэнні</string>
<string name="share_toot_title">Абагуліць</string>
<string name="settings">Налады</string>
<string name="cancel">Скасаваць</string>
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<!-- %s is version like 1.2.3 -->
<!-- %s is version like 1.2.3 -->

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -5,15 +5,15 @@
<string name="next">Següent</string>
<string name="loading_instance">Obtenint informació sobre la instància…</string>
<string name="error">Error</string>
<string name="not_a_mastodon_instance">Sembla que %s no és una instància Mastodon.</string>
<string name="not_a_mastodon_instance">Sembla que %s no és una instància de Mastodon.</string>
<string name="ok">D\'acord</string>
<string name="preparing_auth">Preparant a l\'autenticació…</string>
<string name="finishing_auth">Finalitzant autentificació…</string>
<string name="preparing_auth">Preparant l\'autenticació…</string>
<string name="finishing_auth">Finalitzant l\'autenticació…</string>
<string name="user_boosted">%s ha impulsat</string>
<string name="in_reply_to">En resposta a %s</string>
<string name="notifications">Notificacions</string>
<string name="user_followed_you">t\'ha seguit</string>
<string name="user_sent_follow_request">t\'ha enviat una sol·licitud de seguiment</string>
<string name="user_sent_follow_request">ha sol·licitat seguir-te</string>
<string name="user_favorited">ha afavorit la teva publicació</string>
<string name="notification_boosted">ha impulsat la teva publicació</string>
<string name="poll_ended">l\'enquesta ha finalitzat</string>
@@ -24,7 +24,7 @@
<string name="share_toot_title">Comparteix</string>
<string name="settings">Configuració</string>
<string name="publish">Publica</string>
<string name="discard_draft">Voleu descartar l\'esborrany?</string>
<string name="discard_draft">Vols descartar l\'esborrany?</string>
<string name="discard">Descarta</string>
<string name="cancel">Cancel·la</string>
<plurals name="followers">
@@ -49,14 +49,14 @@
<string name="mention_user">Menciona %s</string>
<string name="share_user">Comparteix %s</string>
<string name="mute_user">Silencia %s</string>
<string name="unmute_user">Deixar de silenciar %s</string>
<string name="unmute_user">Deixa de silenciar %s</string>
<string name="block_user">Bloca %s</string>
<string name="unblock_user">Desbloca %s</string>
<string name="report_user">Denuncia %s</string>
<string name="block_domain">Bloca %s</string>
<string name="unblock_domain">Desbloca %s</string>
<plurals name="x_posts">
<item quantity="one">%,d entrada</item>
<item quantity="one">%,d publicació</item>
<item quantity="other">%,d publicacions</item>
</plurals>
<string name="profile_joined">S\'ha unit</string>
@@ -101,12 +101,12 @@
<item quantity="other">%,d votants</item>
</plurals>
<string name="poll_closed">Finalitzada</string>
<string name="confirm_mute_title">Silenciar el compte</string>
<string name="confirm_mute">Confirma per silenciar %s</string>
<string name="do_mute">Silenciar</string>
<string name="confirm_mute_title">Silencia el compte</string>
<string name="confirm_mute">Confirma per a silenciar %s</string>
<string name="do_mute">Silencia</string>
<string name="confirm_unmute_title">Deixar de silenciar el compte</string>
<string name="confirm_unmute">Confirma per deixar de silenciar %s</string>
<string name="do_unmute">Deixar de silenciar</string>
<string name="do_unmute">Deixa de silenciar</string>
<string name="confirm_block_title">Bloca el compte</string>
<string name="confirm_block_domain_title">Bloca el domini</string>
<string name="confirm_block">Confirma per blocar %s</string>
@@ -135,12 +135,12 @@
<string name="all_notifications">Totes</string>
<string name="mentions">Mencions</string>
<plurals name="x_people_talking">
<item quantity="one">%d persona parla</item>
<item quantity="other">%d persones estan parlant</item>
<item quantity="one">%d persona està parlant-ne</item>
<item quantity="other">%d persones estan parlant-ne</item>
</plurals>
<plurals name="discussed_x_times">
<item quantity="one">S\'ha comentat %d cop</item>
<item quantity="other">S\'ha comentat %d cops</item>
<item quantity="one">S\'ha comentat %d vegada</item>
<item quantity="other">S\'ha comentat %d vegades</item>
</plurals>
<string name="report_title">Denuncia %s</string>
<string name="report_choose_reason">Quin és el problema amb aquesta publicació?</string>
@@ -160,7 +160,7 @@
<string name="report_choose_posts_subtitle">Selecciona tots els aplicables</string>
<string name="report_comment_title">Hi ha res més que hauríem de saber?</string>
<string name="report_comment_hint">Comentaris addicionals</string>
<string name="sending_report">Enviant informe…</string>
<string name="sending_report">S\'està enviant l\'informe…</string>
<string name="report_sent_title">Gràcies per informar, ho investigarem.</string>
<string name="report_sent_subtitle">Mentre ho revisem, pots prendre mesures contra %s.</string>
<string name="unfollow_user">Deixar de seguir %s</string>
@@ -176,7 +176,7 @@
<string name="instance_rules_title">Algunes normes bàsiques</string>
<string name="instance_rules_subtitle">Pren un minut per revisar les normes establertes i aplicades pels administradors de %s.</string>
<string name="signup_title">Deixa que et posem en marxa a %s</string>
<string name="edit_photo">editar</string>
<string name="edit_photo">edita</string>
<string name="display_name">nom visible</string>
<string name="username">nom d\'usuari</string>
<string name="email">correu electrònic</string>
@@ -223,9 +223,9 @@
<string name="notification_type_mention">Mencions</string>
<string name="notification_type_poll">Enquestes</string>
<string name="choose_account">Selecciona un compte</string>
<string name="err_not_logged_in">Si us plau, inicia sessió primer a Mastodon</string>
<string name="err_not_logged_in">Primer inicia sessió a Mastodon</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">No pots afegir més de %d fitxer multimèdia</item>
<item quantity="one">No pots afegir més d\'%d fitxer multimèdia</item>
<item quantity="other">No pots afegir més de %d fitxers multimèdia</item>
</plurals>
<string name="media_attachment_unsupported_type">El tipus de fitxer %s no és compatible</string>
@@ -236,8 +236,8 @@
<string name="theme_dark">Fosc</string>
<string name="theme_true_black">Mode negre pur</string>
<string name="settings_behavior">Comportament</string>
<string name="settings_gif">Reproduir emojis i avatar animats</string>
<string name="settings_custom_tabs">Utilitzar el navegador intern</string>
<string name="settings_gif">Reprodueix emojis i avatar animats</string>
<string name="settings_custom_tabs">Utilitza el navegador intern</string>
<string name="settings_notifications">Notificacions</string>
<string name="notify_me_when">Notifica\'m quan</string>
<string name="notify_anyone">qualsevol</string>
@@ -259,7 +259,7 @@
<string name="media_cache_cleared">S\'ha esborrat la memòria cau multimèdia</string>
<string name="confirm_log_out">Segur que vols tancar la sessió?</string>
<string name="sensitive_content">Contingut sensible</string>
<string name="sensitive_content_explain">L\'autor va marcar aquest mitjà com a sensible. Toca per a mostrar-lo.</string>
<string name="sensitive_content_explain">L\'autor ha marcat aquest mitjà com a sensible. Toca per a mostrar-lo.</string>
<string name="media_hidden">Toca per a mostrar</string>
<string name="avatar_description">Vés al perfil de: %s</string>
<string name="more_options">Més opcions</string>
@@ -368,7 +368,7 @@
<string name="file_size_gb">%.2f GB</string>
<string name="file_upload_progress">%1$s de %2$s</string>
<string name="file_upload_time_remaining">%s restants</string>
<string name="upload_error_connection_lost">El teu dispositiu ha perdut la connexió a internet</string>
<string name="upload_error_connection_lost">El dispositiu ha perdut la connexió a Internet</string>
<string name="upload_processing">S\'està processant…</string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Mastodon per a Android %s està preparat per a baixar-se.</string>
@@ -380,9 +380,7 @@
<string name="privacy_policy_title">Mastodon i la teva privacitat</string>
<string name="privacy_policy_subtitle">Tot i que l\'aplicació Mastodon no recull cap dada, el servidor mitjançant el qual et registres pot tenir una política diferent. Pren un minut per revisar i acceptar la política de privadesa de l\'aplicació Mastodon i la política de privadesa del teu servidor.</string>
<string name="i_agree">Dacord</string>
<!-- Missing strings -->
<string name="bookmarks">Marcadors</string>
<string name="pinned_posts">Fixat</string>
<string name="empty_list">Aquesta llista està buida</string>
<string name="instance_signup_closed">Aquest servidor no accepta nous registres.</string>
<string name="bookmarks">Marcadors</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_pinned_posts">Fixat</string>
</resources>

View File

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="get_started">Začít</string>
<string name="get_started">Začínáme</string>
<string name="log_in">Přihlásit se</string>
<string name="next">Další</string>
<string name="loading_instance">Získávání informací o instanci…</string>
<string name="error">Chyba</string>
<string name="not_a_mastodon_instance">Zdá se, že %s není instancí Mastodonu.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Příprava na ověřování</string>
<string name="finishing_auth">Dokončení ověřování</string>
<string name="preparing_auth">Příprava na autentizaci</string>
<string name="finishing_auth">Dokončení autentizace</string>
<string name="user_boosted">Uživatel %s boostnul</string>
<string name="in_reply_to">V odpovědi na %s</string>
<string name="notifications">Upozornění</string>
<string name="user_followed_you">vás sleduje</string>
<string name="user_followed_you">vás začal(a) sledovat</string>
<string name="user_sent_follow_request">vám poslal(a) žádost o sledování</string>
<string name="user_favorited">si oblíbil(a) váš příspěvek</string>
<string name="notification_boosted">boostnul(a) váš příspěvek</string>
@@ -34,9 +34,9 @@
<item quantity="other">sledujících</item>
</plurals>
<plurals name="following">
<item quantity="one">sledovaní</item>
<item quantity="few">sledová</item>
<item quantity="many">sledování</item>
<item quantity="one">sledovaný</item>
<item quantity="few">sledova</item>
<item quantity="many">sledovaných</item>
<item quantity="other">sledovaných</item>
</plurals>
<plurals name="posts">
@@ -52,10 +52,10 @@
<string name="button_follow">Sledovat</string>
<string name="button_following">Sleduji</string>
<string name="edit_profile">Upravit profil</string>
<string name="mention_user">Zmínit @%s</string>
<string name="mention_user">Zmínit %s</string>
<string name="share_user">Sdílet %s</string>
<string name="mute_user">Skrýt %s</string>
<string name="unmute_user">Odkrýt @%s</string>
<string name="unmute_user">Zrušit skrytí @%s</string>
<string name="block_user">Blokovat %s</string>
<string name="unblock_user">Odblokovat %s</string>
<string name="report_user">Nahlásit %s</string>
@@ -67,14 +67,14 @@
<item quantity="many">%,d příspěvků</item>
<item quantity="other">%,d příspěvků</item>
</plurals>
<string name="profile_joined">Účet vytvořen</string>
<string name="profile_joined">Připojen/a</string>
<string name="done">Hotovo</string>
<string name="loading">Načítání…</string>
<string name="field_label">Označení</string>
<string name="field_content">Obsah</string>
<string name="saving">Ukládání…</string>
<string name="post_from_user">Příspěvek od %s</string>
<string name="poll_option_hint">Možnost %d</string>
<string name="poll_option_hint">Volba %d</string>
<plurals name="x_minutes">
<item quantity="one">%d minuta</item>
<item quantity="few">%d minuty</item>
@@ -108,7 +108,7 @@
</plurals>
<plurals name="x_hours_left">
<item quantity="one">Zbývá %d hodina</item>
<item quantity="few">Zbývá %d hodiny</item>
<item quantity="few">Zbývají %d hodiny</item>
<item quantity="many">Zbývá %d hodin</item>
<item quantity="other">Zbývá %d hodin</item>
</plurals>
@@ -129,20 +129,20 @@
<string name="confirm_mute">Potvrdit skrytí %s</string>
<string name="do_mute">Skrýt</string>
<string name="confirm_unmute_title">Zrušit skrytí účtu</string>
<string name="confirm_unmute">Potvrďte zrušení skrytí %s</string>
<string name="do_unmute">Odkrýt</string>
<string name="confirm_unmute">Potvrdit zrušení skrytí %s</string>
<string name="do_unmute">Zrušit skrytí</string>
<string name="confirm_block_title">Blokovat účet</string>
<string name="confirm_block_domain_title">Blokovat doménu</string>
<string name="confirm_block">Potvrďte blokování %s</string>
<string name="confirm_block">Potvrdit blokování %s</string>
<string name="do_block">Blokovat</string>
<string name="confirm_unblock_title">Odblokovat účet</string>
<string name="confirm_unblock_domain_title">Odblokovat doménu</string>
<string name="confirm_unblock">Potvrďte odblokování %s</string>
<string name="confirm_unblock">Potvrdit odblokování %s</string>
<string name="do_unblock">Odblokovat</string>
<string name="button_muted">Skrytý</string>
<string name="button_blocked">Blokovaný</string>
<string name="action_vote">Hlasovat</string>
<string name="tap_to_reveal">Klepnutím zobraz</string>
<string name="tap_to_reveal">Klepněte pro zobrazení</string>
<string name="delete">Smazat</string>
<string name="confirm_delete_title">Smazat příspěvek</string>
<string name="confirm_delete">Opravdu chcete smazat tento příspěvek?</string>
@@ -165,42 +165,42 @@
<item quantity="other">%d lidí mluví</item>
</plurals>
<plurals name="discussed_x_times">
<item quantity="one">Diskutováno %d krát</item>
<item quantity="few">Diskutováno %d krát</item>
<item quantity="many">Diskutováno %d krát</item>
<item quantity="other">Diskutováno %d krát</item>
<item quantity="one">Diskutováno %dkrát</item>
<item quantity="few">Diskutováno %dkrát</item>
<item quantity="many">Diskutováno %dkrát</item>
<item quantity="other">Diskutováno %dkrát</item>
</plurals>
<string name="report_title">Nahlásit %s</string>
<string name="report_choose_reason">Co je na tomto příspěvku špatně?</string>
<string name="report_choose_reason_account">Co je špatně na %s?</string>
<string name="report_choose_reason_subtitle">Vyberte nejbližší možnost</string>
<string name="report_reason_personal">Nelíbí se mi</string>
<string name="report_reason_personal">Nelíbí se mi to</string>
<string name="report_reason_personal_subtitle">Není to něco, co chcete vidět</string>
<string name="report_reason_spam">Je to spam</string>
<string name="report_reason_spam_subtitle">Škodlivé odkazy, falešné interakce nebo opakované odpovědi</string>
<string name="report_reason_violation">Porušuje pravidla serveru</string>
<string name="report_reason_violation_subtitle">Máte za to, že porušuje konkrétní pravidla</string>
<string name="report_reason_other">Jde o něco jiného</string>
<string name="report_reason_violation">Porušuje to pravidla serveru</string>
<string name="report_reason_violation_subtitle">Máte za to, že to porušuje konkrétní pravidla</string>
<string name="report_reason_other">It\'s something else</string>
<string name="report_reason_other_subtitle">Problém neodpovídá ostatním kategoriím</string>
<string name="report_choose_rule">Která pravidla porušuje?</string>
<string name="report_choose_rule">Která pravidla to porušuje?</string>
<string name="report_choose_rule_subtitle">Vyberte všechna relevantní</string>
<string name="report_choose_posts">Existují příspěvky dokládající toto hlášení?</string>
<string name="report_choose_posts_subtitle">Vyberte všechna relevantní</string>
<string name="report_choose_posts_subtitle">Vyberte všechny relevantní</string>
<string name="report_comment_title">Je ještě něco jiného, co bychom měli vědět?</string>
<string name="report_comment_hint">Dodatečné komentáře</string>
<string name="report_comment_hint">Další komentáře</string>
<string name="sending_report">Odesílání hlášení…</string>
<string name="report_sent_title">Děkujeme za nahlášení, podíváme se na to.</string>
<string name="report_sent_subtitle">Zatímco to posuzujeme, můžete podniknout kroky proti %s.</string>
<string name="report_sent_subtitle">Zatímco to posuzujeme, můžete podniknout akce proti %s.</string>
<string name="unfollow_user">Přestat sledovat %s</string>
<string name="unfollow">Přestat sledovat</string>
<string name="mute_user_explain">Neuvidíte jejich příspěvky nebo boostnutí v domovském kanálu. Nebudou vědět, že jsou skrytí.</string>
<string name="block_user_explain">Už nebudou moci sledovat nebo vidět vaše příspěvky, ale mohou vidět, že byli blokováni.</string>
<string name="mute_user_explain">Neuvidíte příspěvky nebo boosty tohoto uživatele ve svém domovském kanálu. Nebude vědět, že je skryt.</string>
<string name="block_user_explain">Tento uživatel vás již nebude moci sledovat ani vidět vaše příspěvky, ale může zjistit, že je blokován.</string>
<string name="report_personal_title">Nechcete tohle vidět?</string>
<string name="report_personal_subtitle">Když uvidíte něco, co se vám nelíbí na Mastodonu, můžete odstranit tuto osobu ze svého zážitku.</string>
<string name="back">Zpět</string>
<string name="instance_catalog_title">Mastodon tvoří uživatelé z různých serverů.</string>
<string name="instance_catalog_subtitle">Vyberte si server podle na svých zájmů, regionu nebo obecného účelu. Stále se můžete spojit se všemi bez ohledu na server.</string>
<string name="search_communities">Hledat nebo zadat URL</string>
<string name="search_communities">Hledat server nebo zadat URL</string>
<string name="instance_rules_title">Některá základní pravidla</string>
<string name="instance_rules_subtitle">Udělejte si chvíli čas a zkontrolujte pravidla, která admini %s nastavili a vynucují.</string>
<string name="signup_title">Pojďme si nastavit %s</string>
@@ -227,19 +227,19 @@
<string name="confirm_email_subtitle">Klepněte na odkaz, který jsme vám poslali e-mailem, abyste účet ověřili.</string>
<string name="resend">Poslat znovu</string>
<string name="open_email_app">Otevřít e-mailovou aplikaci</string>
<string name="resent_email">Potvrzující e-mail odeslán</string>
<string name="resent_email">Potvrzující e-mail byl odeslán</string>
<string name="compose_hint">Napište nebo vložte, co máte na mysli</string>
<string name="content_warning">Varování o obsahu</string>
<string name="add_image_description">Přidat popis obrázku…</string>
<string name="retry_upload">Opakovat nahrání</string>
<string name="edit_image">Upravit obrázek</string>
<string name="save">Uložit</string>
<string name="add_alt_text">Přidat alt text</string>
<string name="alt_text_subtitle">Alt text popisuje obrázky pro lidi se špatným nebo žádným zrakem. Pokuste se zahrnout jen tolik obsahu, kolik je potřeba pro pochopení kontextu.</string>
<string name="alt_text_hint">Např. podezřívavě rozhlížející se pes se zúženýma očima namířenýma na kameru.</string>
<string name="visibility_public">Veřejný</string>
<string name="add_alt_text">Přidat alternativní text</string>
<string name="alt_text_subtitle">Alternativní text popisuje obrázky pro lidi se špatným zrakem nebo bez zraku. Pokuste se zahrnout jen tolik obsahu, kolik je potřeba pro pochopení kontextu.</string>
<string name="alt_text_hint">např. Podezřívavě rozhlížející se pes se zúženýma očima namířenýma na kameru.</string>
<string name="visibility_public">Veřejné</string>
<string name="visibility_followers_only">Pouze sledující</string>
<string name="visibility_private">Pouze lidé, které zmíním</string>
<string name="visibility_private">Pouze zmínění lidé</string>
<string name="search_all">Vše</string>
<string name="search_people">Lidé</string>
<string name="recent_searches">Nedávná hledání</string>
@@ -247,53 +247,53 @@
<string name="skip">Přeskočit</string>
<string name="notification_type_follow">Noví sledující</string>
<string name="notification_type_favorite">Oblíbené</string>
<string name="notification_type_reblog">Boostnutí</string>
<string name="notification_type_reblog">Boosty</string>
<string name="notification_type_mention">Zmínky</string>
<string name="notification_type_poll">Ankety</string>
<string name="choose_account">Vybrat účet</string>
<string name="err_not_logged_in">Nejprve se přihlaste do Mastodonu</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">Nelze přidat více než %d multimediálních příloh</item>
<item quantity="one">Nelze přidat více než %d multimediální přílohu</item>
<item quantity="few">Nelze přidat více než %d multimediální přílohy</item>
<item quantity="many">Nelze přidat více než %d multimediálních příloh</item>
<item quantity="other">Nelze přidat více než %d multimediálních příloh</item>
</plurals>
<string name="media_attachment_unsupported_type">Soubor %s nepatří mezi podporované typy</string>
<string name="media_attachment_too_big">Soubor %1$s překračuje limit velikosti %2$s MB</string>
<string name="settings_theme">Vizuální podoba</string>
<string name="settings_theme">Vizuální vzhled</string>
<string name="theme_auto">Automatická</string>
<string name="theme_light">Světlá</string>
<string name="theme_dark">Tmavá</string>
<string name="theme_light">Světlý</string>
<string name="theme_dark">Tmavý</string>
<string name="theme_true_black">Režim skutečně černé</string>
<string name="settings_behavior">Chování</string>
<string name="settings_gif">Přehrávat animované avatary a emoji</string>
<string name="settings_custom_tabs">Používat interní prohlížeč</string>
<string name="settings_notifications">Upozorně</string>
<string name="settings_custom_tabs">Používat prohlížeč v aplikaci</string>
<string name="settings_notifications">Oznáme</string>
<string name="notify_me_when">Upozornit mě, když</string>
<string name="notify_anyone">kdokoliv</string>
<string name="notify_follower">sledující</string>
<string name="notify_followed">někdo, koho sleduji</string>
<string name="notify_none">nikoho</string>
<string name="notify_favorites">Oblíbil si můj příspěvek</string>
<string name="notify_follow">Sleduje mě</string>
<string name="notify_reblog">Boostnul můj příspěvek</string>
<string name="notify_mention">Zmiňuje</string>
<string name="notify_favorites">Oblíbí si můj příspěvek</string>
<string name="notify_follow">Začne mě sledovat</string>
<string name="notify_reblog">Boostne můj příspěvek</string>
<string name="notify_mention">Zmíní</string>
<string name="settings_boring">Nudná část</string>
<string name="settings_account">Nastavení účtu</string>
<string name="settings_contribute">Přispějte do Mastodonu</string>
<string name="settings_tos">Podmínky používání</string>
<string name="settings_tos">Podmínky užití</string>
<string name="settings_privacy_policy">Zásady ochrany osobních údajů</string>
<string name="settings_spicy">Ostrá část</string>
<string name="settings_clear_cache">Vymazat mezipaměť médií</string>
<string name="settings_app_version">Mastodon pro Android v%1$s (%2$d)</string>
<string name="media_cache_cleared">Mezipaměť médií vymazána</string>
<string name="settings_app_version">Mastodon for Android v%1$s (%2$d)</string>
<string name="media_cache_cleared">Mezipaměť médií byla vymazána</string>
<string name="confirm_log_out">Opravdu se chcete odhlásit?</string>
<string name="sensitive_content">Citlivý obsah</string>
<string name="sensitive_content_explain">Autor označil toto médium za citlivé. Klepnutím zobrazíte.</string>
<string name="media_hidden">Klepnutím zobrazit</string>
<string name="avatar_description">Jít na profil %s</string>
<string name="sensitive_content_explain">Autor označil toto médium jako citlivé. Klepnutím ho zobrazíte.</string>
<string name="media_hidden">Klepněte pro zobrazení</string>
<string name="avatar_description">Přejít na profil uživatele %s</string>
<string name="more_options">Více možností</string>
<string name="reveal_content">Zobrazit obsah</string>
<string name="reveal_content">Odhalit obsah</string>
<string name="hide_content">Skrýt obsah</string>
<string name="new_post">Nový příspěvek</string>
<string name="button_reply">Odpovědět</string>
@@ -309,34 +309,34 @@
<string name="my_profile">Můj profil</string>
<string name="media_viewer">Prohlížeč médií</string>
<string name="follow_user">Sledovat %s</string>
<string name="unfollowed_user">Sledování %s ukončeno</string>
<string name="unfollowed_user">Sledování %s bylo zrušeno</string>
<string name="followed_user">Nyní sledujete %s</string>
<string name="open_in_browser">Otevřít v prohlížeči</string>
<string name="hide_boosts_from_user">Skrýt boosty od %s</string>
<string name="show_boosts_from_user">Zobrazit boosty od %s</string>
<string name="signup_reason">proč se chcete připojit?</string>
<string name="signup_reason">proč se chcete zaregistrovat?</string>
<string name="signup_reason_note">Toto nám pomůže posoudit vaši žádost.</string>
<string name="clear">Vyčistit</string>
<string name="profile_header">Obrázek v záhlaví</string>
<string name="profile_picture">Profilová fotografie</string>
<string name="profile_picture">Profilový obrázek</string>
<string name="reorder">Změnit pořadí</string>
<string name="download">Stáhnout</string>
<string name="permission_required">Vyžadováno oprávnění</string>
<string name="permission_required">Je vyžadováno oprávnění</string>
<string name="storage_permission_to_download">Aplikace potřebuje přístup k vašemu úložišti, aby mohla uložit tento soubor.</string>
<string name="open_settings">Otevřít nastavení</string>
<string name="error_saving_file">Chyba při ukládání souboru</string>
<string name="file_saved">Soubor uložen</string>
<string name="error_saving_file">Nastala chyba při ukládání souboru</string>
<string name="file_saved">Soubor byl uložen</string>
<string name="downloading">Stahování…</string>
<string name="no_app_to_handle_action">Nebyly nalezeny žádné aplikace pro tuto úlohu</string>
<string name="local_timeline">Komunita</string>
<string name="trending_posts_info_banner">Toto jsou příspěvky, které získávají pozornost ve vašem koutu Mastodonu.</string>
<string name="trending_hashtags_info_banner">Toto jsou hashtagy, které získávají pozornost ve vašem koutu Mastodonu.</string>
<string name="trending_links_info_banner">Toto jsou zprávy, které jsou nejvíce sdíleny ve vašem koutu Mastodonu.</string>
<string name="trending_posts_info_banner">Tyto příspěvky získávají na popularitě ve vašem koutu Mastodonu.</string>
<string name="trending_hashtags_info_banner">Tyto hashtagy získávají na popularitě ve vašem koutu Mastodonu.</string>
<string name="trending_links_info_banner">Tyto zprávy jsou nejvíce sdíleny ve vašem koutu Mastodonu.</string>
<string name="local_timeline_info_banner">Toto jsou nejnovější příspěvky od lidí, kteří používají stejný server Mastodonu jako vy.</string>
<string name="dismiss">Zavřít</string>
<string name="see_new_posts">Zobrazit nové příspěvky</string>
<string name="load_missing_posts">Načíst chybějící příspěvky</string>
<string name="follow_back">Sledovat zpátky</string>
<string name="follow_back">Sledovat nazpět</string>
<string name="button_follow_pending">Čekající</string>
<string name="follows_you">Sleduje vás</string>
<string name="manually_approves_followers">Ručně schvaluje sledující</string>
@@ -351,34 +351,34 @@
</plurals>
<plurals name="x_following">
<item quantity="one">%,d sledující</item>
<item quantity="few">%,d sledová</item>
<item quantity="many">%,d sledování</item>
<item quantity="other">%,d sledování</item>
<item quantity="few">%,d sledova</item>
<item quantity="many">%,d sledovaných</item>
<item quantity="other">%,d sledovaných</item>
</plurals>
<plurals name="x_favorites">
<item quantity="one">%,d oblíbený</item>
<item quantity="few">%,d oblíbené</item>
<item quantity="many">%,d oblíbených</item>
<item quantity="other">%,d oblíbených</item>
<item quantity="one">%,d oblíbení</item>
<item quantity="few">%,d oblíbení</item>
<item quantity="many">%,d oblíbení</item>
<item quantity="other">%,d oblíbení</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="one">%,d boostnul</item>
<item quantity="few">%,d boostnuli</item>
<item quantity="many">%,d boostnulo</item>
<item quantity="other">%,d boostnulo</item>
<item quantity="few">%,d boosty</item>
<item quantity="many">%,d boostů</item>
<item quantity="other">%,d boostů</item>
</plurals>
<string name="timestamp_via_app">%1$s přes %2$s</string>
<string name="time_now">teď</string>
<string name="post_info_reblogs">Reblogy</string>
<string name="post_info_favorites">Oblíbené</string>
<string name="edit_history">Historie změn</string>
<string name="post_info_reblogs">Boosty</string>
<string name="post_info_favorites">Oblíbení</string>
<string name="edit_history">Historie úprav</string>
<string name="last_edit_at_x">Poslední úprava %s</string>
<string name="time_just_now">právě teď</string>
<plurals name="x_seconds_ago">
<item quantity="one">Před 1 vteřinou</item>
<item quantity="few">Před %d vteřinami</item>
<item quantity="many">Před %d vteřinami</item>
<item quantity="other">Před %d vteřinami</item>
<item quantity="one">před %d sekundou</item>
<item quantity="few">před %d sekundami</item>
<item quantity="many">před %d sekundami</item>
<item quantity="other">před %d sekundami</item>
</plurals>
<plurals name="x_minutes_ago">
<item quantity="one">před %d minutou</item>
@@ -388,21 +388,21 @@
</plurals>
<string name="edited_timestamp">upraveno %s</string>
<string name="edit_original_post">Původní příspěvek</string>
<string name="edit_text_edited">Text upraven</string>
<string name="edit_spoiler_added">Upozorněna obsah bylo přidáno</string>
<string name="edit_spoiler_edited">Upozorněna obsah upraveno</string>
<string name="edit_spoiler_removed">Upozorněna obsah odstraněno</string>
<string name="edit_poll_added">Anketa přidána</string>
<string name="edit_poll_edited">Anketa upravena</string>
<string name="edit_poll_removed">Anketa odstraněna</string>
<string name="edit_text_edited">Text byl upraven</string>
<string name="edit_spoiler_added">Varováo obsahu bylo přidáno</string>
<string name="edit_spoiler_edited">Varováo obsahu bylo upraveno</string>
<string name="edit_spoiler_removed">Varováo obsahu bylo odebráno</string>
<string name="edit_poll_added">Anketa byla přidána</string>
<string name="edit_poll_edited">Anketa byla upravena</string>
<string name="edit_poll_removed">Anketa byla odebrána</string>
<string name="edit_media_added">Média přidána</string>
<string name="edit_media_removed">Média odstraněna</string>
<string name="edit_media_reordered">Média přeřazena</string>
<string name="edit_media_reordered">Pořadí médií bylo změněno</string>
<string name="edit_marked_sensitive">Označeno jako citlivé</string>
<string name="edit_marked_not_sensitive">Označeno, že není citlivé</string>
<string name="edit_multiple_changed">Příspěvek upraven</string>
<string name="edit_multiple_changed">Příspěvek byl upraven</string>
<string name="edit">Upravit</string>
<string name="discard_changes">Zrušit změny?</string>
<string name="discard_changes">Zahodit změny?</string>
<string name="upload_failed">Nahrávání se nezdařilo</string>
<string name="file_size_bytes">%d bajtů</string>
<string name="file_size_kb">%.2f KB</string>
@@ -413,7 +413,7 @@
<string name="upload_error_connection_lost">Vaše zařízení ztratilo připojení k internetu</string>
<string name="upload_processing">Zpracovávání…</string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Mastodon pro Android %s je připravený ke stažení.</string>
<string name="update_available">Mastodon pro Android %s je připraven ke stažení.</string>
<!-- %s is version like 1.2.3 -->
<string name="update_ready">Mastodon pro Android %s je stažený a připravený k instalaci.</string>
<!-- %s is file size -->
@@ -423,4 +423,10 @@
<string name="privacy_policy_subtitle">Ačkoliv aplikace Mastodon neshromažďuje žádná data, server, na kterém se registrujete, může mít jiné zásady. Věnujte minutu kontrole a odsouhlasení zásad soukromí aplikace Mastodon a vašeho serveru.</string>
<string name="i_agree">Souhlasím</string>
<string name="empty_list">Tento seznam je prázdný</string>
<string name="instance_signup_closed">Tento server nepřijímá nové registrace.</string>
<string name="text_copied">Zkopírováno do schránky</string>
<string name="add_bookmark">Přidat do záložek</string>
<string name="remove_bookmark">Odstranit ze záložek</string>
<string name="bookmarks">Záložky</string>
<string name="your_favorites">Vaše oblíbení</string>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -3,12 +3,12 @@
<string name="get_started">Loslegen</string>
<string name="log_in">Anmelden</string>
<string name="next">Weiter</string>
<string name="loading_instance">Instanzinformationen werden geladen…</string>
<string name="loading_instance">Instanzinformationen werden geladen </string>
<string name="error">Fehler</string>
<string name="not_a_mastodon_instance">%s scheint keine Mastodon-Instanz zu sein.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Authentifizierung wird vorbereitet…</string>
<string name="finishing_auth">Authentifizierung wird abgeschlossen…</string>
<string name="preparing_auth">Authentifizierung wird vorbereitet </string>
<string name="finishing_auth">Authentifizierung wird abgeschlossen </string>
<string name="user_boosted">%s hat diesen Beitrag geteilt</string>
<string name="in_reply_to">Als Antwort auf %s</string>
<string name="notifications">Benachrichtigungen</string>
@@ -40,7 +40,6 @@
<item quantity="other">Beiträge</item>
</plurals>
<string name="posts">Beiträge</string>
<string name="pinned_posts">Angeheftet</string>
<string name="posts_and_replies">Beiträge und Antworten</string>
<string name="media">Medien</string>
<string name="profile_about">Über</string>
@@ -62,10 +61,10 @@
</plurals>
<string name="profile_joined">Beigetreten</string>
<string name="done">Fertig</string>
<string name="loading">Wird geladen…</string>
<string name="loading">wird geladen </string>
<string name="field_label">Beschriftung</string>
<string name="field_content">Inhalt</string>
<string name="saving">Speichern…</string>
<string name="saving">Speichern </string>
<string name="post_from_user">Beitrag von %s</string>
<string name="poll_option_hint">%d. Auswahl</string>
<plurals name="x_minutes">
@@ -121,20 +120,9 @@
<string name="action_vote">Abstimmen</string>
<string name="tap_to_reveal">Tippen zum Anzeigen</string>
<string name="delete">Löschen</string>
<string name="delete_and_redraft">Löschen und neu erstellen</string>
<string name="confirm_delete_title">Beitrag löschen</string>
<string name="confirm_delete_and_redraft_title">Beitrag löschen und neu erstellen</string>
<string name="confirm_delete">Bist du dir sicher, dass du diesen Beitrag löschen möchtest?</string>
<string name="confirm_delete_and_redraft">Bist du dir sicher, dass du diesen Beitrag löschen und neu erstellen möchtest?</string>
<string name="deleting">Wird gelöscht…</string>
<string name="pin_post">An Profil anheften</string>
<string name="confirm_pin_post_title">Beitrag an Profil anheften</string>
<string name="confirm_pin_post">Möchtest du den Beitrag an dein Profil anheften?</string>
<string name="pinning">Wird angeheftet…</string>
<string name="unpin_post">Von Profil lösen</string>
<string name="confirm_unpin_post_title">Angehefteten Beitrag von Profil lösen</string>
<string name="confirm_unpin_post">Bist du dir sicher, dass du den angehefteten Beitrag von deinem Profil lösen möchtest?</string>
<string name="unpinning">Wird vom Profil gelöst…</string>
<string name="notification_channel_audio_player">Audiowiedergabe</string>
<string name="play">Abspielen</string>
<string name="pause">Pausieren</string>
@@ -214,18 +202,14 @@
<string name="resent_email">Bestätigungs-E-Mail gesendet</string>
<string name="compose_hint">Eintippen oder einfügen, was dir am Herzen liegt</string>
<string name="content_warning">Inhaltwarnung</string>
<string name="add_image_description">Bildbeschreibung hinzufügen…</string>
<string name="add_image_description">Bildbeschreibung hinzufügen </string>
<string name="retry_upload">Hochladen erneut versuchen</string>
<string name="image_description">Bildbeschreibung</string>
<string name="image_upload_failed">Fehler beim Hochladen des Bildes</string>
<string name="video_upload_failed">Fehler beim Hochladen des Videos</string>
<string name="edit_image">Bild bearbeiten</string>
<string name="save">Speichern</string>
<string name="add_alt_text">Bildbeschreibung hinzufügen</string>
<string name="alt_text_subtitle">Die Bildbeschreibung („Alt-Text“) ist eine wichtige Unterstützung für blinde und sehbehinderte Menschen. Beschränke dich bei der Formulierung auf das nötigste, interpretiere nicht und beschreibe nur, was zu sehen ist, damit der Kontext verständlich ist und alle Menschen daran teilhaben können.</string>
<string name="alt_text_hint">z. B. „Eine Giraffe auf einem Dreirad, während sie eine Banane isst.“</string>
<string name="visibility_public">Öffentlich</string>
<string name="visibility_unlisted">Nicht gelistet</string>
<string name="visibility_followers_only">Nur Follower</string>
<string name="visibility_private">Nur erwähnte Profile</string>
<string name="search_all">Alle</string>
@@ -253,11 +237,7 @@
<string name="theme_true_black">Echter Schwarzmodus</string>
<string name="settings_behavior">Verhalten</string>
<string name="settings_gif">Animierte GIFs, Avatare und Emojis abspielen</string>
<string name="settings_show_replies">Antworten anzeigen</string>
<string name="settings_show_boosts">Geteilte Beiträge anzeigen</string>
<string name="settings_load_new_posts">Automatisch neue Beiträge laden</string>
<string name="settings_custom_tabs">In-App-Browser verwenden</string>
<string name="settings_show_interaction_counts">Interaktions-Anzahlen anzeigen</string>
<string name="settings_notifications">Benachrichtigungen</string>
<string name="notify_me_when">Benachrichtige mich, wenn</string>
<string name="notify_anyone">irgendjemand</string>
@@ -275,7 +255,7 @@
<string name="settings_privacy_policy">Datenschutzbestimmungen</string>
<string name="settings_spicy">Gefährliches</string>
<string name="settings_clear_cache">Medienpuffer leeren</string>
<string name="settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="settings_app_version">Mastodon für Android v%1$s (%2$d)</string>
<string name="media_cache_cleared">Medienpuffer geleert</string>
<string name="confirm_log_out">Bist du dir sicher, dass du dich abmelden möchtest?</string>
<string name="sensitive_content">Inhaltswarnung</string>
@@ -292,7 +272,6 @@
<string name="button_share">Teilen</string>
<string name="media_no_description">Medien ohne Beschreibung</string>
<string name="add_media">Medien hinzufügen</string>
<string name="mark_media_as_sensitive">Medien als NSFW markieren</string>
<string name="add_poll">Umfrage hinzufügen</string>
<string name="emoji">Emoji</string>
<string name="post_visibility">Sichtbarkeit des Beitrages</string>
@@ -303,10 +282,8 @@
<string name="unfollowed_user">%s entfolgt</string>
<string name="followed_user">Du folgst nun %s</string>
<string name="open_in_browser">Im Browser öffnen</string>
<string name="hide_boosts_from_user">Geteilte Beiträge von %s ausblenden</string>
<string name="show_boosts_from_user">Geteilte Beiträge von %s anzeigen</string>
<string name="user_post_notifications_on">Benachrichtigungen über Beiträge von %s aktiviert</string>
<string name="user_post_notifications_off">Benachrichtigungen über Beiträge von %s deaktiviert</string>
<string name="hide_boosts_from_user">geteilte Beiträge von %s ausblenden</string>
<string name="show_boosts_from_user">geteilte Beiträge von %s anzeigen</string>
<string name="signup_reason">Weshalb möchtest du beitreten?</string>
<string name="signup_reason_note">Das erleichtert uns die Prüfung deiner Anmeldung.</string>
<string name="clear">Leeren</string>
@@ -319,15 +296,13 @@
<string name="open_settings">Einstellungen öffnen</string>
<string name="error_saving_file">Fehler beim Speichern der Datei</string>
<string name="file_saved">Datei gespeichert</string>
<string name="downloading">Wird heruntergeladen…</string>
<string name="downloading">wird heruntergeladen </string>
<string name="no_app_to_handle_action">Es gibt keine App, um diese Aktion auszuführen</string>
<string name="local_timeline">Lokal</string>
<string name="federated_timeline">Föderation</string>
<string name="trending_posts_info_banner">Dies sind Beiträge, die auf deinem Mastodon-Server gerade angesagt sind.</string>
<string name="trending_hashtags_info_banner">Diese Hashtags sind auf deinem Mastodon-Server gerade angesagt.</string>
<string name="trending_links_info_banner">Diese journalistischen Nachrichten werden auf deinem Mastodon-Server gerade am häufigsten geteilt.</string>
<string name="local_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die denselben Mastodon-Server benutzen.</string>
<string name="federated_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die in der Föderation deines Servers sind.</string>
<string name="dismiss">Verwerfen</string>
<string name="see_new_posts">Neue Beiträge anzeigen</string>
<string name="load_missing_posts">Weitere Beiträge laden</string>
@@ -394,28 +369,18 @@
<string name="file_upload_progress">%1$s von %2$s</string>
<string name="file_upload_time_remaining">%s verbleibend</string>
<string name="upload_error_connection_lost">Dein Gerät hat gerade keinen Zugang zum Internet</string>
<string name="upload_processing">Wird verarbeitet…</string>
<string name="upload_processing">wird verarbeitet </string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Megalodon %s ist zum Herunterladen bereit.</string>
<string name="update_available">Mastodon für Android %s ist zum Herunterladen bereit.</string>
<!-- %s is version like 1.2.3 -->
<string name="update_ready">Megalodon %s wurde heruntergeladen und kann jetzt installiert werden.</string>
<string name="update_ready">Mastodon für Android %s wurde heruntergeladen und kann jetzt installiert werden.</string>
<!-- %s is file size -->
<string name="download_update">(%s) herunterladen</string>
<string name="install_update">Installieren</string>
<string name="check_for_update">Auf Update prüfen</string>
<string name="no_update_available">Kein Update verfügbar</string>
<string name="list_timelines">Listen</string>
<string name="favorited_posts">Favorisierte Beiträge</string>
<string name="follow_requests">Folgeanfragen</string>
<string name="accept_follow_request">Folgeanfrage akzeptieren</string>
<string name="reject_follow_request">Folgeanfrage ablehnen</string>
<string name="lists_with_user">Listen mit %s</string>
<string name="privacy_policy_title">Mastodon und Ihre Privatsphäre</string>
<string name="privacy_policy_subtitle">Obwohl die Megalodon-App keine Daten sammelt, kann der Server, über den Sie sich anmelden, eine andere Richtlinie haben. Nehmen Sie sich eine Minute Zeit, um die Mastodon-Datenschutzrichtlinien und die Datenschutzrichtlinien Ihres Servers zu lesen und zu akzeptieren.</string>
<string name="privacy_policy_subtitle">Obwohl die Mastodon-App keine Daten sammelt, kann der Server, über den Sie sich anmelden, eine andere Richtlinie haben. Nehmen Sie sich eine Minute Zeit, um die Mastodon-Datenschutzrichtlinien und die Datenschutzrichtlinien Ihres Servers zu lesen und zu akzeptieren.</string>
<string name="i_agree">Ich stimme zu</string>
<string name="settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="disable_marquee">Laufschrift in Titelleisten deaktivieren</string>
<string name="copied_to_clipboard">In die Zwischenablage kopiert</string>
<string name="empty_list">Diese Liste ist leer</string>
<string name="instance_signup_closed">Dieser Server akzeptiert keine neuen Registrierungen.</string>
<string name="your_favorites">Deine Favoriten</string>
</resources>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_app_name">Megalodon</string>
<string name="sk_pinned_posts">Angeheftet</string>
<string name="sk_delete_and_redraft">Löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft_title">Beitrag löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft">Bist du dir sicher, dass du diesen Beitrag löschen und neu erstellen möchtest?</string>
<string name="sk_pin_post">An Profil anheften</string>
<string name="sk_confirm_pin_post_title">Beitrag an Profil anheften</string>
<string name="sk_confirm_pin_post">Möchtest du den Beitrag an dein Profil anheften?</string>
<string name="sk_pinning">Wird angeheftet…</string>
<string name="sk_unpin_post">Von Profil lösen</string>
<string name="sk_confirm_unpin_post_title">Angehefteten Beitrag von Profil lösen</string>
<string name="sk_confirm_unpin_post">Bist du dir sicher, dass du den angehefteten Beitrag von deinem Profil lösen möchtest?</string>
<string name="sk_unpinning">Wird vom Profil gelöst…</string>
<string name="sk_image_description">Bildbeschreibung</string>
<string name="sk_visibility_unlisted">Nicht gelistet</string>
<string name="sk_settings_show_replies">Antworten anzeigen</string>
<string name="sk_settings_show_boosts">Geteilte Beiträge anzeigen</string>
<string name="sk_settings_load_new_posts">Automatisch neue Beiträge laden</string>
<string name="sk_settings_show_interaction_counts">Interaktions-Anzahlen anzeigen</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Medien als sensibel markieren</string>
<string name="sk_user_post_notifications_on">Benachrichtigungen über Beiträge von %s aktiviert</string>
<string name="sk_user_post_notifications_off">Benachrichtigungen über Beiträge von %s deaktiviert</string>
<string name="sk_federated_timeline">Föderation</string>
<string name="sk_federated_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die in der Föderation deines Servers sind.</string>
<string name="sk_update_available">Megalodon %s ist zum Herunterladen bereit.</string>
<string name="sk_update_ready">Megalodon %s wurde heruntergeladen und kann jetzt installiert werden.</string>
<string name="sk_check_for_update">Auf Update prüfen</string>
<string name="sk_no_update_available">Kein Update verfügbar</string>
<string name="sk_list_timelines">Listen</string>
<string name="sk_follow_requests">Folgeanfragen</string>
<string name="sk_accept_follow_request">Folgeanfrage akzeptieren</string>
<string name="sk_reject_follow_request">Folgeanfrage ablehnen</string>
<string name="sk_lists_with_user">Listen mit %s</string>
<string name="sk_settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="sk_disable_marquee">Laufschrift in Titelleisten deaktivieren</string>
<string name="sk_settings_contribute">Zu Megalodon beitragen</string>
<string name="sk_settings_show_federated_timeline">Föderierte Timeline anzeigen</string>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_pinned_posts">Anclado</string>
<string name="sk_delete_and_redraft">Eliminar y editar</string>
<string name="sk_confirm_delete_and_redraft_title">Eliminar y editar post</string>
<string name="sk_confirm_delete_and_redraft">Seguro que quiere eliminar y volver a editar este post\?</string>
<string name="sk_pin_post">Fijar en perfil</string>
<string name="sk_confirm_pin_post_title">Fijar post en perfil</string>
<string name="sk_confirm_pin_post">Desea fijar el post en su perfil\?</string>
<string name="sk_pinning">Fijando post…</string>
<string name="sk_unpin_post">Quitar del perfil</string>
<string name="sk_confirm_unpin_post_title">Quitar post del perfil</string>
<string name="sk_confirm_unpin_post">Está seguro que quiere quitar el post\?</string>
<string name="sk_app_name">Megalodon</string>
<string name="sk_unpinning">Quitando post…</string>
<string name="sk_image_description">Descripción de la imagen</string>
<string name="sk_visibility_unlisted">Sin listar</string>
<string name="sk_settings_show_replies">Mostrar respuestas</string>
<string name="sk_settings_show_boosts">Mostrar boosts</string>
<string name="sk_settings_load_new_posts">Cargar nuevos posts automáticamente</string>
<string name="sk_settings_show_interaction_counts">Mostrar contadores de interacciones</string>
<string name="sk_mark_media_as_sensitive">Marcar medio como sensible</string>
<string name="sk_user_post_notifications_on">Activadas las notificaciones de posts para %s</string>
<string name="sk_user_post_notifications_off">Desactivadas las notificaciones de posts para %s</string>
<string name="sk_federated_timeline">Federación</string>
<string name="sk_federated_timeline_info_banner">Estos son los posts más recientes de las personas de tu federación.</string>
<string name="sk_update_available">Megalodon %s está listo para descargar.</string>
<string name="sk_update_ready">Megalodon %s se ha descargado y está listo para instalarse.</string>
<string name="sk_check_for_update">Buscar actualizaciones</string>
<string name="sk_no_update_available">No hay actualizaciones disponibles</string>
<string name="sk_list_timelines">Listas</string>
<string name="sk_follow_requests">Solicitudes de seguimiento</string>
<string name="sk_accept_follow_request">Aceptar solicitud de seguimiento</string>
<string name="sk_reject_follow_request">Rechazar solicitud de seguimiento</string>
<string name="sk_lists_with_user">Listas con %s</string>
<string name="sk_settings_always_reveal_content_warnings">Mostrar siempre advertencias de contenido</string>
<string name="sk_disable_marquee">Desactivar desplazamiento de texto en barras del título</string>
<string name="sk_settings_contribute">Contribuir a Megalodon</string>
<string name="sk_settings_show_federated_timeline">Mostrar el timeline federado</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -382,4 +382,9 @@
<string name="i_agree">Jaccepte</string>
<string name="empty_list">Cette liste est vide</string>
<string name="instance_signup_closed">Ce serveur n\'accepte pas les nouvelles inscriptions.</string>
<string name="text_copied">Copié dans le presse-papier</string>
<string name="add_bookmark">Favoris</string>
<string name="remove_bookmark">Retirer des favoris</string>
<string name="bookmarks">Favoris</string>
<string name="your_favorites">Vos favoris</string>
</resources>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_pinned_posts">Épinglé</string>
<string name="sk_confirm_delete_and_redraft_title">Supprimer et reformuler le message</string>
<string name="sk_confirm_delete_and_redraft">Voulez-vous vraiment supprimer et reformuler ce message \?</string>
<string name="sk_confirm_pin_post">Voulez-vous épingler ce message à votre profil \?</string>
<string name="sk_pinning">Épinglage de la publication…</string>
<string name="sk_unpin_post">Détacher du profil</string>
<string name="sk_confirm_unpin_post_title">Détacher la publication du profil</string>
<string name="sk_unpinning">Détachement de la publication…</string>
<string name="sk_image_description">Description de l\'image</string>
<string name="sk_visibility_unlisted">Non répertorié</string>
<string name="sk_settings_show_replies">Afficher les réponses</string>
<string name="sk_settings_show_boosts">Afficher les boosts</string>
<string name="sk_settings_load_new_posts">Charger automatiquement les nouvelles publications</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Marquer le média comme sensible</string>
<string name="sk_user_post_notifications_on">Notifications de publication activées pour %s</string>
<string name="sk_user_post_notifications_off">Désactivation des notifications de publication pour %s</string>
<string name="sk_update_available">Megalodon %s est prêt à être téléchargé.</string>
<string name="sk_update_ready">Megalodon %s est téléchargé et prêt à être installé.</string>
<string name="sk_check_for_update">Vérifier les mises à jour</string>
<string name="sk_no_update_available">Pas de mise a jour disponible</string>
<string name="sk_list_timelines">Listes</string>
<string name="sk_follow_requests">Suivre les demandes</string>
<string name="sk_accept_follow_request">Accepter la demande de suivi</string>
<string name="sk_reject_follow_request">Refuser la demande de suivi</string>
<string name="sk_lists_with_user">Listes avec %s</string>
<string name="sk_settings_always_reveal_content_warnings">Toujours afficher les avertissements de contenu</string>
<string name="sk_disable_marquee">Désactiver le défilement du texte dans les barres de titre</string>
<string name="sk_settings_contribute">Contribuez à Megalodon</string>
<string name="sk_settings_show_federated_timeline">Afficher la timeline fédérée</string>
<string name="sk_app_name">Megalodon</string>
<string name="sk_delete_and_redraft">Supprimer et reformuler</string>
<string name="sk_pin_post">Épingler au profil</string>
<string name="sk_confirm_pin_post_title">Épingler la publication au profil</string>
<string name="sk_confirm_unpin_post">Êtes-vous sûr de vouloir détacher cette publication \?</string>
<string name="sk_settings_show_interaction_counts">Afficher le nombre d\'interactions</string>
<string name="sk_federated_timeline">Fédération</string>
<string name="sk_federated_timeline_info_banner">Ce sont les publications les plus récentes des membres de votre fédération.</string>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

Some files were not shown because too many files have changed in this diff Show More