Compare commits
239 Commits
feature/ca
...
v1.2.3+for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4baaa39f35 | ||
|
|
52f025ae5a | ||
|
|
14b805e883 | ||
|
|
433a7b15fe | ||
|
|
6c8cbbc34a | ||
|
|
d4fbb298c1 | ||
|
|
2aeb5f03d6 | ||
|
|
6522403c37 | ||
|
|
f090ca7f75 | ||
|
|
2f02a238df | ||
|
|
0d5fa97800 | ||
|
|
b102deaee1 | ||
|
|
968b2ee460 | ||
|
|
890340de94 | ||
|
|
4ca1a7b29e | ||
|
|
5432f2590c | ||
|
|
60ccf5cf0a | ||
|
|
bc717f5b10 | ||
|
|
486eef21dd | ||
|
|
44a4d02815 | ||
|
|
336a8194bd | ||
|
|
7859f4cd05 | ||
|
|
37622ba9ce | ||
|
|
7a6af89375 | ||
|
|
056bfaacfe | ||
|
|
6684311ec5 | ||
|
|
11943571ad | ||
|
|
f696fcd412 | ||
|
|
2919e109ca | ||
|
|
995f478708 | ||
|
|
fb8764bcd7 | ||
|
|
d7f73e02c5 | ||
|
|
e897b3af57 | ||
|
|
e04fd8a004 | ||
|
|
ada70ae1b5 | ||
|
|
5fdec0900e | ||
|
|
56a93288c4 | ||
|
|
02e3421f98 | ||
|
|
fdbf331432 | ||
|
|
aed86ac6f0 | ||
|
|
3a13d4d6c0 | ||
|
|
f5336564d0 | ||
|
|
1ce49c68fe | ||
|
|
d37e880993 | ||
|
|
6fdb81a01f | ||
|
|
f9d6827572 | ||
|
|
10bf72b9ff | ||
|
|
800f929a15 | ||
|
|
bfcff1e19f | ||
|
|
f373e7df3e | ||
|
|
3985de5b14 | ||
|
|
e175a721d4 | ||
|
|
d9784ebc31 | ||
|
|
f241092277 | ||
|
|
0702703d78 | ||
|
|
2c4504bad3 | ||
|
|
07ca5a8b77 | ||
|
|
798a43906f | ||
|
|
41cb0f2e09 | ||
|
|
e12c0fb81f | ||
|
|
ac39f119e2 | ||
|
|
016faf3df0 | ||
|
|
b2d6879282 | ||
|
|
6926a212f4 | ||
|
|
89afc05d5c | ||
|
|
936f39161b | ||
|
|
ee20ee0722 | ||
|
|
02f9f8c8ea | ||
|
|
de3a252884 | ||
|
|
5e7a00de3e | ||
|
|
2858aeb55e | ||
|
|
357104efa9 | ||
|
|
bb8027c7ef | ||
|
|
f9dd787009 | ||
|
|
e005731ba6 | ||
|
|
18ae3f4f61 | ||
|
|
10dfe0327e | ||
|
|
1d1e921137 | ||
|
|
0985a4c968 | ||
|
|
8df589c103 | ||
|
|
71b6b2f451 | ||
|
|
d85940ded8 | ||
|
|
e9e491c0b0 | ||
|
|
c73562fb75 | ||
|
|
3feacb59c8 | ||
|
|
a033d711c1 | ||
|
|
32081b71f5 | ||
|
|
7849c34d1f | ||
|
|
24977ec613 | ||
|
|
786bbab0d5 | ||
|
|
1facb07c28 | ||
|
|
bba5aba22d | ||
|
|
d7b85d6eba | ||
|
|
6832bfb95c | ||
|
|
4c379b67a3 | ||
|
|
3a2ae1ce71 | ||
|
|
c80afaf9c0 | ||
|
|
31d22bac47 | ||
|
|
b5f6687925 | ||
|
|
b3f25af923 | ||
|
|
78c141e946 | ||
|
|
83d36ce736 | ||
|
|
b928357ff1 | ||
|
|
c074bc57bc | ||
|
|
0e80c88b7d | ||
|
|
5ffa5b01fc | ||
|
|
61d9929485 | ||
|
|
231f19d113 | ||
|
|
bb41f62db5 | ||
|
|
47edc3180b | ||
|
|
9939d99c4b | ||
|
|
8053e8bb05 | ||
|
|
b7e9380bc4 | ||
|
|
83600087e1 | ||
|
|
fe84dc4823 | ||
|
|
c38eb545b1 | ||
|
|
1fc2f81dab | ||
|
|
69ddc95c2c | ||
|
|
a6ac68499c | ||
|
|
c10d7cfee4 | ||
|
|
f933bdbc53 | ||
|
|
274bca84d9 | ||
|
|
6abfe6ddd7 | ||
|
|
ab7489a049 | ||
|
|
a6fd6ae135 | ||
|
|
b30d4a025f | ||
|
|
5b747bfc74 | ||
|
|
a410d19114 | ||
|
|
a8589cc5b0 | ||
|
|
b057c9f7a8 | ||
|
|
96e4a4933c | ||
|
|
630064500d | ||
|
|
9543294996 | ||
|
|
56e9cc3406 | ||
|
|
be569cbe72 | ||
|
|
99f0817bdb | ||
|
|
220cd35d82 | ||
|
|
07f4ef1697 | ||
|
|
f20732ddc2 | ||
|
|
b1e0dc5843 | ||
|
|
285eb25706 | ||
|
|
ec556511e6 | ||
|
|
85c3d9f65f | ||
|
|
a7ebadf269 | ||
|
|
94c09d46c2 | ||
|
|
f6f08d176c | ||
|
|
66cdd63496 | ||
|
|
8b502b605c | ||
|
|
2c0ec28803 | ||
|
|
2e1795dc6f | ||
|
|
cd1be782fa | ||
|
|
67059f3d71 | ||
|
|
15f4d3326b | ||
|
|
a9ab9cb249 | ||
|
|
e65404a466 | ||
|
|
3d47d1b4db | ||
|
|
d1749ab610 | ||
|
|
806c264686 | ||
|
|
34a9cb5a74 | ||
|
|
64fad2e871 | ||
|
|
961c69b525 | ||
|
|
c70f393559 | ||
|
|
9abdc174f4 | ||
|
|
2e5bfa1d9c | ||
|
|
9c89c26097 | ||
|
|
e3b6a5d389 | ||
|
|
0fb54efde5 | ||
|
|
a4a3f32dba | ||
|
|
03a1e29e0c | ||
|
|
eda9ff272b | ||
|
|
b3728e06ac | ||
|
|
33cbd85e19 | ||
|
|
8cb1f3f387 | ||
|
|
3f0c6fcec5 | ||
|
|
797cf893da | ||
|
|
a3564b70e1 | ||
|
|
43004307b8 | ||
|
|
acd1e4ced3 | ||
|
|
6717070f93 | ||
|
|
387499ae49 | ||
|
|
8ab140c55d | ||
|
|
914abb95dd | ||
|
|
5360c0f0f7 | ||
|
|
243d803b51 | ||
|
|
b343fe3835 | ||
|
|
3c42c1120f | ||
|
|
ad840dcef6 | ||
|
|
f73072d95e | ||
|
|
95cb9b5079 | ||
|
|
c6684d3c9b | ||
|
|
5c5989d8c0 | ||
|
|
60e92d30b0 | ||
|
|
8bf8e3f86b | ||
|
|
891ee2d06b | ||
|
|
b450bc7ae8 | ||
|
|
4ca1e0d5db | ||
|
|
859213dd9e | ||
|
|
ad2857791d | ||
|
|
497827f2e2 | ||
|
|
967e333022 | ||
|
|
8df1406006 | ||
|
|
4af42fafdc | ||
|
|
a9e6a452c1 | ||
|
|
a4a4632397 | ||
|
|
421f39e414 | ||
|
|
f8121e2dc4 | ||
|
|
b1784fc51c | ||
|
|
96db0d7de7 | ||
|
|
3837ed9cb1 | ||
|
|
2be789a43c | ||
|
|
fd8d96169a | ||
|
|
1562dc32c1 | ||
|
|
38f377ca09 | ||
|
|
cc28bba884 | ||
|
|
beb3081918 | ||
|
|
1b3c9106b5 | ||
|
|
385b91761b | ||
|
|
43600756c0 | ||
|
|
3c3e0633ad | ||
|
|
f819ad6917 | ||
|
|
e7ad396fc6 | ||
|
|
b1cb4d4257 | ||
|
|
100bd4b062 | ||
|
|
7da09d9b37 | ||
|
|
f46eb07228 | ||
|
|
7627b5eb25 | ||
|
|
c710448c6b | ||
|
|
1ad270b1d6 | ||
|
|
099e253b2b | ||
|
|
66de4a5b91 | ||
|
|
41437d91d5 | ||
|
|
d33d5a6efa | ||
|
|
4f9248d040 | ||
|
|
f40c0e41f3 | ||
|
|
deeb03ff2b | ||
|
|
5c2a09e243 | ||
|
|
2473c999db | ||
|
|
ea2cc265e3 | ||
|
|
a0cd2d42cf |
3
.github/FUNDING.yml
vendored
@@ -1,8 +1,9 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: LucasGGamerM
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # mastodon
|
||||
open_collective: # Replace with a single Open Collective username e.g., user1
|
||||
ko_fi: xsk22
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username e.g., user1
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,6 +22,8 @@ Steps to reproduce the behavior:
|
||||
**Does this happen in the official app?**
|
||||
|
||||
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) or at least using the current Mastodon version from the Play Store)
|
||||
|
||||
> No / Yes
|
||||
|
||||
> In case it does, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead.
|
||||
@@ -33,7 +35,7 @@ If applicable, add screenshots (and screen recordings, if possible) to help expl
|
||||
|
||||
**Version**
|
||||
|
||||
Moshidon version: [e.g. v1.1.4+fork.#]
|
||||
Megalodon version: [e.g. v1.1.4+fork.#]
|
||||
|
||||
**Crash log**
|
||||
|
||||
|
||||
48
.github/workflows/nightly-builds.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: Nightly builds
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'corretto'
|
||||
cache: gradle
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Decode Keystore
|
||||
id: decode_keystore
|
||||
uses: timheuer/base64-to-file@v1
|
||||
with:
|
||||
fileName: 'nightly_keystore.jks'
|
||||
fileDir: './mastodon/keystore/'
|
||||
encodedString: ${{ secrets.KEYSTORE }}
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew assembleNightly
|
||||
env:
|
||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||
CURRENT_DATE: ${{ steps.date.outputs.date }}
|
||||
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: moshidon-nightly.apk
|
||||
path: ./mastodon/build/outputs/apk/nightly/moshidon-nightly.apk
|
||||
2
.gitignore
vendored
@@ -9,5 +9,3 @@
|
||||
.cxx
|
||||
local.properties
|
||||
*.jks
|
||||
*.keystore
|
||||
/mastodon/keystore/nightly_keystore.keystore
|
||||
|
||||
5
FAQ.md
@@ -1,5 +0,0 @@
|
||||
## F.A.Q
|
||||
|
||||
Q: What are the main differences between Moshidon and Megalodon?
|
||||
|
||||
A: There are many, but the most outstanding differences are: the ability to have other server's local timeline inside the app. It can be acessed in the "Add community" option in the top right corner of the Edit timelines screen. Other outstanding features that Moshidon has are some quality of life improvements, such as notification actions and allowing for unlisted replies by default. Most other features are pretty minor, such as profile notes directly available in the person's profile. Other features are quite minor usability and visibility improvements. All of which can be found in the settings page.
|
||||
191
README.md
@@ -1,59 +1,25 @@
|
||||

|
||||

|
||||
|
||||
# Moshidon, the material you mastodon client!
|
||||
# Megalodon
|
||||
|
||||
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly won’t ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
|
||||
|
||||
|
||||
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||
|
||||
[](https://github.com/LucasGGamerM/moshidon-nightly/releases/latest/download/moshidon-nightly.apk)
|
||||
|
||||
|
||||
[](https://translate.codeberg.org/engage/moshidon/)
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
[](https://github.com/LucasGGamerM/moshidon/actions/workflows/nightly-builds.yml)
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.moshinda"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||
<a href="#installation"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||
|
||||
## Help out the project by donating at: https://github.com/sponsors/LucasGGamerM!
|
||||
> 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 won’t ever be implemented, such as the federated timeline, unlisted posting and an image description viewer.
|
||||
|
||||
---
|
||||
|
||||
## Key features
|
||||
|
||||
### **The ability to add other server's local timeline to your timelines**
|
||||
|
||||
It can be accessed in the "Edit timelines" menu, where you can add a new "Community" to see other server's local posts!
|
||||
|
||||
### **View remote profiles**
|
||||
|
||||
You can now see all of a profile follows and followers, by directly loading them from the profile's home instance. In case of a failed lookup, the app will automatically fall back to the older method.
|
||||
|
||||
### **Translate posts easily**
|
||||
|
||||
Allows you to easily translate posts in another language with a translate button! Your instance must support translation, otherwise it will not work.
|
||||
|
||||
### **Show posts filtered with a warning**
|
||||
|
||||
Allows you to have filtered posts collapsed with a warning! As shown in the screenshots:
|
||||
|
||||
Before | After
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
|
||||
### **Color themes**
|
||||
|
||||
Allows you to change theme within the app. Supports Material You, purple, pink, green, blue, red, orange, yellow and Nord!
|
||||
|
||||
### **Unlisted posting**
|
||||
|
||||
**Allows you to post publicly without having your post show up in trends, hashtags or public timelines (i.e., in the tabs “Local”, “Community” and “Posts”).**
|
||||
**Allows you to post publicly without having your post show up in trends, hashtags or public timelines (i.e., in the tabs “Community”, “Federated” and “Posts”).**
|
||||
|
||||
When posting with Unlisted visibility, your posts will still be publicly accessible in your profile. They will also be shown in people’s Home timelines, but only if they follow you or someone they follow reposted/replied to your post.
|
||||
When posting with Unlisted visibility, your posts will still be publicly accessible in your profile. They will also be shown in people’s Home timelines, but only if they follow you or someone they follow reblogged/replied to your post.
|
||||
|
||||
The Mastodon documentation has some more information about [Unlisted posting](https://docs.joinmastodon.org/user/posting/#unlisted) and [Public timelines](https://docs.joinmastodon.org/user/network/#timelines).
|
||||
|
||||
@@ -65,55 +31,89 @@ Despite being one of the main features of federated social media, the Federated
|
||||
|
||||
That’s one of the reasons why choosing a small, **well-moderated instance is important**. Instance admins and moderators should always make sure to ban abusive users and stop federating with instances who platform them. On well-moderated instances, the Federated timeline can be a welcoming place to meet new people!
|
||||
|
||||
### **Draft and schedule posts**
|
||||
|
||||
**Allows for preparing a post and scheduling it to send it automatically at a specific time.**
|
||||
|
||||
You can create drafts, edit them, send them manually later or set a scheduled date. Drafts are technically saved as scheduled posts, so you can view and edit them from other apps that support scheduled posts. Scheduled posts are handled by your home instance, so they'll work even if you uninstall Megalodon.
|
||||
|
||||
### **Image description viewer**
|
||||
|
||||
**Allows you to quickly check whether an image or video has an alternative text attached to it.**
|
||||
|
||||
This is important to **ensure the content you’re sharing is as accessible as possible** to people who can’t see the images and rely on software to read back the provided content descriptions. Thankfully, it’s quite common for people on the Fediverse to provide such alt texts, and hopefully things stay this way!
|
||||
|
||||
### **Reminder to add alt text to attached media**
|
||||
|
||||
By default, Moshidon will show a warning to add alt text if your post has any attachments without any alt text. This is for better accessibility, and it can easily be bypassed and disabled in settings.
|
||||
|
||||
### **Pinning posts**
|
||||
|
||||
**This lets you can highlight important posts on your profile. A dedicated “Pinned” tab in people’s profiles shows all the posts they pinned.**
|
||||
|
||||
On the Fediverse, it’s quite common for people to pin posts they want others to read before following them. You can pin/unpin posts yourself by clicking the `⋯` button in the top right corner of your posts.
|
||||
|
||||
### **Bookmarks**
|
||||
|
||||
**They allow for quickly saving posts and viewing them through the Bookmarks button on the top right of your profile.**
|
||||
|
||||
To bookmark a post, press the button between the Favorite and Share buttons on the bottom of the post. Bookmarks are saved privately, so the post authors won’t know you saved their post – the list of bookmarked posts is only visible to you.
|
||||
|
||||
## Installation
|
||||
|
||||
**Press the download button above to download the APK. Open the downloaded file on your Android device to install it. Moshidon will automatically notify you about new updates inside the app.**
|
||||
### IzzyOnDroid
|
||||
|
||||
To install this app on your Android device, download the [latest release from GitHub](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.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/LucasGGamerM/moshidon/releases) page.
|
||||
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
|
||||
|
||||
Moshidon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)’s automatic update checker. Moshidon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||
|
||||
Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first:
|
||||
|
||||
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
|
||||
|
||||
### Google Play Store
|
||||
|
||||
[play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
|
||||
### F-Droid
|
||||
|
||||
**[F-Droid.org?](https://f-droid.org)** Not yet, sorry!
|
||||
|
||||
If you want, you can help me figure out if something's missing in the [Issue #47: F-Droid.org](https://github.com/sk22/megalodon/issues/47)
|
||||
|
||||
### Direct
|
||||
|
||||
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.
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
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.
|
||||
|
||||
Megalodon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)’s automatic update checker. Megalodon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
|
||||
|
||||
---
|
||||
|
||||
Moshidon is also available in [IzzyOnDroid repo](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda), compatible with all F-Droid clients. The APK provided here is the same as the one included in the Releases.
|
||||
|
||||
## Release variants
|
||||
|
||||
### Stable variant
|
||||
All downloads can be found on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
||||
|
||||
All stable version downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||
**`megalodon.apk`**
|
||||
|
||||
**`moshidon.apk`**
|
||||
Variant with an integrated updater. If you download Megalodon from here (and not from an app store), just download the regular `megalodon.apk`.
|
||||
|
||||
Variant with an integrated updater. If you download Moshidon from here (and not from an app store), just download the regular `moshidon.apk`.
|
||||
**`upstream-1234abc.apk`**
|
||||
|
||||
### Nightly variant
|
||||
This is an **unmodified version** of the official [Mastodon for Android](https://github.com/mastodon/mastodon-android) app the respective Megalodon release is based on. Should you find any bugs in Megalodon (which you will), try to see if it occurs with this variant, too. The last 7 digits of the file name are important to know which version of the official app you're using.
|
||||
|
||||
All nightly builds can be downloaded at [Nightly Releases](https://github.com/LucasGGamerM/moshidon-nightly/releases) page.
|
||||
<!-- **`megalodon-fdroid.apk`**
|
||||
|
||||
**`moshidon-nightly.apk`**
|
||||
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/
|
||||
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
Unstable variant with an integrated updater. It's for development and testing purposes. If you find any bugs with it, please file a bug report at our [issues](https://github.com/LucasGGamerM/moshidon/issues) page.
|
||||
|
||||
---
|
||||
|
||||
@@ -122,25 +122,16 @@ Unstable variant with an integrated updater. It's for development and testing pu
|
||||
|
||||
### Features
|
||||
|
||||
* [Adding the ability to view other server's local timelines](https://github.com/LucasGGamerM/moshidon/tree/feature/local-timelines)
|
||||
* [Adding the ability to load followers and following from remote instance](https://github.com/LucasGGamerM/moshidon/tree/feature/remote-followers)
|
||||
* [Adding the ability to have filtered posts show with a warning](https://github.com/LucasGGamerM/moshidon/tree/feature/filters_again)
|
||||
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
||||
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
||||
* Adding a useful private profile note box
|
||||
* Auto hiding the compose button on scroll
|
||||
* Adding the ability to remind yourself to add alt text to images
|
||||
* An indicator for if an image has alt text or not
|
||||
* Adding the ability to have drafts
|
||||
* Also adding the ability to view announcements from your instance
|
||||
* Adding the ability to post for local timeline only (Only on instances that support it!)
|
||||
* [Add “Federation” tab and change Discover tab order](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
|
||||
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
|
||||
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
||||
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
||||
* [Implement a bookmark button and list](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/bookmarks) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/22))
|
||||
* [Add “Check for update” button in addition to integrated update checker](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/check-for-update-button)
|
||||
* [Add “Mark media as sensitive” option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/mark-media-as-sensitive)
|
||||
* [Add settings to hide replies and reposts from the timeline](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
|
||||
* [Add settings to hide replies and reblogs from the timeline](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
|
||||
* [Follow and unfollow hashtags](https://github.com/sk22/megalodon/commit/7d38f031f197aa6cefaf53e39d929538689c1e4e) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/233))
|
||||
* [Notification bell for posts](https://github.com/sk22/megalodon/commit/b166ca705eb9169025ef32bbe6315b42491b57ea) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/81))
|
||||
* [Viewing lists and adding/removing users from lists](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:list-timeline-views) based on [@obstsalatschuessel](https://github.com/obstsalatschuessel)'s [Pull request](https://github.com/mastodon/mastodon-android/pull/286)
|
||||
@@ -150,13 +141,23 @@ Unstable variant with an integrated updater. It's for development and testing pu
|
||||
* [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)
|
||||
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
|
||||
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
|
||||
* [Add option to allow voting for multiple options on polls](https://github.com/sk22/megalodon/commit/5b28468efd49387b4f8b83f142f3adf3104ca60c)
|
||||
* [Add translate function](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/translate-button)
|
||||
* [Add language selector](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/language-selector)
|
||||
* [Implement deleting notifications](https://github.com/sk22/megalodon/commit/b0f9ce081f69f29ad59658fc00ca41372cd2677d) (disabled by default)
|
||||
* [Long-click boost button to "quote" a post](https://github.com/sk22/megalodon/commit/b25a237c20c6a924ed4d9b357999867c3a32b32b)
|
||||
* [Draft and schedule posts](https://github.com/sk22/megalodon/pull/217)
|
||||
* [Display original post when replying](https://github.com/sk22/megalodon/commit/375f8ceb2747705fedf43686681cc0e0b812f899)
|
||||
* [Display server announcements](https://github.com/sk22/megalodon/commit/84179bc207d6b69cc2a770a3c28fa0a39b0b54e8)
|
||||
* [Create](https://github.com/sk22/megalodon/commit/294595513a45037359b31377aafc25ae5b58d8e7), [edit](https://github.com/sk22/megalodon/commit/d47797bf7ac8cff3f9ba1cfee219a1bb2af21da6) and [delete](https://github.com/sk22/megalodon/commit/54c29fd787fc2cd0dfd2787ad796b8190f795973) lists
|
||||
* [Soft-blocking (by blocking and immediately unblocking)](https://github.com/sk22/megalodon/commit/e75d350b7a2709259e9fc5138e0e1f361bdb0972)
|
||||
* [Pinnable custom timelines](https://github.com/sk22/megalodon/pull/338/commits)
|
||||
* Support for local-only posts
|
||||
|
||||
|
||||
### Behavior
|
||||
|
||||
* Allow for confirmation before reblogging
|
||||
* Adding a bottom option for the publish button, allowing for easier use on larger screens!
|
||||
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
|
||||
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
|
||||
* [Display full image when adding image description](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
|
||||
@@ -164,6 +165,20 @@ Unstable variant with an integrated updater. It's for development and testing pu
|
||||
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/hide-interaction-numbers)
|
||||
* [Option to always reveal content warnings](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/cw-above-text)
|
||||
* [Option to disable scrolling title bars](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/disable-marquee)
|
||||
* [No ellipsis for long poll answers](https://github.com/mastodon/mastodon-android/commit/c9aae828e2518adccdc092e41f8d1f0489636271)
|
||||
* [Show poll vote button for multiple and single answer polls](https://github.com/mastodon/mastodon-android/commit/e14dfda2fdf32f0fa3043504ac5831683a87559a)
|
||||
* [Show own vote after voting](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28) ([Closes issue](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28))
|
||||
* [Make inline emoji search case-insensitive and don't only search from start of emoji names](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:better-inline-emoji-search) ([Pull request](https://github.com/mastodon/mastodon-android/pull/445))
|
||||
* [Include subject line when sharing e.g. a website to Megalodon](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:external-share-include-subject)
|
||||
* [Improve semantics for voting on polls (radio buttons and checkboxes)](https://github.com/sk22/megalodon/commit/6fd58c96827cb1d2da329cebdc170a1425dd18d7)
|
||||
* [Copy post URL when long-pressing share button](https://github.com/sk22/megalodon/commit/ba36347f03278763ecec617b1ce57ba89db7be72)
|
||||
* [Add option to disable swiping between tabs](https://github.com/sk22/megalodon/commit/1f20b21fc84bf006c1ec14bd2229cbfad5215ec8)
|
||||
* [Resolve Fediverse links in the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/open-urls-in-app)
|
||||
* [Preserve whitespaces in HTML](https://github.com/sk22/megalodon/commit/7d876bddc7a07d98f0fecbf62b13bdb9fcce3412)
|
||||
* [Long-click to copy links](https://github.com/sk22/megalodon/commit/b32e32274923a94742a9926ef38785f746d41405)
|
||||
* Improved filtering using Mastodon 4.0 API: [#202](https://github.com/sk22/megalodon/pull/202), [#212](https://github.com/sk22/megalodon/pull/212), [#255](https://github.com/sk22/megalodon/pull/255) by [@thiagojedi](https://github.com/thiagojedi)
|
||||
* [Support admin notifications](https://github.com/sk22/megalodon/commit/c12a6eaee6b609bc53eb0a45d9199f37d5241801) and [notifications for edited reblogged posts](https://github.com/sk22/megalodon/commit/900e8fb2e9353002c16d15e06b78d2731e121601)
|
||||
* [Android file opener added back in addition to image picker](https://github.com/sk22/megalodon/commit/3a6ace53d5ab01e28077c9c930cb6ed487b78031)
|
||||
|
||||
|
||||
### Visual
|
||||
@@ -171,6 +186,16 @@ Unstable variant with an integrated updater. It's for development and testing pu
|
||||
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:compact-extended-footer)
|
||||
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:true-black-improvements)
|
||||
* [Profile header tweaks](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:ui/profile-header-tweaks)
|
||||
* [Custom color themes](https://github.com/sk22/megalodon/pull/124) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom "megalodon" text logo](https://github.com/sk22/megalodon/commit/563afd487ca5c608cfbb00fa3909d3c27384acc0) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom login screen](https://github.com/sk22/megalodon/commit/9bbf8c4618dbe13accaeb3b5482bf3fe88cac4c0)
|
||||
* [More distinct filled boost icon](https://github.com/sk22/megalodon/commits/more-distinct-filled-boost-icon)
|
||||
* Material You color theme by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Animations for interaction buttons](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/animate-buttons)
|
||||
* [Dedicated icons for different notification types](https://github.com/sk22/megalodon/pull/178) by [@florian-obernberger](https://github.com/florian-obernberger)
|
||||
* Scale text according to system settings
|
||||
* Header in timeline for followed hashtags
|
||||
* [Indicator for missing alt texts](https://github.com/sk22/megalodon/commit/c0c276f03e793b78c478c17dfdef24a66ef7cedb)
|
||||
|
||||
|
||||
## Building
|
||||
@@ -187,10 +212,4 @@ This project is released under the [GPL-3 License](./LICENSE).
|
||||
|
||||
## Links
|
||||
|
||||
[F.A.Q](FAQ.md)
|
||||
|
||||
[Official matrix chatroom:](https://matrix.to/#/#moshidon:floss.social) https://matrix.to/#/#moshidon:floss.social
|
||||
|
||||
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>
|
||||
|
||||
---
|
||||
<a rel="me" href="https://floss.social/@megalodon">@megalodon<wbr>@floss.social</a>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: Moshidon
|
||||
title: Megalodon
|
||||
layout: default
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Moshidon</title>
|
||||
<title>Megalodon</title>
|
||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||
<link rel="me" href="https://floss.social/@megalodon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||
@@ -14,4 +14,4 @@
|
||||
{{ content }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -11,69 +11,30 @@ java {
|
||||
android {
|
||||
compileSdk 33
|
||||
defaultConfig {
|
||||
manifestPlaceholders = [oAuthScheme:"moshidon-android-auth"]
|
||||
archivesBaseName = "moshidon"
|
||||
applicationId "org.joinmastodon.android.moshinda"
|
||||
archivesBaseName = "megalodon"
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 100
|
||||
versionName "1.3.0+fork.100.moshinda"
|
||||
versionCode 90
|
||||
versionName "1.2.3+fork.90"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW']
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
nightly{
|
||||
storeFile = file("keystore/nightly_keystore.jks")
|
||||
storePassword System.getenv("SIGNING_STORE_PASSWORD")
|
||||
if (storePassword == null) {
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
storePassword = properties.getProperty('SIGNING_STORE_PASSWORD')
|
||||
}
|
||||
keyAlias System.getenv("SIGNING_KEY_ALIAS")
|
||||
if (keyAlias == null) {
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
keyAlias = properties.getProperty('SIGNING_KEY_ALIAS')
|
||||
}
|
||||
keyPassword System.getenv("SIGNING_KEY_PASSWORD")
|
||||
if (keyPassword == null) {
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
keyPassword = properties.getProperty('SIGNING_KEY_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
// minifyEnabled true
|
||||
// shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug{
|
||||
debuggable true
|
||||
versionNameSuffix '-debug'
|
||||
applicationIdSuffix '.debug'
|
||||
manifestPlaceholders = [oAuthScheme:"moshidon-android-debug-auth"]
|
||||
}
|
||||
githubRelease{
|
||||
initWith release
|
||||
}
|
||||
nightly{
|
||||
initWith release
|
||||
if(System.getenv("CURRENT_DATE") != null){
|
||||
versionNameSuffix '-nightly+@' + System.getenv("CURRENT_DATE")
|
||||
} else {
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
versionNameSuffix '-nightly+@' + properties.getProperty('CURRENT_DATE')
|
||||
}
|
||||
applicationIdSuffix '.nightly'
|
||||
signingConfig signingConfigs.nightly
|
||||
manifestPlaceholders = [oAuthScheme:"moshidon-android-nightly-auth"]
|
||||
}
|
||||
playRelease{
|
||||
initWith release
|
||||
minifyEnabled true
|
||||
@@ -91,7 +52,7 @@ android {
|
||||
setRoot "src/github"
|
||||
}
|
||||
debug {
|
||||
setRoot "src/debug"
|
||||
setRoot "src/github"
|
||||
}
|
||||
}
|
||||
namespace 'org.joinmastodon.android'
|
||||
|
||||
7
mastodon/proguard-rules.pro
vendored
@@ -42,13 +42,6 @@
|
||||
|
||||
-keepattributes LineNumberTable
|
||||
|
||||
-keepattributes *
|
||||
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||
|
||||
#-keep class javax.** { *; }
|
||||
-keep class org.joinmastodon.android.** { *; }
|
||||
|
||||
# Parceler library
|
||||
-keep interface org.parceler.Parcel
|
||||
-keep @org.parceler.Parcel class * { *; }
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class ThreadFragmentTest {
|
||||
|
||||
private Status fakeStatus(String id, String inReplyTo) {
|
||||
Status status = Status.ofFake(id, null, null);
|
||||
status.inReplyToId = inReplyTo;
|
||||
return status;
|
||||
}
|
||||
|
||||
private ThreadFragment.NeighborAncestryInfo fakeInfo(Status s, Status d, Status a) {
|
||||
return new ThreadFragment.NeighborAncestryInfo(s, d, a);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapNeighborhoodAncestry() {
|
||||
StatusContext context = new StatusContext();
|
||||
context.ancestors = List.of(
|
||||
fakeStatus("oldest ancestor", null),
|
||||
fakeStatus("younger ancestor", "oldest ancestor")
|
||||
);
|
||||
Status mainStatus = fakeStatus("main status", "younger ancestor");
|
||||
context.descendants = List.of(
|
||||
fakeStatus("first reply", "main status"),
|
||||
fakeStatus("reply to first reply", "first reply"),
|
||||
fakeStatus("third level reply", "reply to first reply"),
|
||||
fakeStatus("another reply", "main status")
|
||||
);
|
||||
|
||||
List<ThreadFragment.NeighborAncestryInfo> neighbors =
|
||||
ThreadFragment.mapNeighborhoodAncestry(mainStatus, context);
|
||||
|
||||
assertEquals(List.of(
|
||||
fakeInfo(context.ancestors.get(0), context.ancestors.get(1), null),
|
||||
fakeInfo(context.ancestors.get(1), mainStatus, context.ancestors.get(0)),
|
||||
fakeInfo(mainStatus, context.descendants.get(0), context.ancestors.get(1)),
|
||||
fakeInfo(context.descendants.get(0), context.descendants.get(1), mainStatus),
|
||||
fakeInfo(context.descendants.get(1), context.descendants.get(2), context.descendants.get(0)),
|
||||
fakeInfo(context.descendants.get(2), null, context.descendants.get(1)),
|
||||
fakeInfo(context.descendants.get(3), null, null)
|
||||
), neighbors);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMainStatus() {
|
||||
ThreadFragment fragment = new ThreadFragment();
|
||||
fragment.mainStatus = Status.ofFake("123456", "original text", Instant.EPOCH);
|
||||
|
||||
Status update1 = Status.ofFake("123456", "updated text", Instant.EPOCH);
|
||||
update1.editedAt = Instant.ofEpochSecond(1);
|
||||
fragment.updatedStatus = update1;
|
||||
StatusUpdatedEvent event1 = (StatusUpdatedEvent) fragment.updateMainStatus();
|
||||
assertEquals("fired update event", update1, event1.status);
|
||||
assertEquals("updated main status", update1, fragment.mainStatus);
|
||||
|
||||
Status update2 = Status.ofFake("123456", "updated text", Instant.EPOCH);
|
||||
update2.favouritesCount = 123;
|
||||
fragment.updatedStatus = update2;
|
||||
StatusCountersUpdatedEvent event2 = (StatusCountersUpdatedEvent) fragment.updateMainStatus();
|
||||
assertEquals("only fired counter update event", update2.id, event2.id);
|
||||
assertEquals("updated counter is correct", 123, event2.favorites);
|
||||
assertEquals("updated main status", update2, fragment.mainStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortStatusContext() {
|
||||
StatusContext context = new StatusContext();
|
||||
context.ancestors = List.of(
|
||||
fakeStatus("younger ancestor", "oldest ancestor"),
|
||||
fakeStatus("oldest ancestor", null)
|
||||
);
|
||||
context.descendants = List.of(
|
||||
fakeStatus("reply to first reply", "first reply"),
|
||||
fakeStatus("third level reply", "reply to first reply"),
|
||||
fakeStatus("first reply", "main status"),
|
||||
fakeStatus("another reply", "main status")
|
||||
);
|
||||
|
||||
ThreadFragment.sortStatusContext(
|
||||
fakeStatus("main status", "younger ancestor"),
|
||||
context
|
||||
);
|
||||
List<Status> expectedAncestors = List.of(
|
||||
fakeStatus("oldest ancestor", null),
|
||||
fakeStatus("younger ancestor", "oldest ancestor")
|
||||
);
|
||||
List<Status> expectedDescendants = List.of(
|
||||
fakeStatus("first reply", "main status"),
|
||||
fakeStatus("reply to first reply", "first reply"),
|
||||
fakeStatus("third level reply", "reply to first reply"),
|
||||
fakeStatus("another reply", "main status")
|
||||
);
|
||||
|
||||
// TODO: ??? i have no idea how this code works. it certainly doesn't return what i'd expect
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package org.joinmastodon.android.test;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
@@ -12,10 +14,10 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.MainActivity;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.mastodon.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.GetStatusByID;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||
@@ -30,9 +32,12 @@ import org.parceler.Parcels;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.espresso.PerformException;
|
||||
import androidx.test.espresso.UiController;
|
||||
import androidx.test.espresso.ViewAction;
|
||||
@@ -42,15 +47,18 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.screenshot.ScreenCapture;
|
||||
import androidx.test.runner.screenshot.Screenshot;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
import okio.Source;
|
||||
|
||||
import static androidx.test.espresso.Espresso.*;
|
||||
import static androidx.test.espresso.action.ViewActions.*;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.*;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.joinmastodon.android.ui.utils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class UiUtilsTest {
|
||||
@BeforeClass
|
||||
public static void createDummySession() {
|
||||
Instance dummyInstance = new Instance();
|
||||
dummyInstance.uri = "test.tld";
|
||||
Account dummyAccount = new Account();
|
||||
dummyAccount.id = "123456";
|
||||
AccountSessionManager.getInstance().addAccount(dummyInstance, null, dummyAccount, null, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
AccountSessionManager.getInstance().removeAccount("test.tld_123456");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseFediverseHandle() {
|
||||
assertEquals(
|
||||
Optional.of(Pair.create("megalodon", Optional.of("floss.social"))),
|
||||
UiUtils.parseFediverseHandle("megalodon@floss.social")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.of(Pair.create("megalodon", Optional.of("floss.social"))),
|
||||
UiUtils.parseFediverseHandle("@megalodon@floss.social")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.of(Pair.create("megalodon", Optional.empty())),
|
||||
UiUtils.parseFediverseHandle("@megalodon")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.of(Pair.create("megalodon", Optional.of("floss.social"))),
|
||||
UiUtils.parseFediverseHandle("mailto:megalodon@floss.social")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.empty(),
|
||||
UiUtils.parseFediverseHandle("megalodon")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.empty(),
|
||||
UiUtils.parseFediverseHandle("this is not a fedi handle")
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
Optional.empty(),
|
||||
UiUtils.parseFediverseHandle("not@a-domain")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acctMatches() {
|
||||
assertTrue("local account, domain not specified", UiUtils.acctMatches(
|
||||
"test.tld_123456",
|
||||
"someone",
|
||||
"someone",
|
||||
null
|
||||
));
|
||||
|
||||
assertTrue("domain not specified", UiUtils.acctMatches(
|
||||
"test.tld_123456",
|
||||
"someone@somewhere.social",
|
||||
"someone",
|
||||
null
|
||||
));
|
||||
|
||||
assertTrue("local account, domain specified, different casing", UiUtils.acctMatches(
|
||||
"test.tld_123456",
|
||||
"SomeOne",
|
||||
"someone",
|
||||
"Test.TLD"
|
||||
));
|
||||
|
||||
assertFalse("username doesn't match", UiUtils.acctMatches(
|
||||
"test.tld_123456",
|
||||
"someone-else@somewhere.social",
|
||||
"someone",
|
||||
"somewhere.social"
|
||||
));
|
||||
|
||||
assertFalse("domain doesn't match", UiUtils.acctMatches(
|
||||
"test.tld_123456",
|
||||
"someone@somewhere.social",
|
||||
"someone",
|
||||
"somewhere.else"
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.joinmastodon.android.utils;
|
||||
|
||||
import static org.joinmastodon.android.model.Filter.FilterAction.*;
|
||||
import static org.joinmastodon.android.model.Filter.FilterContext.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class StatusFilterPredicateTest {
|
||||
|
||||
private static final Filter hideMeFilter = new Filter(), warnMeFilter = new Filter();
|
||||
private static final List<Filter> allFilters = List.of(hideMeFilter, warnMeFilter);
|
||||
|
||||
private static final Status
|
||||
hideInHomePublic = Status.ofFake(null, "hide me, please", Instant.now()),
|
||||
warnInHomePublic = Status.ofFake(null, "display me with a warning", Instant.now());
|
||||
|
||||
static {
|
||||
hideMeFilter.phrase = "hide me";
|
||||
hideMeFilter.filterAction = HIDE;
|
||||
hideMeFilter.context = EnumSet.of(PUBLIC, HOME);
|
||||
|
||||
warnMeFilter.phrase = "warning";
|
||||
warnMeFilter.filterAction = WARN;
|
||||
warnMeFilter.context = EnumSet.of(PUBLIC, HOME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHide() {
|
||||
assertFalse("should not pass because matching filter applies to given context",
|
||||
new StatusFilterPredicate(allFilters, HOME).test(hideInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHideRegardlessOfContext() {
|
||||
assertTrue("filters without context should always pass",
|
||||
new StatusFilterPredicate(allFilters, null).test(hideInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHideInDifferentContext() {
|
||||
assertTrue("should pass because matching filter does not apply to given context",
|
||||
new StatusFilterPredicate(allFilters, THREAD).test(hideInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHideWithWarningText() {
|
||||
assertTrue("should pass because matching filter is for warnings",
|
||||
new StatusFilterPredicate(allFilters, HOME).test(warnInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarn() {
|
||||
assertFalse("should not pass because filter applies to given context",
|
||||
new StatusFilterPredicate(allFilters, HOME, WARN).test(warnInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarnRegardlessOfContext() {
|
||||
assertTrue("filters without context should always pass",
|
||||
new StatusFilterPredicate(allFilters, null, WARN).test(warnInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarnInDifferentContext() {
|
||||
assertTrue("should pass because filter does not apply to given context",
|
||||
new StatusFilterPredicate(allFilters, THREAD, WARN).test(warnInHomePublic));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarnWithHideText() {
|
||||
assertTrue("should pass because matching filter is for hiding",
|
||||
new StatusFilterPredicate(allFilters, HOME, WARN).test(hideInHomePublic));
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
package org.joinmastodon.android.updater;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
@Keep
|
||||
public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||
private static final long CHECK_PERIOD=6*3600*1000L;
|
||||
private static final String TAG="GithubSelfUpdater";
|
||||
|
||||
private UpdateState state=UpdateState.NO_UPDATE;
|
||||
private UpdateInfo info;
|
||||
private long downloadID;
|
||||
private BroadcastReceiver downloadCompletionReceiver=new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent){
|
||||
if(downloadID!=0 && intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)==downloadID){
|
||||
MastodonApp.context.unregisterReceiver(this);
|
||||
setState(UpdateState.DOWNLOADED);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public GithubSelfUpdaterImpl(){
|
||||
SharedPreferences prefs=getPrefs();
|
||||
int checkedByBuild=prefs.getInt("checkedByBuild", 0);
|
||||
if(prefs.contains("version") && checkedByBuild==BuildConfig.VERSION_CODE){
|
||||
info=new UpdateInfo();
|
||||
info.version=prefs.getString("version", null);
|
||||
info.size=prefs.getLong("apkSize", 0);
|
||||
info.changelog=prefs.getString("changelog", null);
|
||||
downloadID=prefs.getLong("downloadID", 0);
|
||||
if(downloadID==0 || !getUpdateApkFile().exists()){
|
||||
state=UpdateState.UPDATE_AVAILABLE;
|
||||
}else{
|
||||
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||
state=dm.getUriForDownloadedFile(downloadID)==null ? UpdateState.DOWNLOADING : UpdateState.DOWNLOADED;
|
||||
if(state==UpdateState.DOWNLOADING){
|
||||
MastodonApp.context.registerReceiver(downloadCompletionReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||
}
|
||||
}
|
||||
}else if(checkedByBuild!=BuildConfig.VERSION_CODE && checkedByBuild>0){
|
||||
// We are in a new version, running for the first time after update. Gotta clean things up.
|
||||
long id=getPrefs().getLong("downloadID", 0);
|
||||
if(id!=0){
|
||||
MastodonApp.context.getSystemService(DownloadManager.class).remove(id);
|
||||
}
|
||||
getUpdateApkFile().delete();
|
||||
getPrefs().edit()
|
||||
.remove("apkSize")
|
||||
.remove("version")
|
||||
.remove("apkURL")
|
||||
.remove("checkedByBuild")
|
||||
.remove("downloadID")
|
||||
.remove("changelog")
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private SharedPreferences getPrefs(){
|
||||
return MastodonApp.context.getSharedPreferences("githubUpdater", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeCheckForUpdates(){
|
||||
if(state!=UpdateState.NO_UPDATE && state!=UpdateState.UPDATE_AVAILABLE)
|
||||
return;
|
||||
long timeSinceLastCheck=System.currentTimeMillis()-getPrefs().getLong("lastCheck", CHECK_PERIOD);
|
||||
if(timeSinceLastCheck>=CHECK_PERIOD){
|
||||
setState(UpdateState.CHECKING);
|
||||
MastodonAPIController.runInBackground(this::actuallyCheckForUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkForUpdates() {
|
||||
setState(UpdateState.CHECKING);
|
||||
MastodonAPIController.runInBackground(this::actuallyCheckForUpdates);
|
||||
}
|
||||
|
||||
private void actuallyCheckForUpdates(){
|
||||
Request req=new Request.Builder()
|
||||
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
|
||||
.build();
|
||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||
try(Response resp=call.execute()){
|
||||
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
|
||||
for (JsonElement jsonElement : arr) {
|
||||
JsonObject obj = jsonElement.getAsJsonObject();
|
||||
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
|
||||
|
||||
String tag=obj.get("tag_name").getAsString();
|
||||
String changelog=obj.get("body").getAsString();
|
||||
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
||||
Matcher matcher=pattern.matcher(tag);
|
||||
if(!matcher.find()){
|
||||
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
|
||||
return;
|
||||
}
|
||||
int newMajor=Integer.parseInt(matcher.group(1)),
|
||||
newMinor=Integer.parseInt(matcher.group(2)),
|
||||
newRevision=Integer.parseInt(matcher.group(3)),
|
||||
newForkNumber=Integer.parseInt(matcher.group(4));
|
||||
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
|
||||
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
|
||||
if(!matcher.find()){
|
||||
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
|
||||
return;
|
||||
}
|
||||
int curMajor=Integer.parseInt(matcher.group(1)),
|
||||
curMinor=Integer.parseInt(matcher.group(2)),
|
||||
curRevision=Integer.parseInt(matcher.group(3)),
|
||||
curForkNumber=Integer.parseInt(matcher.group(4));
|
||||
long newVersion=((long)newMajor << 32) | ((long)newMinor << 16) | newRevision;
|
||||
long curVersion=((long)curMajor << 32) | ((long)curMinor << 16) | curRevision;
|
||||
if(newVersion>curVersion || newForkNumber>curForkNumber){
|
||||
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
|
||||
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||
JsonObject asset=el.getAsJsonObject();
|
||||
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||
long size=asset.get("size").getAsLong();
|
||||
String url=asset.get("browser_download_url").getAsString();
|
||||
|
||||
UpdateInfo info=new UpdateInfo();
|
||||
info.size=size;
|
||||
info.version=version;
|
||||
info.changelog=changelog;
|
||||
this.info=info;
|
||||
|
||||
getPrefs().edit()
|
||||
.putLong("apkSize", size)
|
||||
.putString("version", version)
|
||||
.putString("apkURL", url)
|
||||
.putString("changelog", changelog)
|
||||
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
||||
.remove("downloadID")
|
||||
.apply();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
||||
break;
|
||||
}
|
||||
}catch(Exception x){
|
||||
Log.w(TAG, "actuallyCheckForUpdates", x);
|
||||
}finally{
|
||||
setState(info==null ? UpdateState.NO_UPDATE : UpdateState.UPDATE_AVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setState(UpdateState state){
|
||||
this.state=state;
|
||||
E.post(new SelfUpdateStateChangedEvent(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateState getState(){
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateInfo getUpdateInfo(){
|
||||
return info;
|
||||
}
|
||||
|
||||
public File getUpdateApkFile(){
|
||||
return new File(MastodonApp.context.getExternalCacheDir(), "update.apk");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadUpdate(){
|
||||
if(state==UpdateState.DOWNLOADING)
|
||||
throw new IllegalStateException();
|
||||
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||
MastodonApp.context.registerReceiver(downloadCompletionReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||
downloadID=dm.enqueue(
|
||||
new DownloadManager.Request(Uri.parse(getPrefs().getString("apkURL", null)))
|
||||
.setDestinationUri(Uri.fromFile(getUpdateApkFile()))
|
||||
);
|
||||
getPrefs().edit().putLong("downloadID", downloadID).apply();
|
||||
setState(UpdateState.DOWNLOADING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUpdate(Activity activity){
|
||||
if(state!=UpdateState.DOWNLOADED)
|
||||
throw new IllegalStateException();
|
||||
Uri uri;
|
||||
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
|
||||
uri=new Uri.Builder().scheme("content").authority(activity.getPackageName()+".self_update_provider").path("update.apk").build();
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}else{
|
||||
uri=Uri.fromFile(getUpdateApkFile());
|
||||
}
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive");
|
||||
activity.startActivity(intent);
|
||||
|
||||
// TODO figure out how to restart the app when updating via this new API
|
||||
/*
|
||||
PackageInstaller installer=activity.getPackageManager().getPackageInstaller();
|
||||
try{
|
||||
final int sid=installer.createSession(new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));
|
||||
installer.registerSessionCallback(new PackageInstaller.SessionCallback(){
|
||||
@Override
|
||||
public void onCreated(int i){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBadgingChanged(int i){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveChanged(int i, boolean b){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(int id, float progress){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(int id, boolean success){
|
||||
activity.getPackageManager().setComponentEnabledSetting(new ComponentName(activity, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
});
|
||||
activity.getPackageManager().setComponentEnabledSetting(new ComponentName(activity, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
|
||||
PackageInstaller.Session session=installer.openSession(sid);
|
||||
try(OutputStream out=session.openWrite("mastodon.apk", 0, info.size); InputStream in=new FileInputStream(getUpdateApkFile())){
|
||||
byte[] buffer=new byte[16384];
|
||||
int read;
|
||||
while((read=in.read(buffer))>0){
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
// PendingIntent intent=PendingIntent.getBroadcast(activity, 1, new Intent(activity, InstallerStatusReceiver.class), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
PendingIntent intent=PendingIntent.getActivity(activity, 1, new Intent(activity, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
session.commit(intent.getIntentSender());
|
||||
}catch(IOException x){
|
||||
Log.w(TAG, "installUpdate", x);
|
||||
Toast.makeText(activity, x.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDownloadProgress(){
|
||||
if(state!=UpdateState.DOWNLOADING)
|
||||
throw new IllegalStateException();
|
||||
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||
try(Cursor cursor=dm.query(new DownloadManager.Query().setFilterById(downloadID))){
|
||||
if(cursor.moveToFirst()){
|
||||
long loaded=cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
||||
long total=cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
|
||||
// Log.d(TAG, "getDownloadProgress: "+loaded+" of "+total);
|
||||
return total>0 ? (float)loaded/total : 0f;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelDownload(){
|
||||
if(state!=UpdateState.DOWNLOADING)
|
||||
throw new IllegalStateException();
|
||||
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||
dm.remove(downloadID);
|
||||
downloadID=0;
|
||||
getPrefs().edit().remove("downloadID").apply();
|
||||
setState(UpdateState.UPDATE_AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleIntentFromInstaller(Intent intent, Activity activity){
|
||||
int status=intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
|
||||
if(status==PackageInstaller.STATUS_PENDING_USER_ACTION){
|
||||
Intent confirmIntent=intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
activity.startActivity(confirmIntent);
|
||||
}else if(status!=PackageInstaller.STATUS_SUCCESS){
|
||||
String msg=intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||
Toast.makeText(activity, activity.getString(R.string.error)+":\n"+msg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
/*public static class InstallerStatusReceiver extends BroadcastReceiver{
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent){
|
||||
int status=intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
|
||||
if(status==PackageInstaller.STATUS_PENDING_USER_ACTION){
|
||||
Intent confirmIntent=intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
context.startActivity(confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}else if(status!=PackageInstaller.STATUS_SUCCESS){
|
||||
String msg=intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||
Toast.makeText(context, context.getString(R.string.error)+":\n"+msg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AfterUpdateRestartReceiver extends BroadcastReceiver{
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent){
|
||||
if(Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction())){
|
||||
context.getPackageManager().setComponentEnabledSetting(new ComponentName(context, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||
Toast.makeText(context, R.string.update_installed, Toast.LENGTH_SHORT).show();
|
||||
Intent restartIntent=new Intent(context, MainActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setPackage(context.getPackageName());
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.P){
|
||||
context.startActivity(restartIntent);
|
||||
}else{
|
||||
// Bypass activity starting restrictions by starting it from a notification
|
||||
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
||||
NotificationChannel chan=new NotificationChannel("selfUpdateRestart", context.getString(R.string.update_installed), NotificationManager.IMPORTANCE_HIGH);
|
||||
nm.createNotificationChannel(chan);
|
||||
Notification n=new Notification.Builder(context, "selfUpdateRestart")
|
||||
.setContentTitle(context.getString(R.string.update_installed))
|
||||
.setContentIntent(PendingIntent.getActivity(context, 1, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
|
||||
.setFullScreenIntent(PendingIntent.getActivity(context, 1, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE), true)
|
||||
.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||
.build();
|
||||
nm.notify(1, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package org.joinmastodon.android.updater;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class SelfUpdateContentProvider extends ContentProvider{
|
||||
@Override
|
||||
public boolean onCreate(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri){
|
||||
if(isCorrectUri(uri))
|
||||
return "application/vnd.android.package-archive";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException{
|
||||
if(isCorrectUri(uri)){
|
||||
return ParcelFileDescriptor.open(((GithubSelfUpdaterImpl)GithubSelfUpdater.getInstance()).getUpdateApkFile(), ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
private boolean isCorrectUri(Uri uri){
|
||||
return "/update.apk".equals(uri.getPath());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M3.897 4.054L3.97 3.97c0.266-0.267 0.683-0.29 0.976-0.073L5.03 3.97 10 8.939l4.97-4.97c0.266-0.266 0.683-0.29 0.976-0.072L16.03 3.97c0.267 0.266 0.29 0.683 0.073 0.976L16.03 5.03 11.061 10l4.97 4.97c0.266 0.266 0.29 0.683 0.072 0.976L16.03 16.03c-0.266 0.267-0.683 0.29-0.976 0.073L14.97 16.03 10 11.061l-4.97 4.97c-0.266 0.266-0.683 0.29-0.976 0.072L3.97 16.03c-0.267-0.266-0.29-0.683-0.073-0.976L3.97 14.97 8.939 10l-4.97-4.97C3.704 4.764 3.68 4.347 3.898 4.054L3.97 3.97 3.897 4.054z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M22 6.5c0 3.038-2.462 5.5-5.5 5.5S11 9.538 11 6.5 13.462 1 16.5 1 22 3.462 22 6.5zm-7.146-2.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L15.793 6.5l-1.647 1.646c-0.195 0.196-0.195 0.512 0 0.707 0.196 0.196 0.512 0.196 0.708 0L16.5 7.208l1.646 1.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.707L17.207 6.5l1.647-1.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L16.5 5.793l-1.646-1.647zM19.5 14v-1.732c0.551-0.287 1.056-0.651 1.5-1.078v7.56c0 1.733-1.357 3.15-3.066 3.245L17.75 22H6.25c-1.733 0-3.15-1.357-3.245-3.066L3 18.75V7.25C3 5.517 4.356 4.1 6.066 4.005L6.25 4h4.248c-0.198 0.474-0.34 0.977-0.422 1.5H6.25c-0.918 0-1.671 0.707-1.744 1.606L4.5 7.25V14H9c0.38 0 0.694 0.282 0.743 0.648L9.75 14.75C9.75 15.993 10.757 17 12 17c1.19 0 2.166-0.925 2.245-2.096l0.005-0.154c0-0.38 0.282-0.694 0.648-0.743L15 14h4.5zm-15 1.5v3.25c0 0.918 0.707 1.671 1.606 1.744L6.25 20.5h11.5c0.918 0 1.671-0.707 1.744-1.607L19.5 18.75V15.5h-3.825c-0.335 1.648-1.75 2.904-3.475 2.995L12 18.5c-1.747 0-3.215-1.195-3.632-2.812L8.325 15.5H4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M26 7.5c0 3.59-2.91 6.5-6.5 6.5S13 11.09 13 7.5 15.91 1 19.5 1 26 3.91 26 7.5zm-9.146-3.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L18.793 7.5l-2.647 2.646c-0.195 0.196-0.195 0.512 0 0.708 0.196 0.195 0.512 0.195 0.708 0L19.5 8.207l2.646 2.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.708L20.207 7.5l2.647-2.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L19.5 6.793l-2.646-2.647zM25 22.75V12.6c-0.443 0.476-0.947 0.896-1.5 1.245V16h-6l-0.102 0.007c-0.366 0.05-0.648 0.363-0.648 0.743 0 1.519-1.231 2.75-2.75 2.75s-2.75-1.231-2.75-2.75l-0.007-0.102C11.193 16.282 10.88 16 10.5 16h-6V7.25c0-0.966 0.784-1.75 1.75-1.75h6.02c0.145-0.525 0.345-1.028 0.595-1.5H6.25C4.455 4 3 5.455 3 7.25v15.5C3 24.545 4.455 26 6.25 26h15.5c1.795 0 3.25-1.455 3.25-3.25zm-20.5 0V17.5h5.316l0.041 0.204C10.291 19.592 11.982 21 14 21l0.215-0.005c1.994-0.1 3.627-1.574 3.969-3.495H23.5v5.25c0 0.966-0.784 1.75-1.75 1.75H6.25c-0.966 0-1.75-0.784-1.75-1.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M10 2c4.418 0 8 3.582 8 8 0 2.706-1.142 4.5-3 4.5-1.226 0-2.14-0.781-2.62-2.09C11.784 13.393 10.781 14 9.5 14 7.36 14 6 12.307 6 10c0-2.337 1.313-4 3.5-4 1.052 0 1.901 0.385 2.5 1.044V6.5C12 6.224 12.224 6 12.5 6c0.245 0 0.45 0.177 0.492 0.41L13 6.5V10c0 2.223 0.813 3.5 2 3.5s2-1.277 2-3.5c0-3.866-3.134-7-7-7s-7 3.134-7 7 3.134 7 7 7c0.823 0 1.626-0.142 2.383-0.416 0.26-0.094 0.547 0.04 0.64 0.3 0.095 0.26-0.04 0.546-0.3 0.64C11.859 17.838 10.94 18 10 18c-4.418 0-8-3.582-8-8s3.582-8 8-8zM9.5 7C7.924 7 7 8.17 7 10c0 1.797 0.966 3 2.5 3s2.5-1.203 2.5-3c0-1.83-0.924-3-2.5-3z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M6.25 4.5C5.283 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.783 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75v-4c0-0.414 0.335-0.75 0.75-0.75 0.414 0 0.75 0.336 0.75 0.75v4c0 1.795-1.456 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25C3 4.455 4.455 3 6.25 3h4C10.664 3 11 3.336 11 3.75S10.664 4.5 10.25 4.5h-4zM13 3.75C13 3.336 13.335 3 13.75 3h6.5C20.664 3 21 3.336 21 3.75v6.5c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75V5.56l-5.22 5.22c-0.293 0.293-0.768 0.293-1.06 0-0.293-0.293-0.293-0.768 0-1.06l5.22-5.22h-4.69C13.335 4.5 13 4.164 13 3.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M8.502 11.5c0.554 0 1.002 0.448 1.002 1.002 0 0.553-0.448 1.002-1.002 1.002-0.553 0-1.002-0.449-1.002-1.002 0-0.554 0.449-1.003 1.002-1.003zM12 4.353v6.651h7.442L17.72 9.28c-0.267-0.266-0.29-0.683-0.073-0.977L17.72 8.22c0.266-0.266 0.683-0.29 0.976-0.072L18.78 8.22l2.997 2.998c0.266 0.266 0.29 0.682 0.073 0.976l-0.073 0.084-2.996 3.003c-0.293 0.294-0.767 0.294-1.06 0.002-0.267-0.266-0.292-0.683-0.075-0.977l0.073-0.084 1.713-1.717h-7.431L12 19.25c0 0.466-0.421 0.82-0.88 0.738l-8.5-1.501C2.26 18.424 2 18.112 2 17.748V5.75c0-0.368 0.266-0.681 0.628-0.74l8.5-1.396C11.585 3.539 12 3.89 12 4.354zm-1.5 0.883l-7 1.15v10.732l7 1.236V5.237zM13 18.5h0.765l0.102-0.007c0.366-0.05 0.649-0.364 0.648-0.744l-0.007-4.25H13v5zm0.002-8.502L13 8.726V5h0.745c0.38 0 0.693 0.281 0.743 0.647l0.007 0.101L14.502 10h-1.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M14.704 3.44C14.895 3.667 15 3.953 15 4.248V19.75c0 0.69-0.56 1.25-1.25 1.25-0.296 0-0.582-0.105-0.808-0.296l-4.967-4.206H4.25c-1.243 0-2.25-1.008-2.25-2.25v-4.5c0-1.243 1.007-2.25 2.25-2.25h3.725l4.968-4.204c0.526-0.446 1.315-0.38 1.761 0.147zM13.5 4.787l-4.975 4.21H4.25c-0.414 0-0.75 0.337-0.75 0.75v4.5c0 0.415 0.336 0.75 0.75 0.75h4.275L13.5 19.21V4.787z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M16.5 4.814c0-1.094-1.307-1.66-2.105-0.912l-4.937 4.63C9.134 8.836 8.706 9.005 8.261 9.005H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912V4.814zm-6.016 4.812L15 5.39v17.216l-4.516-4.232c-0.602-0.564-1.397-0.878-2.222-0.878H5.25c-0.966 0-1.75-0.784-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.826 0 1.62-0.314 2.223-0.88z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<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="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06L6.438 7.5H4.25C3.007 7.499 2 8.506 2 9.749v4.497c0 1.243 1.007 2.25 2.25 2.25h3.68c0.183 0 0.36 0.068 0.498 0.19l4.491 3.994C13.725 21.396 15 20.824 15 19.746V16.06l5.72 5.72c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM13.5 14.56v4.629l-4.075-3.624c-0.412-0.366-0.944-0.569-1.495-0.569H4.25c-0.414 0-0.75-0.335-0.75-0.75V9.75C3.5 9.335 3.836 9 4.25 9h3.688l5.562 5.56zm0-9.753v5.511l1.5 1.5V4.25c0-1.079-1.274-1.65-2.08-0.934l-3.4 3.022 1.063 1.063L13.5 4.807zm3.641 9.152l1.138 1.138C18.741 14.163 19 13.111 19 12c0-1.203-0.304-2.338-0.84-3.328-0.198-0.364-0.653-0.5-1.017-0.303-0.364 0.197-0.5 0.653-0.303 1.017 0.42 0.777 0.66 1.666 0.66 2.614 0 0.691-0.127 1.351-0.359 1.96zm2.247 2.247l1.093 1.094C21.445 15.763 22 13.946 22 12c0-2.226-0.728-4.284-1.96-5.946-0.246-0.333-0.716-0.403-1.048-0.157-0.333 0.247-0.403 0.716-0.157 1.05C19.881 8.358 20.5 10.106 20.5 12c0 1.531-0.404 2.966-1.112 4.206z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l5.724 5.725H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912v-5.623l8.22 8.22c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM15 16.06v6.547l-4.516-4.231c-0.602-0.565-1.397-0.879-2.222-0.879H5.25c-0.966 0-1.75-0.783-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.35 0 0.693-0.056 1.02-0.164L15 16.061zm-4.378-8.62l1.061 1.061L15 5.392v6.427l1.5 1.5V4.814c0-1.094-1.307-1.66-2.105-0.912L10.622 7.44zm9.55 9.55l1.137 1.137C21.912 16.88 22.25 15.478 22.25 14c0-2.136-0.706-4.11-1.897-5.697-0.249-0.332-0.719-0.399-1.05-0.15-0.332 0.249-0.399 0.719-0.15 1.05C20.156 10.54 20.75 12.199 20.75 14c0 1.058-0.205 2.067-0.578 2.99zm2.803 2.803l1.095 1.096c1.224-2.008 1.93-4.366 1.93-6.89 0-3.35-1.245-6.414-3.298-8.747-0.274-0.31-0.747-0.341-1.058-0.068-0.311 0.274-0.342 0.748-0.068 1.059C23.396 8.313 24.5 11.027 24.5 14c0 2.107-0.554 4.084-1.525 5.793z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,20 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M54,90L54,90c-19.9,0 -36,-16.1 -36,-36v0c0,-19.9 16.1,-36 36,-36h0c19.9,0 36,16.1 36,36v0C90,73.9 73.9,90 54,90z"
|
||||
android:strokeAlpha="0"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M52.5,41.6c-2.4,0 -4.3,0.9 -5.5,2.8l-1.2,2l-1.2,-2c-1.2,-1.9 -3.1,-2.8 -5.5,-2.8c-2.1,0 -3.8,0.8 -5.1,2.2c-1.2,1.4 -1.9,3.4 -1.9,5.9v12h4.7V50c0,-2.4 1.1,-3.7 3.1,-3.7c2.3,0 3.4,1.4 3.4,4.4v6.4h4.7v-6.4c0,-2.9 1.1,-4.4 3.4,-4.4c2.1,0 3.1,1.2 3.1,3.7v11.7h4.7v-12c0,-2.4 -0.6,-4.4 -1.9,-5.9C56.2,42.3 54.6,41.6 52.5,41.6z"
|
||||
android:fillColor="#33D17A"/>
|
||||
<path
|
||||
android:pathData="M65.9,58.1h0.8c0,0 0,0 -0.1,0c-0.6,-0.3 -1.1,-0.8 -1.4,-1.4c-0.3,-0.6 -0.5,-1.4 -0.5,-2.1c0,-0.8 0.2,-1.5 0.5,-2.1c0.4,-0.6 0.8,-1.1 1.4,-1.4c0.6,-0.3 1.2,-0.5 1.9,-0.5c0.7,0 1.3,0.2 1.9,0.5s1.1,0.8 1.4,1.4s0.5,1.3 0.5,2.1c0,0.2 0,0.4 0,0.6l0.7,0.7c0.4,0 0.8,0 1.1,0l1.5,-1.5l0.2,-0.2c-0.1,-1.2 -0.4,-2.3 -0.9,-3.4c-0.6,-1.1 -1.4,-2 -2.6,-2.6c-1.1,-0.6 -2.4,-1 -3.7,-1c-1.4,0 -2.7,0.3 -3.8,1c-1.1,0.6 -2,1.5 -2.6,2.6c-0.6,1.1 -0.9,2.4 -0.9,3.7s0.3,2.7 0.9,3.7c0.6,1.1 1.5,2 2.6,2.6c0.4,0.2 0.8,0.4 1.1,0.5v-1.8V58.1z"
|
||||
android:fillColor="#33D17A"/>
|
||||
<path
|
||||
android:pathData="M76,58.3l1.2,-1.2L76.2,56l-1.7,1.7c-0.4,-0.1 -0.7,-0.2 -1.1,-0.2s-0.8,0.1 -1.1,0.2L70.7,56l-1,1.1l1.2,1.2c-0.5,0.4 -1.1,0.9 -1.4,1.5h-2.1v1.5H69c0,0.2 -0.1,0.5 -0.1,0.8v0.8h-1.5v1.5h1.5v0.8c0,0.2 0,0.5 0.1,0.8h-1.6v1.5h2.1c0.8,1.4 2.3,2.3 4,2.3s3.1,-0.9 4,-2.3h2.1v-1.5H78c0,-0.2 0.1,-0.5 0.1,-0.8v-0.8h1.5v-1.5h-1.5v-0.8c0,-0.2 0,-0.5 -0.1,-0.8h1.6v-1.5h-2.1C77.1,59.2 76.6,58.8 76,58.3zM75,65.9H72v-1.5H75V65.9zM75,62.9H72v-1.5H75V62.9z"
|
||||
android:fillColor="#33D17A"/>
|
||||
</vector>
|
||||
@@ -1,20 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M54,90L54,90c-19.9,0 -36,-16.1 -36,-36v0c0,-19.9 16.1,-36 36,-36h0c19.9,0 36,16.1 36,36v0C90,73.9 73.9,90 54,90z"
|
||||
android:strokeAlpha="0"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M52.5,41.6c-2.4,0 -4.3,0.9 -5.5,2.8l-1.2,2l-1.2,-2c-1.2,-1.9 -3.1,-2.8 -5.5,-2.8c-2.1,0 -3.8,0.8 -5.1,2.2c-1.2,1.4 -1.9,3.4 -1.9,5.9v12h4.7V50c0,-2.4 1.1,-3.7 3.1,-3.7c2.3,0 3.4,1.4 3.4,4.4v6.4h4.7v-6.4c0,-2.9 1.1,-4.4 3.4,-4.4c2.1,0 3.1,1.2 3.1,3.7v11.7h4.7v-12c0,-2.4 -0.6,-4.4 -1.9,-5.9C56.2,42.3 54.6,41.6 52.5,41.6z"
|
||||
android:fillColor="#33D17A"/>
|
||||
<path
|
||||
android:pathData="M65.9,58.1h0.8c0,0 0,0 -0.1,0c-0.6,-0.3 -1.1,-0.8 -1.4,-1.4c-0.3,-0.6 -0.5,-1.4 -0.5,-2.1c0,-0.8 0.2,-1.5 0.5,-2.1c0.4,-0.6 0.8,-1.1 1.4,-1.4c0.6,-0.3 1.2,-0.5 1.9,-0.5c0.7,0 1.3,0.2 1.9,0.5s1.1,0.8 1.4,1.4s0.5,1.3 0.5,2.1c0,0.2 0,0.4 0,0.6l0.7,0.7c0.4,0 0.8,0 1.1,0l1.5,-1.5l0.2,-0.2c-0.1,-1.2 -0.4,-2.3 -0.9,-3.4c-0.6,-1.1 -1.4,-2 -2.6,-2.6c-1.1,-0.6 -2.4,-1 -3.7,-1c-1.4,0 -2.7,0.3 -3.8,1c-1.1,0.6 -2,1.5 -2.6,2.6c-0.6,1.1 -0.9,2.4 -0.9,3.7s0.3,2.7 0.9,3.7c0.6,1.1 1.5,2 2.6,2.6c0.4,0.2 0.8,0.4 1.1,0.5v-1.8V58.1z"
|
||||
android:fillColor="#33D17A"/>
|
||||
<path
|
||||
android:pathData="M76,58.3l1.2,-1.2L76.2,56l-1.7,1.7c-0.4,-0.1 -0.7,-0.2 -1.1,-0.2s-0.8,0.1 -1.1,0.2L70.7,56l-1,1.1l1.2,1.2c-0.5,0.4 -1.1,0.9 -1.4,1.5h-2.1v1.5H69c0,0.2 -0.1,0.5 -0.1,0.8v0.8h-1.5v1.5h1.5v0.8c0,0.2 0,0.5 0.1,0.8h-1.6v1.5h2.1c0.8,1.4 2.3,2.3 4,2.3s3.1,-0.9 4,-2.3h2.1v-1.5H78c0,-0.2 0.1,-0.5 0.1,-0.8v-0.8h1.5v-1.5h-1.5v-0.8c0,-0.2 0,-0.5 -0.1,-0.8h1.6v-1.5h-2.1C77.1,59.2 76.6,58.8 76,58.3zM75,65.9H72v-1.5H75V65.9zM75,62.9H72v-1.5H75V62.9z"
|
||||
android:fillColor="#33D17A"/>
|
||||
</vector>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_debug"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_monochrome_debug"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_debug"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_monochrome_debug"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 988 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#000000</color>
|
||||
</resources>
|
||||
@@ -1,14 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.joinmastodon.android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
|
||||
|
||||
<application
|
||||
tools:replace="android:label"
|
||||
android:label="@string/mo_app_name_debug">
|
||||
<application>
|
||||
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$InstallerStatusReceiver" android:exported="false"/>-->
|
||||
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$AfterUpdateRestartReceiver" android:exported="true" android:enabled="false">-->
|
||||
<!-- <intent-filter>-->
|
||||
@@ -115,7 +115,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||
|
||||
private void actuallyCheckForUpdates(){
|
||||
Request req=new Request.Builder()
|
||||
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
|
||||
.url("https://api.github.com/repos/sk22/megalodon/releases")
|
||||
.build();
|
||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||
try(Response resp=call.execute()){
|
||||
@@ -153,7 +153,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||
JsonObject asset=el.getAsJsonObject();
|
||||
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||
if("megalodon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||
long size=asset.get("size").getAsLong();
|
||||
String url=asset.get("browser_download_url").getAsString();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.joinmastodon.android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
@@ -25,13 +24,12 @@
|
||||
<application
|
||||
android:name=".MastodonApp"
|
||||
android:allowBackup="true"
|
||||
android:label="@string/mo_app_name"
|
||||
android:label="@string/sk_app_name"
|
||||
android:supportsRtl="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/Theme.Mastodon.AutoLightDark"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:largeHeap="true">
|
||||
|
||||
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTask">
|
||||
@@ -40,12 +38,28 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".PanicResponderActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ExitActivity"
|
||||
android:exported="false"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
<activity android:name=".OAuthActivity" android:exported="true" android:configChanges="orientation|screenSize" android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="${oAuthScheme}" 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"
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
# lists.d Mastodon Blocklist (c) 2022 Greyhat Academy LICENSED UNDER: CC-BY-NC-SA 4.0
|
||||
# https://raw.githubusercontent.com/greyhat-academy/lists.d/main/mastodon.domains.block.list.tsv
|
||||
# This list contains domains of toxic mastodon instances
|
||||
# Last-Modified: 1672044500
|
||||
|
||||
# gab - a neonazi social network
|
||||
gab.ai
|
||||
gab.com
|
||||
gab.protohype.net
|
||||
|
||||
# consequence-free speech
|
||||
social.unzensiert.to
|
||||
freeatlantis.com
|
||||
|
||||
# reactionary bigotry and hatespeech against marginalized groups
|
||||
poa.st
|
||||
freespeechextremist.com
|
||||
rdrama.cc
|
||||
outpoa.st
|
||||
anime.website
|
||||
gameliberty.club
|
||||
social.byoblu.com
|
||||
yggdrasil.social
|
||||
smuglo.li
|
||||
dogeposting.social
|
||||
unsafe.space
|
||||
freezepeach.xyz
|
||||
|
||||
# + CSAM
|
||||
rojogato.com
|
||||
|
||||
# antivaxxer shitposting & fearmongering
|
||||
shadowsocial.org
|
||||
|
||||
# Kiwifarms
|
||||
kiwifarms.net
|
||||
kiwifarms.cc
|
||||
kiwifarms.is
|
||||
kiwifarms.pleroma.net
|
||||
|
||||
|
||||
# https://mastodon.art/@Curator/109649354849593592
|
||||
|
||||
poa.st antisemitic racist homophobic
|
||||
nicecrew.digital antisemitic
|
||||
beefyboys.win antisemitic racist homophobic harassment
|
||||
cawfee.club antisemitic racist homophobic
|
||||
comfyboy.club antisemitic racist homophobic
|
||||
freespeechextremist.com racist homophobic
|
||||
cum.salon racist misogynist
|
||||
bae.st racist
|
||||
natehiggers.online racist
|
||||
rapemeat.solutions misogynist
|
||||
rapist.town misogynist
|
||||
rapefeminists.network misogynist
|
||||
kiwifarms.cc harassment
|
||||
noagendasocial.com noagenda
|
||||
posting.lolicon.rocks underage
|
||||
urchan.org harassment homophobic racist
|
||||
ryona.agency harassment
|
||||
yggdrasil.social antisemitic homophobic racist
|
||||
genderheretics.xyz transphobic
|
||||
baraag.net underage
|
||||
lolison.top underage
|
||||
shota.house underage
|
||||
shota.social underage
|
||||
aethy.com underage
|
||||
taullo.social underage
|
||||
childpawn.shop underage
|
||||
posting.lolicon.rocks underage
|
||||
loli.best underage
|
||||
gothloli.club underage
|
||||
smuglo.li underage
|
||||
youjo.love underage
|
||||
pedo.school underage
|
||||
lolison.network underage
|
||||
freak.university underage
|
||||
mirr0r.city underage
|
||||
xhais.love underage
|
||||
refusal.biz underage
|
||||
refusal.llc underage
|
||||
mirr0r.city underage
|
||||
nnia.space underage
|
||||
ignorelist.com malicious
|
||||
repl.co malicious
|
||||
|
||||
# custom
|
||||
|
||||
pawoo.net csam
|
||||
|
171
mastodon/src/main/assets/blocks.txt
Normal file
@@ -0,0 +1,171 @@
|
||||
13bells.com
|
||||
4aem.com
|
||||
aethy.com
|
||||
anime.website
|
||||
annihilation.social
|
||||
anon-kenkai.com
|
||||
asbestos.cafe
|
||||
bae.st
|
||||
bajax.us
|
||||
banepo.st
|
||||
baraag.net
|
||||
beefyboys.win
|
||||
beepboop.ga
|
||||
berserker.town
|
||||
bikeshed.party
|
||||
boks.moe
|
||||
brainsoap.net
|
||||
breastmilk.club
|
||||
brighteon.social
|
||||
cawfee.club
|
||||
clew.lol
|
||||
clubcyberia.co
|
||||
collapsitarian.io
|
||||
comfyboy.club
|
||||
contrapointsfan.club
|
||||
cum.camp
|
||||
cum.salon
|
||||
cybercriminal.eu
|
||||
darknight-coffee.org
|
||||
dembased.xyz
|
||||
desupost.soy
|
||||
detroitriotcity.com
|
||||
eatthebugs.social
|
||||
eientei.org
|
||||
elementality.org
|
||||
eveningzoo.club
|
||||
firedragonstudios.com
|
||||
firefaithfellowship.com
|
||||
fluf.club
|
||||
foxfam.club
|
||||
freak.university
|
||||
freeatlantis.com
|
||||
freecumextremist.com
|
||||
freedomstrike.org
|
||||
freesoftwareextremist.com
|
||||
freespeech.group
|
||||
freespeechextremist.com
|
||||
freetalklive.com
|
||||
froth.zone
|
||||
fulltermprivacy.com
|
||||
gameliberty.club
|
||||
gearlandia.haus
|
||||
genderheretics.xyz
|
||||
geofront.rocks
|
||||
gleasonator.com
|
||||
glee.li
|
||||
glindr.org
|
||||
goyim.app
|
||||
goyslop.cafe
|
||||
haeder.net
|
||||
handholding.io
|
||||
hidamari.apartments
|
||||
hitchhiker.social
|
||||
hunk.city
|
||||
iddqd.social
|
||||
intkos.link
|
||||
justicewarrior.social
|
||||
kawa-kun.com
|
||||
kitsunemimi.club
|
||||
kiwifarms.cc
|
||||
kompost.cz
|
||||
kurosawa.moe
|
||||
leafposter.club
|
||||
leftychan.net
|
||||
lewdieheaven.com
|
||||
liberdon.com
|
||||
ligma.pro
|
||||
lizards.live
|
||||
lolicon.rocks
|
||||
lolison.top
|
||||
lovingexpressions.net
|
||||
lucasvl.nl
|
||||
mahodou.moe
|
||||
makemysarcophagus.com
|
||||
maladaptive.art
|
||||
masochi.st
|
||||
mastinator.com
|
||||
merovingian.club
|
||||
midwaytrades.com
|
||||
mirr0r.city
|
||||
moa.st
|
||||
mouse.services
|
||||
mugicha.club
|
||||
narrativerry.xyz
|
||||
natehiggers.online
|
||||
neckbeard.xyz
|
||||
needs.vodka
|
||||
neenster.org
|
||||
nicecrew.digital
|
||||
nnia.space
|
||||
noagendasocial.com
|
||||
noagendasocial.nl
|
||||
noagendatube.com
|
||||
nobodyhasthe.biz
|
||||
nukem.biz
|
||||
obo.sh
|
||||
onionfarms.org
|
||||
outpoa.st
|
||||
pawlicker.com
|
||||
pawoo.net
|
||||
pedo.school
|
||||
piazza.today
|
||||
pibvt.net
|
||||
pieville.net
|
||||
pisskey.io
|
||||
plagu.ee
|
||||
pmth.us
|
||||
poa.st
|
||||
poast.org
|
||||
poast.tv
|
||||
poster.place
|
||||
prospeech.space
|
||||
quodverum.com
|
||||
rakket.app
|
||||
rapemeat.solutions
|
||||
rdrama.cc
|
||||
rebelbase.site
|
||||
retardedniggers.forsale
|
||||
rojogato.com
|
||||
ryona.agency
|
||||
schwartzwelt.xyz
|
||||
seal.cafe
|
||||
shigusegubu.club
|
||||
shitpost.cloud
|
||||
shitposter.club
|
||||
shota.house
|
||||
silliness.observer
|
||||
skinheads.eu
|
||||
skinheads.io
|
||||
skinheads.social
|
||||
skinheads.uk
|
||||
skippers-bin.com
|
||||
skyshanty.xyz
|
||||
slash.cl
|
||||
sleepy.cafe
|
||||
smuglo.li
|
||||
sneed.social
|
||||
sonichu.com
|
||||
spinster.xyz
|
||||
springbo.cc
|
||||
starnix.network
|
||||
stereophonic.space
|
||||
strelizia.net
|
||||
syspxl.xyz
|
||||
tastingtraffic.net
|
||||
teci.world
|
||||
theapex.social
|
||||
thepostearthdestination.com
|
||||
tkammer.de
|
||||
trumpislovetrumpis.life
|
||||
truthsocial.co.in
|
||||
urchan.org
|
||||
varishangout.net
|
||||
whinge.house
|
||||
whinge.town
|
||||
wideboys.org
|
||||
wolfgirl.bar
|
||||
xn--p1abe3d.xn--80asehdb
|
||||
yggdrasil.social
|
||||
youjo.love
|
||||
zztails.gay
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 358 KiB |
@@ -1,26 +0,0 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
public class DomainManager {
|
||||
private static final String TAG="DomainManager";
|
||||
|
||||
private static final DomainManager instance=new DomainManager();
|
||||
|
||||
private String currentDomain = "";
|
||||
|
||||
|
||||
public static DomainManager getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
private DomainManager(){
|
||||
|
||||
}
|
||||
|
||||
public String getCurrentDomain() {
|
||||
return currentDomain;
|
||||
}
|
||||
|
||||
public void setCurrentDomain(String domain) {
|
||||
this.currentDomain = domain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class ExitActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
public static void exit(Context context) {
|
||||
Intent intent = new Intent(context, ExitActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.assist.AssistContent;
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
@@ -19,6 +19,8 @@ import org.jsoup.internal.StringUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
@@ -30,22 +32,42 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
||||
super.onCreate(savedInstanceState);
|
||||
if(savedInstanceState==null){
|
||||
|
||||
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
boolean isMastodonURL = UiUtils.looksLikeMastodonUrl(text);
|
||||
Optional<String> text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT));
|
||||
Optional<Pair<String, Optional<String>>> fediHandle = text.flatMap(UiUtils::parseFediverseHandle);
|
||||
boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false);
|
||||
boolean isOpenable = isFediUrl || fediHandle.isPresent();
|
||||
|
||||
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
||||
if(sessions.isEmpty()){
|
||||
if (sessions.isEmpty()){
|
||||
Toast.makeText(this, R.string.err_not_logged_in, Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}else if(sessions.size()==1 && !isMastodonURL){
|
||||
} else if (isOpenable || sessions.size() > 1) {
|
||||
AccountSwitcherSheet sheet = new AccountSwitcherSheet(this, null, true, isOpenable);
|
||||
if (isOpenable) sheet.setOnClick((accountId, open) -> {
|
||||
if (open && text.isPresent()) {
|
||||
BiConsumer<Class<? extends Fragment>, Bundle> callback = (clazz, args) -> {
|
||||
if (clazz == null) {
|
||||
Toast.makeText(this, R.string.sk_open_in_app_failed, Toast.LENGTH_SHORT).show();
|
||||
// TODO: do something about the window getting leaked
|
||||
sheet.dismiss();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
args.putString("fromExternalShare", clazz.getSimpleName());
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.putExtras(args);
|
||||
finish();
|
||||
startActivity(intent);
|
||||
};
|
||||
if (isFediUrl) UiUtils.lookupURL(this, accountId, text.get(), false, callback);
|
||||
else UiUtils.lookupAccountHandle(this, accountId, fediHandle.get(), callback);
|
||||
} else {
|
||||
openComposeFragment(accountId);
|
||||
}
|
||||
});
|
||||
sheet.show();
|
||||
} else if (sessions.size() == 1) {
|
||||
openComposeFragment(sessions.get(0).getID());
|
||||
}else{
|
||||
new AccountSwitcherSheet(this, false, false, isMastodonURL, accountSession -> {
|
||||
if(accountSession!=null)
|
||||
openComposeFragment(accountSession.getID());
|
||||
else
|
||||
UiUtils.openURL(this, AccountSessionManager.getInstance().getLastActiveAccountID(), text);
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,11 +130,4 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
||||
return null;
|
||||
return new ArrayList<>(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProvideAssistContent(AssistContent outContent) {
|
||||
super.onProvideAssistContent(outContent);
|
||||
|
||||
outContent.setWebUri(Uri.parse(DomainManager.getInstance().getCurrentDomain()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
import org.joinmastodon.android.model.TimelineDefinition;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@@ -30,14 +30,12 @@ public class GlobalUserPreferences{
|
||||
public static boolean alwaysExpandContentWarnings;
|
||||
public static boolean disableMarquee;
|
||||
public static boolean disableSwipe;
|
||||
public static boolean disableDividers;
|
||||
public static boolean voteButtonForSingleChoice;
|
||||
public static boolean uniformNotificationIcon;
|
||||
public static boolean enableDeleteNotifications;
|
||||
public static boolean relocatePublishButton;
|
||||
public static boolean translateButtonOpenedOnly;
|
||||
public static boolean uniformNotificationIcon;
|
||||
public static boolean reduceMotion;
|
||||
public static boolean keepOnlyLatestNotification;
|
||||
public static boolean enableFabAutoHide;
|
||||
public static boolean disableAltTextReminder;
|
||||
public static boolean showAltIndicator;
|
||||
public static boolean showNoAltIndicator;
|
||||
@@ -47,35 +45,29 @@ public class GlobalUserPreferences{
|
||||
public static boolean collapseLongPosts;
|
||||
public static boolean spectatorMode;
|
||||
public static boolean autoHideFab;
|
||||
public static boolean defaultToUnlistedReplies;
|
||||
public static boolean disableDoubleTapToSwipe;
|
||||
public static boolean replyLineAboveHeader;
|
||||
public static boolean compactReblogReplyLine;
|
||||
public static boolean confirmBeforeReblog;
|
||||
public static boolean replyLineAboveHeader;
|
||||
public static boolean swapBookmarkWithBoostAction;
|
||||
public static boolean loadRemoteAccountFollowers;
|
||||
public static boolean mentionRebloggerAutomatically;
|
||||
public static String publishButtonText;
|
||||
public static ThemePreference theme;
|
||||
public static ColorPreference color;
|
||||
|
||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
||||
private final static Type accountsDefaultContentTypesType = new TypeToken<Map<String, ContentType>>() {}.getType();
|
||||
public static Map<String, List<String>> recentLanguages;
|
||||
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
|
||||
public static Set<String> accountsWithLocalOnlySupport;
|
||||
public static Set<String> accountsInGlitchMode;
|
||||
|
||||
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
||||
public static Map<String, Integer> recentEmojis;
|
||||
public static Set<String> accountsWithContentTypesEnabled;
|
||||
public static Map<String, ContentType> accountsDefaultContentTypes;
|
||||
|
||||
/**
|
||||
* Pleroma
|
||||
*/
|
||||
public static String replyVisibility;
|
||||
|
||||
|
||||
public static SharedPreferences getPrefs(){
|
||||
private static SharedPreferences getPrefs(){
|
||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@@ -85,6 +77,16 @@ public class GlobalUserPreferences{
|
||||
catch (JsonSyntaxException ignored) { return orElse; }
|
||||
}
|
||||
|
||||
public static void removeAccount(String accountId) {
|
||||
recentLanguages.remove(accountId);
|
||||
pinnedTimelines.remove(accountId);
|
||||
accountsInGlitchMode.remove(accountId);
|
||||
accountsWithLocalOnlySupport.remove(accountId);
|
||||
accountsWithContentTypesEnabled.remove(accountId);
|
||||
accountsDefaultContentTypes.remove(accountId);
|
||||
save();
|
||||
}
|
||||
|
||||
public static void load(){
|
||||
SharedPreferences prefs=getPrefs();
|
||||
playGifs=prefs.getBoolean("playGifs", true);
|
||||
@@ -94,18 +96,16 @@ public class GlobalUserPreferences{
|
||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
|
||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
|
||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||
disableSwipe=prefs.getBoolean("disableSwipe", false);
|
||||
disableDividers=prefs.getBoolean("disableDividers", true);
|
||||
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
|
||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", false);
|
||||
translateButtonOpenedOnly=prefs.getBoolean("translateButtonOpenedOnly", false);
|
||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
|
||||
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
||||
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
||||
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
||||
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
||||
@@ -115,34 +115,24 @@ public class GlobalUserPreferences{
|
||||
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
||||
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
||||
autoHideFab=prefs.getBoolean("autoHideFab", true);
|
||||
compactReblogReplyLine=prefs.getBoolean("compactReblogReplyLine", true);
|
||||
defaultToUnlistedReplies=prefs.getBoolean("defaultToUnlistedReplies", false);
|
||||
disableDoubleTapToSwipe=prefs.getBoolean("disableDoubleTapToSwipe", false);
|
||||
replyLineAboveHeader=prefs.getBoolean("replyLineAboveHeader", true);
|
||||
compactReblogReplyLine=prefs.getBoolean("compactReblogReplyLine", true);
|
||||
confirmBeforeReblog=prefs.getBoolean("confirmBeforeReblog", false);
|
||||
swapBookmarkWithBoostAction=prefs.getBoolean("swapBookmarkWithBoostAction", false);
|
||||
loadRemoteAccountFollowers=prefs.getBoolean("loadRemoteAccountFollowers", true);
|
||||
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
|
||||
publishButtonText=prefs.getString("publishButtonText", "");
|
||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
||||
publishButtonText=prefs.getString("publishButtonText", "");
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>());
|
||||
pinnedTimelines=fromJson(prefs.getString("pinnedTimelines", null), pinnedTimelinesType, new HashMap<>());
|
||||
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
||||
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
|
||||
replyVisibility=prefs.getString("replyVisibility", null);
|
||||
accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>());
|
||||
accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>());
|
||||
|
||||
try {
|
||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.MATERIAL3.name()));
|
||||
}else{
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PURPLE.name()));
|
||||
}
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
||||
} catch (IllegalArgumentException|ClassCastException ignored) {
|
||||
// invalid color name or color was previously saved as integer
|
||||
color=ColorPreference.PURPLE;
|
||||
color=ColorPreference.PINK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,13 +149,11 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||
.putBoolean("disableMarquee", disableMarquee)
|
||||
.putBoolean("disableSwipe", disableSwipe)
|
||||
.putBoolean("disableDividers", disableDividers)
|
||||
.putBoolean("relocatePublishButton", relocatePublishButton)
|
||||
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
||||
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
||||
.putBoolean("translateButtonOpenedOnly", translateButtonOpenedOnly)
|
||||
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
||||
.putBoolean("reduceMotion", reduceMotion)
|
||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||
.putBoolean("enableFabAutoHide", enableFabAutoHide)
|
||||
.putBoolean("disableAltTextReminder", disableAltTextReminder)
|
||||
.putBoolean("showAltIndicator", showAltIndicator)
|
||||
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
||||
@@ -174,24 +162,20 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
||||
.putBoolean("spectatorMode", spectatorMode)
|
||||
.putBoolean("autoHideFab", autoHideFab)
|
||||
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
||||
.putString("publishButtonText", publishButtonText)
|
||||
.putBoolean("bottomEncoding", bottomEncoding)
|
||||
.putBoolean("defaultToUnlistedReplies", defaultToUnlistedReplies)
|
||||
.putBoolean("disableDoubleTapToSwipe", disableDoubleTapToSwipe)
|
||||
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
||||
.putBoolean("replyLineAboveHeader", replyLineAboveHeader)
|
||||
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
|
||||
.putBoolean("swapBookmarkWithBoostAction", swapBookmarkWithBoostAction)
|
||||
.putBoolean("loadRemoteAccountFollowers", loadRemoteAccountFollowers)
|
||||
.putBoolean("mentionRebloggerAutomatically", mentionRebloggerAutomatically)
|
||||
.putInt("theme", theme.ordinal())
|
||||
.putString("color", color.name())
|
||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||
.putString("pinnedTimelines", gson.toJson(pinnedTimelines))
|
||||
.putString("recentEmojis", gson.toJson(recentEmojis))
|
||||
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
||||
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
||||
.putString("replyVisibility", replyVisibility)
|
||||
.putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled)
|
||||
.putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes))
|
||||
.apply();
|
||||
}
|
||||
|
||||
@@ -203,8 +187,7 @@ public class GlobalUserPreferences{
|
||||
BLUE,
|
||||
BROWN,
|
||||
RED,
|
||||
YELLOW,
|
||||
NORD
|
||||
YELLOW
|
||||
}
|
||||
|
||||
public enum ThemePreference{
|
||||
@@ -213,4 +196,3 @@ public class GlobalUserPreferences{
|
||||
DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@ import android.app.Fragment;
|
||||
import android.app.assist.AssistContent;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.fragments.HomeFragment;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
@@ -22,13 +23,13 @@ import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
|
||||
public class MainActivity extends FragmentStackActivity{
|
||||
|
||||
public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState){
|
||||
UiUtils.setUserPreferredTheme(this);
|
||||
@@ -38,10 +39,18 @@ public class MainActivity extends FragmentStackActivity{
|
||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
||||
}else{
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||
AccountSession session;
|
||||
Bundle args=new Bundle();
|
||||
Intent intent=getIntent();
|
||||
if(intent.hasExtra("fromExternalShare")) {
|
||||
AccountSessionManager.getInstance()
|
||||
.setLastActiveAccountID(intent.getStringExtra("account"));
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
||||
AccountSessionManager.getInstance().getLastActiveAccount());
|
||||
showFragmentForExternalShare(intent.getExtras());
|
||||
return;
|
||||
}
|
||||
|
||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
||||
boolean hasNotification = intent.hasExtra("notification");
|
||||
if(fromNotification){
|
||||
@@ -55,6 +64,7 @@ public class MainActivity extends FragmentStackActivity{
|
||||
}else{
|
||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||
}
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
||||
args.putString("account", session.getID());
|
||||
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
||||
fragment.setArguments(args);
|
||||
@@ -78,12 +88,12 @@ public class MainActivity extends FragmentStackActivity{
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent){
|
||||
super.onNewIntent(intent);
|
||||
if(intent.getBooleanExtra("fromNotification", false)){
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||
if (intent.hasExtra("fromExternalShare")) showFragmentForExternalShare(intent.getExtras());
|
||||
else if (intent.getBooleanExtra("fromNotification", false)) {
|
||||
String accountID=intent.getStringExtra("accountID");
|
||||
AccountSession accountSession;
|
||||
try{
|
||||
accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
DomainManager.getInstance().setCurrentDomain(accountSession.domain);
|
||||
AccountSessionManager.getInstance().getAccount(accountID);
|
||||
}catch(IllegalStateException x){
|
||||
return;
|
||||
}
|
||||
@@ -107,23 +117,24 @@ public class MainActivity extends FragmentStackActivity{
|
||||
}
|
||||
|
||||
private void showFragmentForNotification(Notification notification, String accountID){
|
||||
Fragment fragment;
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putBoolean("_can_go_back", true);
|
||||
try{
|
||||
notification.postprocess();
|
||||
}catch(ObjectValidationException x){
|
||||
Log.w("MainActivity", x);
|
||||
return;
|
||||
}
|
||||
if(notification.status!=null){
|
||||
fragment=new ThreadFragment();
|
||||
args.putParcelable("status", Parcels.wrap(notification.status));
|
||||
}else{
|
||||
fragment=new ProfileFragment();
|
||||
args.putParcelable("profileAccount", Parcels.wrap(notification.account));
|
||||
}
|
||||
UiUtils.showFragmentForNotification(this, notification, accountID, null);
|
||||
}
|
||||
|
||||
private void showFragmentForExternalShare(Bundle args) {
|
||||
String clazz = args.getString("fromExternalShare");
|
||||
Fragment fragment = switch (clazz) {
|
||||
case "ThreadFragment" -> new ThreadFragment();
|
||||
case "ProfileFragment" -> new ProfileFragment();
|
||||
default -> null;
|
||||
};
|
||||
if (fragment == null) return;
|
||||
args.putBoolean("_can_go_back", true);
|
||||
fragment.setArguments(args);
|
||||
showFragment(fragment);
|
||||
}
|
||||
@@ -157,25 +168,40 @@ public class MainActivity extends FragmentStackActivity{
|
||||
(fragmentContainers.get(fragmentContainers.size() - 1)).getId()
|
||||
);
|
||||
Bundle currentArgs = currentFragment.getArguments();
|
||||
if (this.fragmentContainers.size() == 1
|
||||
&& currentArgs != null
|
||||
&& currentArgs.getBoolean("_can_go_back", false)
|
||||
&& currentArgs.containsKey("account")) {
|
||||
if (fragmentContainers.size() != 1
|
||||
|| currentArgs == null
|
||||
|| !currentArgs.getBoolean("_can_go_back", false)) {
|
||||
super.onBackPressed();
|
||||
return;
|
||||
}
|
||||
if (currentArgs.getBoolean("_finish_on_back", false)) {
|
||||
finish();
|
||||
} else if (currentArgs.containsKey("account")) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString("account", currentArgs.getString("account"));
|
||||
args.putString("tab", "notifications");
|
||||
if (getIntent().getBooleanExtra("fromNotification", false)) {
|
||||
args.putString("tab", "notifications");
|
||||
}
|
||||
Fragment fragment=new HomeFragment();
|
||||
fragment.setArguments(args);
|
||||
showFragmentClearingBackStack(fragment);
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onProvideAssistContent(AssistContent outContent) {
|
||||
super.onProvideAssistContent(outContent);
|
||||
|
||||
outContent.setWebUri(Uri.parse(DomainManager.getInstance().getCurrentDomain()));
|
||||
public Fragment getCurrentFragment() {
|
||||
for (int i = fragmentContainers.size() - 1; i >= 0; i--) {
|
||||
FrameLayout fl = fragmentContainers.get(i);
|
||||
if (fl.getVisibility() == View.VISIBLE) {
|
||||
return getFragmentManager().findFragmentById(fl.getId());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProvideAssistContent(AssistContent assistContent) {
|
||||
super.onProvideAssistContent(assistContent);
|
||||
Fragment fragment = getCurrentFragment();
|
||||
if (fragment != null) callFragmentToProvideAssistContent(fragment, assistContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.api.mastodon.requests.accounts.GetOwnAccount;
|
||||
import org.joinmastodon.android.api.mastodon.requests.oauth.GetOauthToken;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||
import org.joinmastodon.android.api.requests.oauth.GetOauthToken;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Application;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
|
||||
|
||||
public class PanicResponderActivity extends Activity {
|
||||
public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Intent intent = getIntent();
|
||||
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
|
||||
AccountSessionManager.getInstance().getLoggedInAccounts().forEach(accountSession -> logOut(accountSession.getID()));
|
||||
ExitActivity.exit(this);
|
||||
}
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
private void logOut(String accountID){
|
||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
new RevokeOauthToken(session.app.clientId, session.app.clientSecret, session.token.accessToken)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Object result){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
private void onLoggedOut(String accountID){
|
||||
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import static org.joinmastodon.android.GlobalUserPreferences.getPrefs;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
@@ -18,23 +16,25 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.mastodon.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.api.mastodon.requests.notifications.GetNotificationByID;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusBookmarked;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusFavorited;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusReblogged;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
||||
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Mention;
|
||||
import org.joinmastodon.android.model.NotificationAction;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.PushNotification;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -54,7 +54,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
private static final String ACTION_KEY_TEXT_REPLY = "ACTION_KEY_TEXT_REPLY";
|
||||
|
||||
private static final int SUMMARY_ID = 791;
|
||||
private static int notificationId;
|
||||
private static int notificationId = 0;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent){
|
||||
@@ -121,26 +121,17 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
|
||||
if(intent.hasExtra("notification")){
|
||||
org.joinmastodon.android.model.Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||
String statusID = null;
|
||||
String targetAccountID = null;
|
||||
|
||||
if(notification.status != null){
|
||||
statusID = notification.status.id;
|
||||
}
|
||||
if(notification.account != null){
|
||||
targetAccountID = notification.account.id;
|
||||
}
|
||||
if (statusID != null || targetAccountID != null) {
|
||||
String statusID=notification.status.id;
|
||||
if (statusID != null) {
|
||||
AccountSessionManager accountSessionManager = AccountSessionManager.getInstance();
|
||||
Preferences preferences = accountSessionManager.getAccount(accountID).preferences;
|
||||
|
||||
switch (NotificationAction.values()[intent.getIntExtra("notificationAction", 0)]) {
|
||||
case FAVORITE -> new SetStatusFavorited(statusID, true).exec(accountID);
|
||||
case BOOKMARK -> new SetStatusBookmarked(statusID, true).exec(accountID);
|
||||
case BOOST -> new SetStatusReblogged(notification.status.id, true, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case UNBOOST -> new SetStatusReblogged(notification.status.id, false, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case REBLOG -> new SetStatusReblogged(notification.status.id, true, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case UNDO_REBLOG -> new SetStatusReblogged(notification.status.id, false, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case REPLY -> handleReplyAction(context, accountID, intent, notification, notificationId, preferences);
|
||||
case FOLLOW_BACK -> new SetAccountFollowed(notification.account.id, true, true, false).exec(accountID);
|
||||
default -> Log.w(TAG, "onReceive: Failed to get NotificationAction");
|
||||
}
|
||||
}
|
||||
@@ -152,7 +143,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
|
||||
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
||||
notificationId=getPrefs().getInt("latestNotificationId", 0);
|
||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||
Notification.Builder builder;
|
||||
@@ -200,7 +190,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
.setShowWhen(true)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setAutoCancel(true)
|
||||
.setColor(context.getColor(R.color.shortcut_icon_background));
|
||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
||||
|
||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||
builder.setSmallIcon(switch (pn.notificationType) {
|
||||
@@ -224,7 +214,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
}
|
||||
|
||||
int id = GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId++;
|
||||
getPrefs().edit().putInt("latestNotificationId", notificationId).apply();
|
||||
|
||||
if (notification != null){
|
||||
switch (pn.notificationType){
|
||||
@@ -233,23 +222,14 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
builder.addAction(buildReplyAction(context, id, accountID, notification));
|
||||
}
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.button_favorite), NotificationAction.FAVORITE));
|
||||
if(GlobalUserPreferences.swapBookmarkWithBoostAction){
|
||||
if(notification.status.visibility != StatusPrivacy.DIRECT) {
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.button_reblog), NotificationAction.BOOST));
|
||||
}else{
|
||||
// This is just so there is a bookmark action if you cannot reblog the toot
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.add_bookmark), NotificationAction.BOOKMARK));
|
||||
}
|
||||
} else {
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.add_bookmark), NotificationAction.BOOKMARK));
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.add_bookmark), NotificationAction.BOOKMARK));
|
||||
if(notification.status.visibility != StatusPrivacy.DIRECT) {
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.button_reblog), NotificationAction.REBLOG));
|
||||
}
|
||||
}
|
||||
case UPDATE -> {
|
||||
if(notification.status.reblogged)
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNBOOST));
|
||||
}
|
||||
case FOLLOW -> {
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.follow_back), NotificationAction.FOLLOW_BACK));
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNDO_REBLOG));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,27 +275,60 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
}
|
||||
CharSequence input = remoteInput.getCharSequence(ACTION_KEY_TEXT_REPLY);
|
||||
|
||||
// copied from ComposeFragment - TODO: generalize?
|
||||
ArrayList<String> mentions=new ArrayList<>();
|
||||
Status status = notification.status;
|
||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
||||
if(!status.account.id.equals(ownID))
|
||||
mentions.add('@'+status.account.acct);
|
||||
for(Mention mention:status.mentions){
|
||||
if(mention.id.equals(ownID))
|
||||
continue;
|
||||
String m='@'+mention.acct;
|
||||
if(!mentions.contains(m))
|
||||
mentions.add(m);
|
||||
}
|
||||
String initialText=mentions.isEmpty() ? "" : TextUtils.join(" ", mentions)+" ";
|
||||
|
||||
CreateStatus.Request req=new CreateStatus.Request();
|
||||
req.status = input.toString() + "\n\n" + "@" + notification.status.account.acct;
|
||||
req.language = notification.status.language;
|
||||
req.visibility = (notification.status.visibility == StatusPrivacy.PUBLIC && GlobalUserPreferences.defaultToUnlistedReplies ? StatusPrivacy.UNLISTED : notification.status.visibility);
|
||||
req.status = initialText + input.toString();
|
||||
req.language = preferences.postingDefaultLanguage;
|
||||
req.visibility = preferences.postingDefaultVisibility;
|
||||
req.inReplyToId = notification.status.id;
|
||||
if(!notification.status.spoilerText.isEmpty() && GlobalUserPreferences.prefixRepliesWithRe && !notification.status.spoilerText.startsWith("re: ")){
|
||||
req.spoilerText = "re: " + notification.status.spoilerText;
|
||||
}
|
||||
|
||||
new CreateStatus(req, UUID.randomUUID().toString()).exec(accountID);
|
||||
new CreateStatus(req, UUID.randomUUID().toString()).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Status status) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
Notification.Builder builder = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
|
||||
new Notification.Builder(context, accountID+"_"+notification.type) :
|
||||
new Notification.Builder(context)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
Notification.Builder builder = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
|
||||
new Notification.Builder(context, accountID+"_"+notification.type) :
|
||||
new Notification.Builder(context)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||
notification.status = status;
|
||||
Intent contentIntent=new Intent(context, MainActivity.class);
|
||||
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
contentIntent.putExtra("fromNotification", true);
|
||||
contentIntent.putExtra("accountID", accountID);
|
||||
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
||||
|
||||
Notification repliedNotification = builder.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||
.setContentText(context.getString(R.string.mo_notification_action_replied, notification.status.account.getDisplayUsername()))
|
||||
.build();
|
||||
notificationManager.notify(accountID, notificationId, repliedNotification);
|
||||
Notification repliedNotification = builder.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||
.setContentTitle(context.getString(R.string.sk_notification_action_replied, notification.status.account.displayName))
|
||||
.setContentText(status.getStrippedText())
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.build();
|
||||
notificationManager.notify(accountID, notificationId, repliedNotification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse errorResponse) {
|
||||
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.adapter.ApiAdapter;
|
||||
import org.joinmastodon.android.api.mastodon.requests.notifications.GetNotifications;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
@@ -73,10 +73,8 @@ public class CacheController{
|
||||
int flags=cursor.getInt(1);
|
||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||
newMaxID=status.id;
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(status))
|
||||
continue outer;
|
||||
}
|
||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.HOME).test(status))
|
||||
continue outer;
|
||||
result.add(status);
|
||||
}while(cursor.moveToNext());
|
||||
String _newMaxID=newMaxID;
|
||||
@@ -87,12 +85,11 @@ public class CacheController{
|
||||
Log.w(TAG, "getHomeTimeline: corrupted status object in database", x);
|
||||
}
|
||||
}
|
||||
ApiAdapter apiAdapter = new ApiAdapter(ApiAdapter.ServerType.MASTODON);
|
||||
apiAdapter.getHomeTimeline(maxID, null, count, null)
|
||||
new GetHomeTimeline(maxID, null, count, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters, Filter.FilterContext.HOME)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
putHomeTimeline(result, maxID==null);
|
||||
}
|
||||
|
||||
@@ -148,10 +145,8 @@ public class CacheController{
|
||||
ntf.postprocess();
|
||||
newMaxID=ntf.id;
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
result.add(ntf);
|
||||
}while(cursor.moveToNext());
|
||||
@@ -164,17 +159,13 @@ public class CacheController{
|
||||
}
|
||||
}
|
||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(accountSession.domain);
|
||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.pleroma != null)
|
||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.isAkkoma())
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Notification> result){
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(ntf->{
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status);
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
||||
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -28,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -51,7 +52,9 @@ public class MastodonAPIController{
|
||||
.registerTypeAdapter(Status.class, new Status.StatusDeserializer())
|
||||
.create();
|
||||
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
||||
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
|
||||
private static OkHttpClient httpClient=new OkHttpClient.Builder()
|
||||
.readTimeout(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private AccountSession session;
|
||||
private static List<String> badDomains = new ArrayList<>();
|
||||
@@ -60,7 +63,7 @@ public class MastodonAPIController{
|
||||
thread.start();
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
MastodonApp.context.getAssets().open("blocks.tsv")
|
||||
MastodonApp.context.getAssets().open("blocks.txt")
|
||||
));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
@@ -85,13 +88,13 @@ public class MastodonAPIController{
|
||||
final boolean isBad = host == null || badDomains.stream().anyMatch(h -> h.equalsIgnoreCase(host) || host.toLowerCase().endsWith("." + h));
|
||||
thread.postRunnable(()->{
|
||||
try{
|
||||
// if (isBad) throw new IllegalArgumentException();
|
||||
if (isBad) throw new IllegalArgumentException();
|
||||
if(req.canceled)
|
||||
return;
|
||||
Request.Builder builder=new Request.Builder()
|
||||
.url(req.getURL().toString())
|
||||
.method(req.getMethod(), req.getRequestBody())
|
||||
.header("User-Agent", "MastodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||
.header("User-Agent", "MegalodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||
|
||||
String token=null;
|
||||
if(session!=null)
|
||||
|
||||
@@ -9,8 +9,8 @@ import android.util.Pair;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.BaseModel;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.mastodon.requests.notifications.RegisterForPushNotifications;
|
||||
import org.joinmastodon.android.api.mastodon.requests.notifications.UpdatePushSettings;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSession;
|
||||
import org.joinmastodon.android.api.mastodon.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.api.requests.notifications.RegisterForPushNotifications;
|
||||
import org.joinmastodon.android.api.requests.notifications.UpdatePushSettings;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.PushNotification;
|
||||
import org.joinmastodon.android.model.PushSubscription;
|
||||
|
||||
@@ -45,6 +45,7 @@ import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import android.os.Looper;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusBookmarked;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusFavorited;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.SetStatusReblogged;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
@@ -46,7 +46,7 @@ public class StatusInteractionController{
|
||||
@Override
|
||||
public void onSuccess(Status result){
|
||||
runningFavoriteRequests.remove(status.id);
|
||||
result.favouritesCount = Math.max(0, status.favouritesCount) + (favorited ? 1 : -1);
|
||||
result.favouritesCount = Math.max(0, status.favouritesCount + (favorited ? 1 : -1));
|
||||
cb.accept(result);
|
||||
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class StatusInteractionController{
|
||||
public void onSuccess(Status reblog){
|
||||
Status result = reblog.getContentStatus();
|
||||
runningReblogRequests.remove(status.id);
|
||||
result.reblogsCount = Math.max(0, status.reblogsCount) + (reblogged ? 1 : -1);
|
||||
result.reblogsCount = Math.max(0, status.reblogsCount + (reblogged ? 1 : -1));
|
||||
cb.accept(result);
|
||||
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.joinmastodon.android.api.adapter;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.api.mastodon.requests.statuses.GetStatusByID;
|
||||
import org.joinmastodon.android.api.mastodon.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ApiAdapter {
|
||||
public final ServerType serverType;
|
||||
|
||||
public ApiAdapter(ServerType serverType){
|
||||
this.serverType = serverType;
|
||||
}
|
||||
|
||||
public MastodonAPIRequest<?> getPostById(String id){
|
||||
switch (serverType){
|
||||
case MASTODON -> {
|
||||
return new GetStatusByID(id);
|
||||
}
|
||||
case MISSKEY -> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MastodonAPIRequest<List<Status>> getHomeTimeline(String maxID, String minID, int limit, String sinceID){
|
||||
switch (serverType){
|
||||
case MASTODON -> {
|
||||
return new GetHomeTimeline(maxID, minID, limit, sinceID);
|
||||
}
|
||||
case MISSKEY -> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum ServerType {
|
||||
MASTODON,
|
||||
MISSKEY,
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetAccountByHandle extends MastodonAPIRequest<Account>{
|
||||
public GetAccountByHandle(String acct){
|
||||
super(HttpMethod.GET, "/accounts/lookup", Account.class);
|
||||
addQueryParameter("acct", acct);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
|
||||
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
||||
public SetAccountMuted(String id, boolean muted, long duration){
|
||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
||||
setRequestBody(muted ? new Request(duration): new Object());
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public long duration;
|
||||
public Request(long duration){
|
||||
this.duration=duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
|
||||
public class SetPrivateNote extends MastodonAPIRequest<Relationship>{
|
||||
public SetPrivateNote(String id, String comment){
|
||||
super(MastodonAPIRequest.HttpMethod.POST, "/accounts/"+id+"/note", Relationship.class);
|
||||
Request req = new Request(comment);
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public String comment;
|
||||
public Request(String comment){
|
||||
this.comment=comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class AddList extends MastodonAPIRequest<Object> {
|
||||
public AddList(String listName){
|
||||
super(HttpMethod.POST, "/lists", Object.class);
|
||||
Request req = new Request();
|
||||
req.title = listName;
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
public static class Request{
|
||||
public String title;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class EditListName extends MastodonAPIRequest<Object> {
|
||||
public EditListName(String newListName, String listId){
|
||||
super(HttpMethod.PUT, "/lists/"+listId, Object.class);
|
||||
Request req = new Request();
|
||||
req.title = newListName;
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
public static class Request{
|
||||
public String title;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class RemoveList extends MastodonAPIRequest<Object> {
|
||||
public RemoveList(String listId){
|
||||
super(HttpMethod.DELETE, "/lists/"+listId, Object.class);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.timelines;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetHomeTimeline extends MastodonAPIRequest<List<Status>>{
|
||||
public GetHomeTimeline(String maxID, String minID, int limit, String sinceID){
|
||||
super(HttpMethod.GET, "/timelines/home", new TypeToken<>(){});
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(minID!=null)
|
||||
addQueryParameter("min_id", minID);
|
||||
if(sinceID!=null)
|
||||
addQueryParameter("since_id", sinceID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", ""+limit);
|
||||
if(GlobalUserPreferences.replyVisibility != null)
|
||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests;
|
||||
package org.joinmastodon.android.api.requests;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.mastodon.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetAccountFollowers extends HeaderPaginationRequest<Account>{
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.mastodon.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetAccountFollowing extends HeaderPaginationRequest<Account>{
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.mastodon.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetFollowRequests extends HeaderPaginationRequest<Account>{
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
|
||||
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
||||
public SetAccountMuted(String id, boolean muted){
|
||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.accounts;
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.announcements;
|
||||
package org.joinmastodon.android.api.requests.announcements;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.announcements;
|
||||
package org.joinmastodon.android.api.requests.announcements;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.catalog;
|
||||
package org.joinmastodon.android.api.requests.catalog;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.catalog;
|
||||
package org.joinmastodon.android.api.requests.catalog;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.instance;
|
||||
package org.joinmastodon.android.api.requests.instance;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.instance;
|
||||
package org.joinmastodon.android.api.requests.instance;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.api.mastodon.requests.lists;
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||