Compare commits
2 Commits
feature/re
...
feature/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eec547618f | ||
|
|
a3ee174d66 |
20
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -8,33 +8,25 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To reproduce**
|
**To Reproduce**
|
||||||
|
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**Does this happen in the official app?**
|
|
||||||
|
|
||||||
Does this issue also occur with the respective upstream release?
|
|
||||||
> No / Yes
|
|
||||||
|
|
||||||
> In case it does, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead.
|
|
||||||
> If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
|
|
||||||
|
|
||||||
**Screenshots and screen recordings**
|
**Screenshots and screen recordings**
|
||||||
|
|
||||||
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
|
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
|
||||||
|
|
||||||
**Version**
|
**Version**
|
||||||
|
Megalodon version: [e.g. v1.1.4+fork.#]
|
||||||
|
|
||||||
Moshidon version: [e.g. v1.1.4+fork.#]
|
**Additional context**
|
||||||
|
- Does this issue also occur with the respective upstream release? (Please test using the respective `upstream-xxxxxx.apk` provided in [Releases](https://github.com/sk22/megalodon/releases)) No / Yes (`mastodon#…`)
|
||||||
|
|
||||||
|
> In this case, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead. If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
|
||||||
|
|
||||||
**Crash log**
|
**Crash log**
|
||||||
|
|
||||||
If you know your way around Android development tools, please consider attaching a crash log, if possible.
|
If you know your way around Android development tools, please consider attaching a crash log, if possible.
|
||||||
|
|||||||
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: 'temurin'
|
|
||||||
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
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
*.jks
|
*.jks
|
||||||
*.keystore
|
|
||||||
/mastodon/keystore/nightly_keystore.keystore
|
|
||||||
|
|||||||
52
README.md
@@ -5,49 +5,27 @@
|
|||||||
> 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.
|
> 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/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
[](https://nightly.link/LucasGGamerM/moshidon/workflows/nightly-builds/master/moshidon-nightly.apk.zip)
|
|
||||||
|
|
||||||
|
|
||||||
[](https://translate.codeberg.org/engage/moshidon/)
|
|
||||||
|
|
||||||
[](https://github.com/LucasGGamerM/moshidon/actions/workflows/android.yml)
|
|
||||||
|
|
||||||
<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://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
|
|
||||||
### **The ability to add new custom local timelines!**
|
|
||||||
|
|
||||||
#### It can be accessed in the "Edit timelines" menu, where you can add a new "Community" to see other server's local posts!
|
|
||||||
|
|
||||||
### **Material you theme support on Android 12+ devices!**
|
### **Material you theme support on Android 12+ devices!**
|
||||||
|
|
||||||
### **Show posts filtered with a warning!**
|
### **Translate button**
|
||||||
|
|
||||||
**Allows you to have filtered posts collapsed with a warning! As shown in the screenshots:**
|
**Allows you to translate posts in instances with the translate feature!**
|
||||||
|
|
||||||
Before | After
|
**Screenshots**
|
||||||
:-------------------------:|:-------------------------:
|
|
||||||
 | 
|
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
### **Color themes**
|
### **Color themes**
|
||||||
|
|
||||||
**Allows you to change theme within the app. Supports Purple, pink, green, blue, red, orange, yellow and Nord!**
|
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
||||||
|
|
||||||
### **Unlisted posting**
|
### **Unlisted posting**
|
||||||
|
|
||||||
@@ -89,9 +67,9 @@ To bookmark a post, press the button between the Favorite and Share buttons on t
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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!
|
Moshidon 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
|
## Release variants
|
||||||
|
|
||||||
@@ -108,16 +86,9 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* [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)
|
* [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))
|
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
||||||
* Adding a useful private profile note box!*
|
* [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))
|
||||||
* 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 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))
|
* [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 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 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))
|
||||||
@@ -139,7 +110,6 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
|||||||
|
|
||||||
### Behavior
|
### Behavior
|
||||||
|
|
||||||
* 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))
|
* [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))
|
* [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))
|
* [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))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Moshidon</title>
|
<title>Moshidon</title>
|
||||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||||
<link rel="me" href="https://floss.social/@megalodon">
|
<link rel="me" href="https://floss.social/@mastodon">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="markdown-body">
|
<body class="markdown-body">
|
||||||
|
|||||||
5
crowdin.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
files:
|
||||||
|
- source: /mastodon/src/main/res/values/strings.xml
|
||||||
|
translation: /mastodon/src/main/res/values-%android_code%/strings.xml
|
||||||
|
- source: /fastlane/metadata/android/en-US/*.txt
|
||||||
|
translation: /fastlane/metadata/android/%locale%/%original_file_name%
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
find metadata -name '*.txt' -exec sed -Ei 's/^[–—─•·*]\s+/- /' {} \;
|
|
||||||
@@ -16,4 +16,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=false
|
android.enableJetifier=true
|
||||||
|
Before Width: | Height: | Size: 20 KiB |
@@ -5,41 +5,16 @@ plugins {
|
|||||||
android {
|
android {
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
manifestPlaceholders = [oAuthScheme:"moshidon-android-auth"]
|
|
||||||
archivesBaseName = "moshidon"
|
archivesBaseName = "moshidon"
|
||||||
applicationId "org.joinmastodon.android.moshinda"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 99
|
versionCode 87
|
||||||
versionName "1.2.0+fork.99.moshinda"
|
versionName "1.1.4+fork.87.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "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 {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// minifyEnabled true
|
// minifyEnabled true
|
||||||
@@ -50,24 +25,10 @@ android {
|
|||||||
debuggable true
|
debuggable true
|
||||||
versionNameSuffix '-debug'
|
versionNameSuffix '-debug'
|
||||||
applicationIdSuffix '.debug'
|
applicationIdSuffix '.debug'
|
||||||
manifestPlaceholders = [oAuthScheme:"moshidon-android-debug-auth"]
|
|
||||||
}
|
}
|
||||||
githubRelease{
|
githubRelease{
|
||||||
initWith release
|
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{
|
playRelease{
|
||||||
initWith release
|
initWith release
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
@@ -109,7 +70,6 @@ dependencies {
|
|||||||
implementation 'com.squareup:otto:1.3.8'
|
implementation 'com.squareup:otto:1.3.8'
|
||||||
implementation 'de.psdev:async-otto:1.0.3'
|
implementation 'de.psdev:async-otto:1.0.3'
|
||||||
implementation 'org.parceler:parceler-api:1.1.12'
|
implementation 'org.parceler:parceler-api:1.1.12'
|
||||||
implementation 'com.github.bottom-software-foundation:bottom-java:2.1.0'
|
|
||||||
annotationProcessor 'org.parceler:parceler:1.1.12'
|
annotationProcessor 'org.parceler:parceler:1.1.12'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="org.joinmastodon.android">
|
package="org.joinmastodon.android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
|
|
||||||
|
<application>
|
||||||
<application
|
|
||||||
tools:replace="android:label"
|
|
||||||
android:label="@string/mo_app_name_debug">
|
|
||||||
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$InstallerStatusReceiver" android:exported="false"/>-->
|
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$InstallerStatusReceiver" android:exported="false"/>-->
|
||||||
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$AfterUpdateRestartReceiver" android:exported="true" android:enabled="false">-->
|
<!-- <receiver android:name=".updater.GithubSelfUpdaterImpl$AfterUpdateRestartReceiver" android:exported="true" android:enabled="false">-->
|
||||||
<!-- <intent-filter>-->
|
<!-- <intent-filter>-->
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
@@ -14,14 +14,12 @@ import android.os.Build;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
@@ -115,70 +113,64 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
|||||||
|
|
||||||
private void actuallyCheckForUpdates(){
|
private void actuallyCheckForUpdates(){
|
||||||
Request req=new Request.Builder()
|
Request req=new Request.Builder()
|
||||||
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
|
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases/latest")
|
||||||
.build();
|
.build();
|
||||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||||
try(Response resp=call.execute()){
|
try(Response resp=call.execute()){
|
||||||
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
|
JsonObject obj=JsonParser.parseReader(resp.body().charStream()).getAsJsonObject();
|
||||||
for (JsonElement jsonElement : arr) {
|
String changelog=obj.get("body").getAsString();
|
||||||
JsonObject obj = jsonElement.getAsJsonObject();
|
String tag=obj.get("tag_name").getAsString();
|
||||||
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
|
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();
|
||||||
|
|
||||||
String tag=obj.get("tag_name").getAsString();
|
UpdateInfo info=new UpdateInfo();
|
||||||
String changelog=obj.get("body").getAsString();
|
info.size=size;
|
||||||
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
info.version=version;
|
||||||
Matcher matcher=pattern.matcher(tag);
|
info.changelog=changelog;
|
||||||
if(!matcher.find()){
|
this.info=info;
|
||||||
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();
|
getPrefs().edit()
|
||||||
info.size=size;
|
.putLong("apkSize", size)
|
||||||
info.version=version;
|
.putString("version", version)
|
||||||
info.changelog=changelog;
|
.putString("apkURL", url)
|
||||||
this.info=info;
|
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
||||||
|
.putString("changelog", changelog)
|
||||||
|
.remove("downloadID")
|
||||||
|
.apply();
|
||||||
|
|
||||||
getPrefs().edit()
|
break;
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
||||||
}catch(Exception x){
|
}catch(Exception x){
|
||||||
Log.w(TAG, "actuallyCheckForUpdates", x);
|
Log.w(TAG, "actuallyCheckForUpdates", x);
|
||||||
}finally{
|
}finally{
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -12,16 +12,6 @@
|
|||||||
|
|
||||||
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
|
||||||
<data android:mimeType="text/plain" />
|
|
||||||
</intent>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.TRANSLATE" />
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MastodonApp"
|
android:name=".MastodonApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -44,11 +34,10 @@
|
|||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="${oAuthScheme}" android:host="callback"/>
|
<data android:scheme="moshidon-android-auth" android:host="callback"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize"
|
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
||||||
android:theme="@style/TransparentDialog">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|||||||
@@ -83,7 +83,3 @@ mirr0r.city underage
|
|||||||
nnia.space underage
|
nnia.space underage
|
||||||
ignorelist.com malicious
|
ignorelist.com malicious
|
||||||
repl.co malicious
|
repl.co malicious
|
||||||
|
|
||||||
# custom
|
|
||||||
|
|
||||||
pawoo.net csam
|
|
||||||
|
|||||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,83 +0,0 @@
|
|||||||
package org.joinmastodon.android;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
|
||||||
import android.content.pm.ShortcutManager;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.browser.customtabs.CustomTabsIntent;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
|
||||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
|
||||||
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
|
||||||
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.model.Application;
|
|
||||||
import org.joinmastodon.android.model.Emoji;
|
|
||||||
import org.joinmastodon.android.model.EmojiCategory;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
|
||||||
import org.joinmastodon.android.model.Token;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package org.joinmastodon.android;
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -12,9 +12,8 @@ import android.widget.Toast;
|
|||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.jsoup.internal.StringUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -29,23 +28,21 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
UiUtils.setUserPreferredTheme(this);
|
UiUtils.setUserPreferredTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
|
|
||||||
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
boolean isMastodonURL = UiUtils.looksLikeMastodonUrl(text);
|
|
||||||
|
|
||||||
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
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();
|
Toast.makeText(this, R.string.err_not_logged_in, Toast.LENGTH_SHORT).show();
|
||||||
finish();
|
finish();
|
||||||
}else if(sessions.size()==1 && !isMastodonURL){
|
}else if(sessions.size()==1){
|
||||||
openComposeFragment(sessions.get(0).getID());
|
openComposeFragment(sessions.get(0).getID());
|
||||||
}else{
|
}else{
|
||||||
new AccountSwitcherSheet(this, false, false, isMastodonURL, accountSession -> {
|
getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000));
|
||||||
if(accountSession!=null)
|
new M3AlertDialogBuilder(this)
|
||||||
openComposeFragment(accountSession.getID());
|
.setItems(sessions.stream().map(as->"@"+as.self.username+"@"+as.domain).toArray(String[]::new), (dialog, which)->{
|
||||||
else
|
openComposeFragment(sessions.get(which).getID());
|
||||||
UiUtils.openURL(this, AccountSessionManager.getInstance().getLastActiveAccountID(), text);
|
})
|
||||||
}).show();
|
.setTitle(R.string.choose_account)
|
||||||
|
.setOnCancelListener(dialog -> finish())
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,15 +55,9 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
String subject = "";
|
String subject = "";
|
||||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
|
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
|
||||||
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
||||||
if (!StringUtil.isBlank(subject)) builder.append(subject).append("\n\n");
|
if (!subject.isBlank()) builder.append(subject).append("\n\n");
|
||||||
}
|
|
||||||
if (intent.hasExtra(Intent.EXTRA_TEXT)) {
|
|
||||||
String extra = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
if (!StringUtil.isBlank(extra)) {
|
|
||||||
if (extra.startsWith(subject)) extra = extra.substring(subject.length()).trim();
|
|
||||||
builder.append(extra).append("\n\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
|
||||||
String text=builder.toString();
|
String text=builder.toString();
|
||||||
List<Uri> mediaUris;
|
List<Uri> mediaUris;
|
||||||
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
||||||
@@ -93,7 +84,8 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
if(!TextUtils.isEmpty(text))
|
if(!TextUtils.isEmpty(text))
|
||||||
args.putString("prefilledText", text);
|
args.putString("prefilledText", text);
|
||||||
args.putInt("selectionStart", StringUtil.isBlank(subject) ? 0 : subject.length());
|
if(!subject.isBlank())
|
||||||
|
args.putInt("selectionEnd", subject.length());
|
||||||
if(mediaUris!=null && !mediaUris.isEmpty())
|
if(mediaUris!=null && !mediaUris.isEmpty())
|
||||||
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
||||||
Fragment fragment=new ComposeFragment();
|
Fragment fragment=new ComposeFragment();
|
||||||
@@ -108,11 +100,4 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
return null;
|
return null;
|
||||||
return new ArrayList<>(l);
|
return new ArrayList<>(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent outContent) {
|
|
||||||
super.onProvideAssistContent(outContent);
|
|
||||||
|
|
||||||
outContent.setWebUri(Uri.parse(DomainManager.getInstance().getCurrentDomain()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,10 @@ import android.os.Build;
|
|||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GlobalUserPreferences{
|
public class GlobalUserPreferences{
|
||||||
public static boolean playGifs;
|
public static boolean playGifs;
|
||||||
@@ -25,7 +21,7 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean showReplies;
|
public static boolean showReplies;
|
||||||
public static boolean showBoosts;
|
public static boolean showBoosts;
|
||||||
public static boolean loadNewPosts;
|
public static boolean loadNewPosts;
|
||||||
public static boolean showNewPostsButton;
|
public static boolean showFederatedTimeline;
|
||||||
public static boolean showInteractionCounts;
|
public static boolean showInteractionCounts;
|
||||||
public static boolean alwaysExpandContentWarnings;
|
public static boolean alwaysExpandContentWarnings;
|
||||||
public static boolean disableMarquee;
|
public static boolean disableMarquee;
|
||||||
@@ -38,48 +34,21 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean reduceMotion;
|
public static boolean reduceMotion;
|
||||||
public static boolean keepOnlyLatestNotification;
|
public static boolean keepOnlyLatestNotification;
|
||||||
public static boolean enableFabAutoHide;
|
public static boolean enableFabAutoHide;
|
||||||
public static boolean disableAltTextReminder;
|
|
||||||
public static boolean showAltIndicator;
|
|
||||||
public static boolean showNoAltIndicator;
|
|
||||||
public static boolean enablePreReleases;
|
|
||||||
public static boolean prefixRepliesWithRe;
|
|
||||||
public static boolean bottomEncoding;
|
|
||||||
public static boolean collapseLongPosts;
|
|
||||||
public static boolean spectatorMode;
|
|
||||||
public static boolean autoHideFab;
|
|
||||||
public static boolean defaultToUnlistedReplies;
|
|
||||||
public static boolean disableDoubleTapToSwipe;
|
|
||||||
public static boolean compactReblogReplyLine;
|
|
||||||
public static boolean confirmBeforeReblog;
|
|
||||||
public static boolean replyLineAboveHeader;
|
|
||||||
public static boolean swapBookmarkWithBoostAction;
|
|
||||||
public static boolean loadRemoteAccountFollowers;
|
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
|
||||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||||
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
|
||||||
public static Map<String, List<String>> recentLanguages;
|
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();
|
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
||||||
public static Map<String, Integer> recentEmojis;
|
public static Map<String, Integer> recentEmojis;
|
||||||
|
|
||||||
/**
|
private static SharedPreferences getPrefs(){
|
||||||
* Pleroma
|
|
||||||
*/
|
|
||||||
public static String replyVisibility;
|
|
||||||
|
|
||||||
|
|
||||||
public static SharedPreferences getPrefs(){
|
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T fromJson(String json, Type type, T orElse) {
|
private static <T> T fromJson(String json, Type type, T orElse) {
|
||||||
if (json == null) return orElse;
|
|
||||||
try { return gson.fromJson(json, type); }
|
try { return gson.fromJson(json, type); }
|
||||||
catch (JsonSyntaxException ignored) { return orElse; }
|
catch (JsonSyntaxException ignored) { return orElse; }
|
||||||
}
|
}
|
||||||
@@ -92,8 +61,8 @@ public class GlobalUserPreferences{
|
|||||||
showReplies=prefs.getBoolean("showReplies", true);
|
showReplies=prefs.getBoolean("showReplies", true);
|
||||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||||
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
|
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
|
||||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
|
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
|
||||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||||
@@ -101,36 +70,14 @@ public class GlobalUserPreferences{
|
|||||||
disableDividers=prefs.getBoolean("disableDividers", true);
|
disableDividers=prefs.getBoolean("disableDividers", true);
|
||||||
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
|
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
|
||||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||||
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", false);
|
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", true);
|
||||||
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
enableFabAutoHide =prefs.getBoolean("enableFabAutoHide", true);
|
||||||
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
|
||||||
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
|
||||||
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
|
||||||
enablePreReleases=prefs.getBoolean("enablePreReleases", false);
|
|
||||||
prefixRepliesWithRe=prefs.getBoolean("prefixRepliesWithRe", false);
|
|
||||||
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
|
||||||
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);
|
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
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);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||||
@@ -151,7 +98,7 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("showReplies", showReplies)
|
.putBoolean("showReplies", showReplies)
|
||||||
.putBoolean("showBoosts", showBoosts)
|
.putBoolean("showBoosts", showBoosts)
|
||||||
.putBoolean("loadNewPosts", loadNewPosts)
|
.putBoolean("loadNewPosts", loadNewPosts)
|
||||||
.putBoolean("showNewPostsButton", showNewPostsButton)
|
.putBoolean("showFederatedTimeline", showFederatedTimeline)
|
||||||
.putBoolean("trueBlackTheme", trueBlackTheme)
|
.putBoolean("trueBlackTheme", trueBlackTheme)
|
||||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||||
@@ -164,31 +111,11 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("reduceMotion", reduceMotion)
|
.putBoolean("reduceMotion", reduceMotion)
|
||||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||||
.putBoolean("enableFabAutoHide", enableFabAutoHide)
|
.putBoolean("enableFabAutoHide", enableFabAutoHide)
|
||||||
.putBoolean("disableAltTextReminder", disableAltTextReminder)
|
|
||||||
.putBoolean("showAltIndicator", showAltIndicator)
|
|
||||||
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
|
||||||
.putBoolean("enablePreReleases", enablePreReleases)
|
|
||||||
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
|
||||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
|
||||||
.putBoolean("spectatorMode", spectatorMode)
|
|
||||||
.putBoolean("autoHideFab", autoHideFab)
|
|
||||||
.putString("publishButtonText", publishButtonText)
|
.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)
|
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putString("color", color.name())
|
.putString("color", color.name())
|
||||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||||
.putString("pinnedTimelines", gson.toJson(pinnedTimelines))
|
|
||||||
.putString("recentEmojis", gson.toJson(recentEmojis))
|
.putString("recentEmojis", gson.toJson(recentEmojis))
|
||||||
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
|
||||||
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
|
||||||
.putString("replyVisibility", replyVisibility)
|
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package org.joinmastodon.android;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -19,6 +17,7 @@ import org.joinmastodon.android.fragments.ProfileFragment;
|
|||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
@@ -28,7 +27,6 @@ import androidx.annotation.Nullable;
|
|||||||
import me.grishka.appkit.FragmentStackActivity;
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
|
|
||||||
public class MainActivity extends FragmentStackActivity{
|
public class MainActivity extends FragmentStackActivity{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState){
|
protected void onCreate(@Nullable Bundle savedInstanceState){
|
||||||
UiUtils.setUserPreferredTheme(this);
|
UiUtils.setUserPreferredTheme(this);
|
||||||
@@ -83,7 +81,6 @@ public class MainActivity extends FragmentStackActivity{
|
|||||||
AccountSession accountSession;
|
AccountSession accountSession;
|
||||||
try{
|
try{
|
||||||
accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
DomainManager.getInstance().setCurrentDomain(accountSession.domain);
|
|
||||||
}catch(IllegalStateException x){
|
}catch(IllegalStateException x){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -158,7 +155,6 @@ public class MainActivity extends FragmentStackActivity{
|
|||||||
);
|
);
|
||||||
Bundle currentArgs = currentFragment.getArguments();
|
Bundle currentArgs = currentFragment.getArguments();
|
||||||
if (this.fragmentContainers.size() == 1
|
if (this.fragmentContainers.size() == 1
|
||||||
&& currentArgs != null
|
|
||||||
&& currentArgs.getBoolean("_can_go_back", false)
|
&& currentArgs.getBoolean("_can_go_back", false)
|
||||||
&& currentArgs.containsKey("account")) {
|
&& currentArgs.containsKey("account")) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@@ -171,11 +167,4 @@ public class MainActivity extends FragmentStackActivity{
|
|||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent outContent) {
|
|
||||||
super.onProvideAssistContent(outContent);
|
|
||||||
|
|
||||||
outContent.setWebUri(Uri.parse(DomainManager.getInstance().getCurrentDomain()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
package org.joinmastodon.android;
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.getPrefs;
|
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationChannelGroup;
|
import android.app.NotificationChannelGroup;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.RemoteInput;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.opengl.Visibility;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -20,27 +16,15 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
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.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.NotificationAction;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -53,14 +37,11 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
private static final String TAG="PushNotificationReceive";
|
private static final String TAG="PushNotificationReceive";
|
||||||
|
|
||||||
public static final int NOTIFICATION_ID=178;
|
public static final int NOTIFICATION_ID=178;
|
||||||
private static final String ACTION_KEY_TEXT_REPLY = "ACTION_KEY_TEXT_REPLY";
|
|
||||||
|
|
||||||
private static final int SUMMARY_ID = 791;
|
private static final int SUMMARY_ID = 791;
|
||||||
private static int notificationId;
|
private static int notificationId = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent){
|
public void onReceive(Context context, Intent intent){
|
||||||
UiUtils.setUserPreferredTheme(context);
|
|
||||||
if(BuildConfig.DEBUG){
|
if(BuildConfig.DEBUG){
|
||||||
Log.e(TAG, "received: "+intent);
|
Log.e(TAG, "received: "+intent);
|
||||||
Bundle extras=intent.getExtras();
|
Bundle extras=intent.getExtras();
|
||||||
@@ -90,7 +71,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
}
|
}
|
||||||
String accountID=account.getID();
|
String accountID=account.getID();
|
||||||
PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s);
|
PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s);
|
||||||
E.post(new NotificationReceivedEvent(accountID, pn.notificationId+""));
|
|
||||||
new GetNotificationByID(pn.notificationId+"")
|
new GetNotificationByID(pn.notificationId+"")
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
@@ -112,43 +92,14 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
Log.w(TAG, "onReceive: invalid push notification format");
|
Log.w(TAG, "onReceive: invalid push notification format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(intent.getBooleanExtra("fromNotificationAction", false)){
|
|
||||||
String accountID=intent.getStringExtra("accountID");
|
|
||||||
int notificationId=intent.getIntExtra("notificationId", -1);
|
|
||||||
|
|
||||||
if (notificationId >= 0){
|
|
||||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.cancel(accountID, notificationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(intent.hasExtra("notification")){
|
|
||||||
org.joinmastodon.android.model.Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
|
||||||
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 REPLY -> handleReplyAction(context, accountID, intent, notification, notificationId, preferences);
|
|
||||||
default -> Log.w(TAG, "onReceive: Failed to get NotificationAction");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
Log.e(TAG, "onReceive: Failed to load notification");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||||
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
||||||
notificationId=getPrefs().getInt("latestNotificationId", 0);
|
|
||||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
Notification.Builder builder;
|
Notification.Builder builder;
|
||||||
|
Notification.Builder summaryNotification;
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
||||||
boolean hasGroup=false;
|
boolean hasGroup=false;
|
||||||
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
|
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
|
||||||
@@ -171,42 +122,48 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
nm.createNotificationChannels(channels);
|
nm.createNotificationChannels(channels);
|
||||||
}
|
}
|
||||||
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
|
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
|
||||||
|
// summaryNotification=new Notification.Builder(context, accountID);
|
||||||
}else{
|
}else{
|
||||||
builder=new Notification.Builder(context)
|
builder=new Notification.Builder(context)
|
||||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||||
|
summaryNotification=new Notification.Builder(context)
|
||||||
|
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||||
|
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||||
}
|
}
|
||||||
Drawable avatar=ImageCache.getInstance(context).get(new UrlImageLoaderRequest(pn.icon, V.dp(50), V.dp(50)));
|
Drawable avatar=ImageCache.getInstance(context).get(new UrlImageLoaderRequest(pn.icon, V.dp(50), V.dp(50)));
|
||||||
Intent contentIntent=new Intent(context, MainActivity.class);
|
Intent contentIntent=new Intent(context, MainActivity.class);
|
||||||
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
contentIntent.putExtra("fromNotification", true);
|
contentIntent.putExtra("fromNotification", true);
|
||||||
contentIntent.putExtra("accountID", accountID);
|
contentIntent.putExtra("accountID", accountID);
|
||||||
|
contentIntent.putExtra("notificationID", notificationId);
|
||||||
if(notification!=null){
|
if(notification!=null){
|
||||||
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setContentTitle(pn.title)
|
builder.setContentTitle(pn.title)
|
||||||
.setContentText(pn.body)
|
.setContentText(pn.body)
|
||||||
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
.setContentTitle(pn.title)
|
||||||
.setSmallIcon(R.drawable.ic_ntf_logo)
|
.setStyle(new Notification.InboxStyle()
|
||||||
|
.addLine(pn.body))
|
||||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
|
.setGroup(accountID)
|
||||||
.setColor(context.getColor(R.color.shortcut_icon_background));
|
.setColor(context.getColor(R.color.shortcut_icon_background));
|
||||||
|
if(!GlobalUserPreferences.uniformNotificationIcon){
|
||||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
switch (pn.notificationType) {
|
||||||
builder.setSmallIcon(switch (pn.notificationType) {
|
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
|
||||||
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
|
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
|
||||||
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
|
||||||
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
|
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
|
||||||
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
|
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
|
||||||
case POLL -> R.drawable.ic_fluent_poll_24_filled;
|
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
||||||
case STATUS -> R.drawable.ic_fluent_chat_24_filled;
|
}
|
||||||
case UPDATE -> R.drawable.ic_fluent_history_24_filled;
|
}else{
|
||||||
case REPORT -> R.drawable.ic_fluent_warning_24_filled;
|
builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
||||||
case SIGN_UP -> R.drawable.ic_fluent_person_available_24_filled;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(avatar!=null){
|
if(avatar!=null){
|
||||||
@@ -216,96 +173,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
builder.setSubText(accountName);
|
builder.setSubText(accountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId++;
|
notificationId++;
|
||||||
getPrefs().edit().putInt("latestNotificationId", notificationId).apply();
|
nm.notify(accountID, GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId, builder.build());
|
||||||
|
|
||||||
if (notification != null){
|
|
||||||
switch (pn.notificationType){
|
|
||||||
case MENTION, STATUS -> {
|
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case UPDATE -> {
|
|
||||||
if(notification.status.reblogged)
|
|
||||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNBOOST));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nm.notify(accountID, id, builder.build());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private Notification.Action buildNotificationAction(Context context, int notificationId, String accountID, org.joinmastodon.android.model.Notification notification, String title, NotificationAction action){
|
|
||||||
Intent notificationIntent=new Intent(context, PushNotificationReceiver.class);
|
|
||||||
notificationIntent.putExtra("notificationId", notificationId);
|
|
||||||
notificationIntent.putExtra("fromNotificationAction", true);
|
|
||||||
notificationIntent.putExtra("accountID", accountID);
|
|
||||||
notificationIntent.putExtra("notificationAction", action.ordinal());
|
|
||||||
notificationIntent.putExtra("notification", Parcels.wrap(notification));
|
|
||||||
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, new Random().nextInt(), notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
|
|
||||||
|
|
||||||
return new Notification.Action.Builder(null, title, actionPendingIntent).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Notification.Action buildReplyAction(Context context, int notificationId, String accountID, org.joinmastodon.android.model.Notification notification){
|
|
||||||
String replyLabel = context.getResources().getString(R.string.button_reply);
|
|
||||||
RemoteInput remoteInput = new RemoteInput.Builder(ACTION_KEY_TEXT_REPLY)
|
|
||||||
.setLabel(replyLabel)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Intent notificationIntent=new Intent(context, PushNotificationReceiver.class);
|
|
||||||
notificationIntent.putExtra("notificationId", notificationId);
|
|
||||||
notificationIntent.putExtra("fromNotificationAction", true);
|
|
||||||
notificationIntent.putExtra("accountID", accountID);
|
|
||||||
notificationIntent.putExtra("notificationAction", NotificationAction.REPLY.ordinal());
|
|
||||||
notificationIntent.putExtra("notification", Parcels.wrap(notification));
|
|
||||||
|
|
||||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT : PendingIntent.FLAG_UPDATE_CURRENT;
|
|
||||||
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context, new Random().nextInt(), notificationIntent,flags);
|
|
||||||
return new Notification.Action.Builder(null, replyLabel, replyPendingIntent).addRemoteInput(remoteInput).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleReplyAction(Context context, String accountID, Intent intent, org.joinmastodon.android.model.Notification notification, int notificationId, Preferences preferences) {
|
|
||||||
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
|
||||||
if (remoteInput == null) {
|
|
||||||
Log.e(TAG, "handleReplyAction: Could not get reply input");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CharSequence input = remoteInput.getCharSequence(ACTION_KEY_TEXT_REPLY);
|
|
||||||
|
|
||||||
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.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);
|
|
||||||
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class ApiUtils{
|
|||||||
//no instance
|
//no instance
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Enum<E>> List<String> enumSetToStrings(EnumSet<E> e, Class<E> cls){
|
public static <E extends Enum<E>> List<String> enumSetToStrings(EnumSet<E> e, Class<E> cls){
|
||||||
return e.stream().map(ev->{
|
return e.stream().map(ev->{
|
||||||
try{
|
try{
|
||||||
SerializedName annotation=cls.getField(ev.name()).getAnnotation(SerializedName.class);
|
SerializedName annotation=cls.getField(ev.name()).getAnnotation(SerializedName.class);
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ import org.joinmastodon.android.BuildConfig;
|
|||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
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.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.SearchResult;
|
import org.joinmastodon.android.model.SearchResult;
|
||||||
@@ -128,12 +126,11 @@ public class CacheController{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<CacheablePaginatedResponse<List<Notification>>> callback){
|
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<PaginatedResponse<List<Notification>>> callback){
|
||||||
cancelDelayedClose();
|
cancelDelayedClose();
|
||||||
databaseThread.postRunnable(()->{
|
databaseThread.postRunnable(()->{
|
||||||
try{
|
try{
|
||||||
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
||||||
List<Filter> filters=accountSession.wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
|
||||||
if(!forceReload){
|
if(!forceReload){
|
||||||
SQLiteDatabase db=getOrOpenDatabase();
|
SQLiteDatabase db=getOrOpenDatabase();
|
||||||
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
|
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
|
||||||
@@ -156,19 +153,18 @@ public class CacheController{
|
|||||||
result.add(ntf);
|
result.add(ntf);
|
||||||
}while(cursor.moveToNext());
|
}while(cursor.moveToNext());
|
||||||
String _newMaxID=newMaxID;
|
String _newMaxID=newMaxID;
|
||||||
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
uiHandler.post(()->callback.onSuccess(new PaginatedResponse<>(result, _newMaxID)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}catch(IOException x){
|
}catch(IOException x){
|
||||||
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
|
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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))
|
||||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.pleroma != null)
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Notification> result){
|
public void onSuccess(List<Notification> result){
|
||||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(ntf->{
|
callback.onSuccess(new PaginatedResponse<>(result.stream().filter(ntf->{
|
||||||
if(ntf.status!=null){
|
if(ntf.status!=null){
|
||||||
for(Filter filter:filters){
|
for(Filter filter:filters){
|
||||||
if(filter.matches(ntf.status)){
|
if(filter.matches(ntf.status)){
|
||||||
@@ -177,7 +173,7 @@ public class CacheController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id));
|
||||||
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
|
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.joinmastodon.android.MastodonApp;
|
|||||||
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
||||||
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -41,15 +40,12 @@ import okhttp3.ResponseBody;
|
|||||||
|
|
||||||
public class MastodonAPIController{
|
public class MastodonAPIController{
|
||||||
private static final String TAG="MastodonAPIController";
|
private static final String TAG="MastodonAPIController";
|
||||||
public static final Gson gsonWithoutDeserializer = new GsonBuilder()
|
public static final Gson gson=new GsonBuilder()
|
||||||
.disableHtmlEscaping()
|
.disableHtmlEscaping()
|
||||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
|
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
|
||||||
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
|
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
|
||||||
.create();
|
.create();
|
||||||
public static final Gson gson = gsonWithoutDeserializer.newBuilder()
|
|
||||||
.registerTypeAdapter(Status.class, new Status.StatusDeserializer())
|
|
||||||
.create();
|
|
||||||
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
||||||
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
|
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
|
||||||
|
|
||||||
@@ -85,7 +81,7 @@ public class MastodonAPIController{
|
|||||||
final boolean isBad = host == null || badDomains.stream().anyMatch(h -> h.equalsIgnoreCase(host) || host.toLowerCase().endsWith("." + h));
|
final boolean isBad = host == null || badDomains.stream().anyMatch(h -> h.equalsIgnoreCase(host) || host.toLowerCase().endsWith("." + h));
|
||||||
thread.postRunnable(()->{
|
thread.postRunnable(()->{
|
||||||
try{
|
try{
|
||||||
// if (isBad) throw new IllegalArgumentException();
|
if (isBad) throw new IllegalArgumentException();
|
||||||
if(req.canceled)
|
if(req.canceled)
|
||||||
return;
|
return;
|
||||||
Request.Builder builder=new Request.Builder()
|
Request.Builder builder=new Request.Builder()
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ public class PushSubscriptionManager{
|
|||||||
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||||
if(session.pushSubscription==null || forceReRegister)
|
if(session.pushSubscription==null || forceReRegister)
|
||||||
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
|
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
|
||||||
else
|
else if(session.needUpdatePushSettings)
|
||||||
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
|
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,10 +4,6 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
|
||||||
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
||||||
public SetAccountFollowed(String id, boolean followed, boolean showReblogs){
|
|
||||||
this(id, followed, showReblogs, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
||||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
||||||
if(followed)
|
if(followed)
|
||||||
|
|||||||
@@ -4,15 +4,8 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
|
||||||
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
||||||
public SetAccountMuted(String id, boolean muted, long duration){
|
public SetAccountMuted(String id, boolean muted){
|
||||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
||||||
setRequestBody(muted ? new Request(duration): new Object());
|
setRequestBody(new Object());
|
||||||
}
|
|
||||||
|
|
||||||
private static class Request{
|
|
||||||
public long duration;
|
|
||||||
public Request(long duration){
|
|
||||||
this.duration=duration;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.lists;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
|
|
||||||
public class GetList extends MastodonAPIRequest<ListTimeline> {
|
|
||||||
public GetList(String id) {
|
|
||||||
super(HttpMethod.GET, "/lists/" + id, ListTimeline.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.markers;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.ApiUtils;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.Marker;
|
|
||||||
import org.joinmastodon.android.model.Markers;
|
|
||||||
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
public class GetMarkers extends MastodonAPIRequest<Markers> {
|
|
||||||
public GetMarkers(EnumSet<Marker.Type> timelines) {
|
|
||||||
super(HttpMethod.GET, "/markers", Markers.class);
|
|
||||||
for (String type : ApiUtils.enumSetToStrings(timelines, Marker.Type.class)){
|
|
||||||
addQueryParameter("timeline[]", type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.joinmastodon.android.api.requests.notifications;
|
package org.joinmastodon.android.api.requests.notifications;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.ApiUtils;
|
import org.joinmastodon.android.api.ApiUtils;
|
||||||
@@ -10,24 +11,18 @@ import java.util.EnumSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
|
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
|
||||||
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes, boolean isPleromaInstance){
|
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes){
|
||||||
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
|
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
|
||||||
if(maxID!=null)
|
if(maxID!=null)
|
||||||
addQueryParameter("max_id", maxID);
|
addQueryParameter("max_id", maxID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(includeTypes!=null){
|
if(includeTypes!=null){
|
||||||
if(!isPleromaInstance) {
|
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
||||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
addQueryParameter("types[]", type);
|
||||||
addQueryParameter("types[]", type);
|
}
|
||||||
}
|
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
||||||
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
addQueryParameter("exclude_types[]", type);
|
||||||
addQueryParameter("exclude_types[]", type);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
|
||||||
addQueryParameter("include_types[]", type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeUnsupportedItems=true;
|
removeUnsupportedItems=true;
|
||||||
|
|||||||
@@ -39,14 +39,11 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
|||||||
public Poll poll;
|
public Poll poll;
|
||||||
public String inReplyToId;
|
public String inReplyToId;
|
||||||
public boolean sensitive;
|
public boolean sensitive;
|
||||||
public boolean localOnly;
|
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
public StatusPrivacy visibility;
|
public StatusPrivacy visibility;
|
||||||
public Instant scheduledAt;
|
public Instant scheduledAt;
|
||||||
public String language;
|
public String language;
|
||||||
|
|
||||||
public String quoteId;
|
|
||||||
|
|
||||||
public static class Poll{
|
public static class Poll{
|
||||||
public ArrayList<String> options=new ArrayList<>();
|
public ArrayList<String> options=new ArrayList<>();
|
||||||
public int expiresIn;
|
public int expiresIn;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.api.requests.timelines;
|
|||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -19,7 +18,5 @@ public class GetHomeTimeline extends MastodonAPIRequest<List<Status>>{
|
|||||||
addQueryParameter("since_id", sinceID);
|
addQueryParameter("since_id", sinceID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
|||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Markers;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
@@ -32,7 +31,6 @@ public class AccountSession{
|
|||||||
public String pushAccountID;
|
public String pushAccountID;
|
||||||
public Preferences preferences;
|
public Preferences preferences;
|
||||||
public AccountActivationInfo activationInfo;
|
public AccountActivationInfo activationInfo;
|
||||||
public Markers markers;
|
|
||||||
private transient MastodonAPIController apiController;
|
private transient MastodonAPIController apiController;
|
||||||
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
|
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
|
||||||
private transient CacheController cacheController;
|
private transient CacheController cacheController;
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
|||||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
import org.joinmastodon.android.api.requests.markers.GetMarkers;
|
|
||||||
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
||||||
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -34,8 +33,6 @@ import org.joinmastodon.android.model.Emoji;
|
|||||||
import org.joinmastodon.android.model.EmojiCategory;
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Marker;
|
|
||||||
import org.joinmastodon.android.model.Markers;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
|
||||||
@@ -49,7 +46,6 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -65,7 +61,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
public class AccountSessionManager{
|
public class AccountSessionManager{
|
||||||
private static final String TAG="AccountSessionManager";
|
private static final String TAG="AccountSessionManager";
|
||||||
public static final String SCOPE="read write follow push";
|
public static final String SCOPE="read write follow push";
|
||||||
public static final String REDIRECT_URI = getRedirectURI();
|
public static final String REDIRECT_URI="moshidon-android-auth://callback";
|
||||||
|
|
||||||
private static final AccountSessionManager instance=new AccountSessionManager();
|
private static final AccountSessionManager instance=new AccountSessionManager();
|
||||||
|
|
||||||
@@ -84,17 +80,6 @@ public class AccountSessionManager{
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRedirectURI() {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
builder.append("moshidon-android-");
|
|
||||||
if (BuildConfig.BUILD_TYPE.equals("debug") || BuildConfig.BUILD_TYPE.equals("nightly")) {
|
|
||||||
builder.append(BuildConfig.BUILD_TYPE);
|
|
||||||
builder.append('-');
|
|
||||||
}
|
|
||||||
builder.append("auth://callback");
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountSessionManager(){
|
private AccountSessionManager(){
|
||||||
prefs=MastodonApp.context.getSharedPreferences("account_manager", Context.MODE_PRIVATE);
|
prefs=MastodonApp.context.getSharedPreferences("account_manager", Context.MODE_PRIVATE);
|
||||||
File file=new File(MastodonApp.context.getFilesDir(), "accounts.json");
|
File file=new File(MastodonApp.context.getFilesDir(), "accounts.json");
|
||||||
@@ -226,7 +211,7 @@ public class AccountSessionManager{
|
|||||||
.path("/oauth/authorize")
|
.path("/oauth/authorize")
|
||||||
.appendQueryParameter("response_type", "code")
|
.appendQueryParameter("response_type", "code")
|
||||||
.appendQueryParameter("client_id", result.clientId)
|
.appendQueryParameter("client_id", result.clientId)
|
||||||
.appendQueryParameter("redirect_uri", REDIRECT_URI)
|
.appendQueryParameter("redirect_uri", "moshidon-android-auth://callback")
|
||||||
.appendQueryParameter("scope", SCOPE)
|
.appendQueryParameter("scope", SCOPE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -270,7 +255,6 @@ public class AccountSessionManager{
|
|||||||
// if(now-session.filtersLastUpdated>3600_000L){
|
// if(now-session.filtersLastUpdated>3600_000L){
|
||||||
updateSessionWordFilters(session);
|
updateSessionWordFilters(session);
|
||||||
// }
|
// }
|
||||||
updateSessionMarkers(session);
|
|
||||||
}
|
}
|
||||||
if(loadedInstances){
|
if(loadedInstances){
|
||||||
maybeUpdateCustomEmojis(domains);
|
maybeUpdateCustomEmojis(domains);
|
||||||
@@ -287,15 +271,6 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void preferencesFromSource(AccountSession session, Account account) {
|
|
||||||
if (account != null && account.source != null && session.preferences != null) {
|
|
||||||
if (account.source.privacy != null)
|
|
||||||
session.preferences.postingDefaultVisibility = account.source.privacy;
|
|
||||||
if (account.source.language != null)
|
|
||||||
session.preferences.postingDefaultLanguage = account.source.language;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSessionLocalInfo(AccountSession session){
|
private void updateSessionLocalInfo(AccountSession session){
|
||||||
new GetOwnAccount()
|
new GetOwnAccount()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@@ -303,12 +278,13 @@ public class AccountSessionManager{
|
|||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
session.self=result;
|
session.self=result;
|
||||||
session.infoLastUpdated=System.currentTimeMillis();
|
session.infoLastUpdated=System.currentTimeMillis();
|
||||||
preferencesFromSource(session, result);
|
|
||||||
writeAccountsFile();
|
writeAccountsFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){}
|
public void onError(ErrorResponse error){
|
||||||
|
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.exec(session.getID());
|
.exec(session.getID());
|
||||||
}
|
}
|
||||||
@@ -318,14 +294,10 @@ public class AccountSessionManager{
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Preferences preferences) {
|
public void onSuccess(Preferences preferences) {
|
||||||
session.preferences=preferences;
|
session.preferences=preferences;
|
||||||
preferencesFromSource(session, session.self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {}
|
||||||
session.preferences = new Preferences();
|
|
||||||
preferencesFromSource(session, session.self);
|
|
||||||
}
|
|
||||||
}).exec(session.getID());
|
}).exec(session.getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,21 +319,6 @@ public class AccountSessionManager{
|
|||||||
.exec(session.getID());
|
.exec(session.getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSessionMarkers(AccountSession session) {
|
|
||||||
new GetMarkers(EnumSet.allOf(Marker.Type.class)).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Markers markers) {
|
|
||||||
session.markers = markers;
|
|
||||||
writeAccountsFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}).exec(session.getID());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateInstanceInfo(String domain){
|
public void updateInstanceInfo(String domain){
|
||||||
new GetInstance()
|
new GetInstance()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class AllNotificationsSeenEvent {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class HashtagUpdatedEvent {
|
|
||||||
public final String name;
|
|
||||||
public final boolean following;
|
|
||||||
|
|
||||||
public HashtagUpdatedEvent(String name, boolean following) {
|
|
||||||
this.name = name;
|
|
||||||
this.following = following;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class ListDeletedEvent {
|
|
||||||
public final String id;
|
|
||||||
|
|
||||||
public ListDeletedEvent(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
|
|
||||||
public class ListUpdatedCreatedEvent {
|
|
||||||
public final String id;
|
|
||||||
public final String title;
|
|
||||||
public final ListTimeline.RepliesPolicy repliesPolicy;
|
|
||||||
|
|
||||||
public ListUpdatedCreatedEvent(String id, String title, ListTimeline.RepliesPolicy repliesPolicy) {
|
|
||||||
this.id = id;
|
|
||||||
this.title = title;
|
|
||||||
this.repliesPolicy = repliesPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class NotificationReceivedEvent {
|
|
||||||
public String account, id;
|
|
||||||
public NotificationReceivedEvent(String account, String id) {
|
|
||||||
this.account = account;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,15 +15,12 @@ import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
|||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
@@ -63,8 +60,8 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(getActivity()==null)
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.ACCOUNT)).collect(Collectors.toList());
|
return;
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -77,7 +77,12 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
public void onMarkAsRead(String id) {
|
public void onMarkAsRead(String id) {
|
||||||
if (unreadIDs == null) return;
|
if (unreadIDs == null) return;
|
||||||
unreadIDs.remove(id);
|
unreadIDs.remove(id);
|
||||||
if (unreadIDs.isEmpty()) setResult(true, null);
|
if (unreadIDs.size() == 0) setResult(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,13 +97,11 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Announcement> result){
|
public void onSuccess(List<Announcement> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
||||||
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
||||||
onDataLoaded(unread, true);
|
onDataLoaded(unread, true);
|
||||||
onDataLoaded(read, false);
|
onDataLoaded(read, false);
|
||||||
if (unread.isEmpty()) setResult(true, null);
|
unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
||||||
else unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import android.text.Layout;
|
|||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -21,14 +20,11 @@ import android.view.animation.TranslateAnimation;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.MainActivity;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||||
@@ -36,21 +32,20 @@ import org.joinmastodon.android.model.Poll;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
import org.joinmastodon.android.ui.TileGridLayoutManager;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||||
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.MediaGridLayout;
|
import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||||
import org.joinmastodon.android.utils.TypedObjectPool;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -61,9 +56,8 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
@@ -74,7 +68,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop, DomainDisplay{
|
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop{
|
||||||
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
||||||
protected DisplayItemsAdapter adapter;
|
protected DisplayItemsAdapter adapter;
|
||||||
protected String accountID;
|
protected String accountID;
|
||||||
@@ -84,23 +78,14 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
||||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||||
protected Rect tmpRect=new Rect();
|
protected Rect tmpRect=new Rect();
|
||||||
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
|
|
||||||
|
|
||||||
private final int THRESHOLD = 800;
|
|
||||||
|
|
||||||
public BaseStatusListFragment(){
|
public BaseStatusListFragment(){
|
||||||
super(20);
|
super(20);
|
||||||
if (withComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
UiUtils.loadMaxWidth(getContext());
|
|
||||||
if(GlobalUserPreferences.disableMarquee){
|
if(GlobalUserPreferences.disableMarquee){
|
||||||
setTitleMarqueeEnabled(false);
|
setTitleMarqueeEnabled(false);
|
||||||
setSubtitleMarqueeEnabled(false);
|
setSubtitleMarqueeEnabled(false);
|
||||||
@@ -109,8 +94,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
return adapter=new DisplayItemsAdapter();
|
return adapter=new DisplayItemsAdapter();
|
||||||
@@ -197,21 +180,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){
|
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex){
|
||||||
final Status status=_status.getContentStatus();
|
final Status status=_status.reblog!=null ? _status.reblog : _status;
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
|
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
|
||||||
private MediaAttachmentViewController transitioningHolder;
|
private ImageStatusDisplayItem.Holder<?> transitioningHolder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPhotoViewVisibility(int index, boolean visible){
|
public void setPhotoViewVisibility(int index, boolean visible){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
holder.photo.setAlpha(visible ? 1f : 0f);
|
holder.photo.setAlpha(visible ? 1f : 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null){
|
if(holder!=null){
|
||||||
transitioningHolder=holder;
|
transitioningHolder=holder;
|
||||||
View view=transitioningHolder.photo;
|
View view=transitioningHolder.photo;
|
||||||
@@ -219,8 +202,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
view.getLocationOnScreen(pos);
|
view.getLocationOnScreen(pos);
|
||||||
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
|
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
|
||||||
list.setClipChildren(false);
|
list.setClipChildren(false);
|
||||||
gridHolder.setClipChildren(false);
|
transitioningHolder.itemView.setElevation(1f);
|
||||||
transitioningHolder.view.setElevation(1f);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -247,16 +229,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
view.setTranslationY(0f);
|
view.setTranslationY(0f);
|
||||||
view.setScaleX(1f);
|
view.setScaleX(1f);
|
||||||
view.setScaleY(1f);
|
view.setScaleY(1f);
|
||||||
transitioningHolder.view.setElevation(0f);
|
transitioningHolder.itemView.setElevation(0f);
|
||||||
if(list!=null)
|
if(list!=null)
|
||||||
list.setClipChildren(true);
|
list.setClipChildren(true);
|
||||||
gridHolder.setClipChildren(true);
|
|
||||||
transitioningHolder=null;
|
transitioningHolder=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Drawable getPhotoViewCurrentDrawable(int index){
|
public Drawable getPhotoViewCurrentDrawable(int index){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
return holder.photo.getDrawable();
|
return holder.photo.getDrawable();
|
||||||
return null;
|
return null;
|
||||||
@@ -272,8 +253,23 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaAttachmentViewController findPhotoViewHolder(int index){
|
private ImageStatusDisplayItem.Holder<?> findPhotoViewHolder(int index){
|
||||||
return gridHolder.getViewController(index);
|
if(list==null)
|
||||||
|
return null;
|
||||||
|
int offset=0;
|
||||||
|
for(StatusDisplayItem item:displayItems){
|
||||||
|
if(item.parentID.equals(parentID)){
|
||||||
|
if(item instanceof ImageStatusDisplayItem){
|
||||||
|
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(getMainAdapterOffset()+offset+index);
|
||||||
|
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||||
|
return imgHolder;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -290,15 +286,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
currentPhotoViewer.offsetView(-dx, -dy);
|
currentPhotoViewer.offsetView(-dx, -dy);
|
||||||
|
|
||||||
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
||||||
// This piece of code should make it so that the fab is always visible if the status list scroll view is at the item at the top
|
if(dy > 0){
|
||||||
if(list.getChildAt(0).getTop() == 0){
|
scrollDiff = 0;
|
||||||
scrollDiff= THRESHOLD +1;
|
|
||||||
}else{
|
|
||||||
if(dy > 0){
|
|
||||||
scrollDiff=0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
0,
|
0,
|
||||||
@@ -306,30 +296,29 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
0,
|
0,
|
||||||
fab.getHeight() * 2);
|
fab.getHeight() * 2);
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
// animate.setFillAfter(true);
|
animate.setFillAfter(true);
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
fab.setEnabled(false);
|
|
||||||
fab.setVisibility(View.INVISIBLE);
|
fab.setVisibility(View.INVISIBLE);
|
||||||
scrollDiff = 0;
|
scrollDiff = 0;
|
||||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||||
if (scrollDiff > THRESHOLD) {
|
if (scrollDiff > 800) {
|
||||||
|
fab.setVisibility(View.VISIBLE);
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
fab.getHeight() * 2,
|
fab.getHeight() * 2,
|
||||||
0);
|
0);
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
// animate.setFillAfter(true);
|
animate.setFillAfter(true);
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
fab.setEnabled(true);
|
|
||||||
fab.setVisibility(View.VISIBLE);
|
|
||||||
scrollDiff = 0;
|
scrollDiff = 0;
|
||||||
} else {
|
} else {
|
||||||
scrollDiff += Math.abs(dy);
|
scrollDiff += Math.abs(dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
list.addItemDecoration(new StatusListItemDecoration());
|
list.addItemDecoration(new StatusListItemDecoration());
|
||||||
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
||||||
private Rect tmpRect=new Rect();
|
private Rect tmpRect=new Rect();
|
||||||
@@ -363,13 +352,31 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
list.setItemAnimator(new BetterItemAnimator());
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
if (withComposeButton()) {
|
@Override
|
||||||
fab = view.findViewById(R.id.fab);
|
protected RecyclerView.LayoutManager onCreateLayoutManager(){
|
||||||
fab.setVisibility(View.VISIBLE);
|
GridLayoutManager lm=new TileGridLayoutManager(getActivity(), 1000);
|
||||||
fab.setOnClickListener(this::onFabClick);
|
lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
|
||||||
fab.setOnLongClickListener(this::onFabLongClick);
|
@Override
|
||||||
}
|
public int getSpanSize(int position){
|
||||||
|
position-=getMainAdapterOffset();
|
||||||
|
if(position>=0 && position<displayItems.size()){
|
||||||
|
StatusDisplayItem item=displayItems.get(position);
|
||||||
|
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult layout=imgItem.tiledLayout;
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgItem.thisTile;
|
||||||
|
int spans=0;
|
||||||
|
for(int i=0;i<tile.colSpan;i++){
|
||||||
|
spans+=layout.columnSizes[tile.startCol+i];
|
||||||
|
}
|
||||||
|
return spans;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return lm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -420,7 +427,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
List<StatusDisplayItem> pollItems=displayItems.subList(firstOptionIndex, footerIndex+1);
|
List<StatusDisplayItem> pollItems=displayItems.subList(firstOptionIndex, footerIndex+1);
|
||||||
int prevSize=pollItems.size();
|
int prevSize=pollItems.size();
|
||||||
pollItems.clear();
|
pollItems.clear();
|
||||||
StatusDisplayItem.buildPollItems(itemID, this, poll, pollItems, status);
|
StatusDisplayItem.buildPollItems(itemID, this, poll, pollItems);
|
||||||
if(prevSize!=pollItems.size()){
|
if(prevSize!=pollItems.size()){
|
||||||
adapter.notifyItemRangeRemoved(firstOptionIndex, prevSize);
|
adapter.notifyItemRangeRemoved(firstOptionIndex, prevSize);
|
||||||
adapter.notifyItemRangeInserted(firstOptionIndex, pollItems.size());
|
adapter.notifyItemRangeInserted(firstOptionIndex, pollItems.size());
|
||||||
@@ -457,26 +464,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(holder.getItem().status.reloadWhenClicked){
|
|
||||||
Status queryStatus = holder.getItem().status;
|
|
||||||
UiUtils.lookupStatus(getContext(), queryStatus, accountID, null, status -> {
|
|
||||||
submitPollVote(holder.getItemID(), status.poll.id, poll.selectedOptions.stream().map(opt->poll.options.indexOf(opt)).collect(Collectors.toList()));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
submitPollVote(holder.getItemID(), poll.id, Collections.singletonList(poll.options.indexOf(option)));
|
submitPollVote(holder.getItemID(), poll.id, Collections.singletonList(poll.options.indexOf(option)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPollVoteButtonClick(PollFooterStatusDisplayItem.Holder holder){
|
public void onPollVoteButtonClick(PollFooterStatusDisplayItem.Holder holder){
|
||||||
Poll poll=holder.getItem().poll;
|
Poll poll=holder.getItem().poll;
|
||||||
if(holder.getItem().status.reloadWhenClicked){
|
|
||||||
Status queryStatus = holder.getItem().status;
|
|
||||||
UiUtils.lookupStatus(getContext(), queryStatus, accountID, null, status -> {
|
|
||||||
submitPollVote(holder.getItemID(), status.poll.id, poll.selectedOptions.stream().map(opt->poll.options.indexOf(opt)).collect(Collectors.toList()));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
submitPollVote(holder.getItemID(), poll.id, poll.selectedOptions.stream().map(opt->poll.options.indexOf(opt)).collect(Collectors.toList()));
|
submitPollVote(holder.getItemID(), poll.id, poll.selectedOptions.stream().map(opt->poll.options.indexOf(opt)).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +497,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRevealSpoilerClick(MediaGridStatusDisplayItem.Holder holder){
|
public void onRevealSpoilerClick(ImageStatusDisplayItem.Holder<?> holder){
|
||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
@@ -524,7 +517,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
status.spoilerRevealed=!status.spoilerRevealed;
|
status.spoilerRevealed=!status.spoilerRevealed;
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(status.spoilerText)){
|
||||||
TextStatusDisplayItem.Holder text = findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class);
|
TextStatusDisplayItem.Holder text=findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class);
|
||||||
if(text!=null){
|
if(text!=null){
|
||||||
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition());
|
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition());
|
||||||
}
|
}
|
||||||
@@ -533,32 +526,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
updateImagesSpoilerState(status, holder.getItemID());
|
updateImagesSpoilerState(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnableExpandable(TextStatusDisplayItem.Holder holder, boolean expandable) {
|
|
||||||
if (holder.getItem().status.textExpandable != expandable && list != null) {
|
|
||||||
holder.getItem().status.textExpandable = expandable;
|
|
||||||
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
|
||||||
if (header != null) header.rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onToggleExpanded(Status status, String itemID) {
|
|
||||||
status.textExpanded = !status.textExpanded;
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
|
||||||
if (text != null) text.rebind();
|
|
||||||
if (header != null) header.rebind();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateImagesSpoilerState(Status status, String itemID){
|
protected void updateImagesSpoilerState(Status status, String itemID){
|
||||||
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
||||||
MediaGridStatusDisplayItem.Holder mediaGrid=findHolderOfType(itemID, MediaGridStatusDisplayItem.Holder.class);
|
for(ImageStatusDisplayItem.Holder photo:(List<ImageStatusDisplayItem.Holder>)findAllHoldersOfType(itemID, ImageStatusDisplayItem.Holder.class)){
|
||||||
if(mediaGrid!=null){
|
photo.setRevealed(status.spoilerRevealed);
|
||||||
mediaGrid.setRevealed(status.spoilerRevealed);
|
updatedPositions.add(photo.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||||
updatedPositions.add(mediaGrid.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
|
||||||
}
|
}
|
||||||
int i=0;
|
int i=0;
|
||||||
for(StatusDisplayItem item:displayItems){
|
for(StatusDisplayItem item:displayItems){
|
||||||
if(itemID.equals(item.parentID) && item instanceof MediaGridStatusDisplayItem && !updatedPositions.contains(i)){
|
if(itemID.equals(item.parentID) && item instanceof ImageStatusDisplayItem && !updatedPositions.contains(i)){
|
||||||
adapter.notifyItemChanged(i);
|
adapter.notifyItemChanged(i);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
@@ -567,15 +543,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
|
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
||||||
|
|
||||||
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
|
||||||
int startPos = warning.getAbsoluteAdapterPosition();
|
|
||||||
displayItems.remove(startPos);
|
|
||||||
displayItems.addAll(startPos, warning.filteredItems);
|
|
||||||
adapter.notifyItemRangeInserted(startPos, warning.filteredItems.size() - 1);
|
|
||||||
if (startPos == 0) scrollToTop();
|
|
||||||
warning.getItem().status.filterRevealed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAccountID(){
|
public String getAccountID(){
|
||||||
return accountID;
|
return accountID;
|
||||||
}
|
}
|
||||||
@@ -645,11 +612,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0) == null || list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getListWidthForMediaLayout(){
|
protected int getListWidthForMediaLayout(){
|
||||||
return list.getWidth();
|
return list.getWidth();
|
||||||
}
|
}
|
||||||
@@ -696,25 +658,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
currentPhotoViewer.onPause();
|
currentPhotoViewer.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean onFabLongClick(View v) {
|
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MediaAttachmentViewController makeNewMediaAttachmentView(MediaGridStatusDisplayItem.GridItemType type){
|
|
||||||
return new MediaAttachmentViewController(getActivity(), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> getAttachmentViewsPool(){
|
|
||||||
return attachmentViewsPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public DisplayItemsAdapter(){
|
public DisplayItemsAdapter(){
|
||||||
@@ -752,6 +695,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
return displayItems.get(position).getImageRequest(image);
|
return displayItems.get(position).getImageRequest(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public void onViewDetachedFromWindow(@NonNull BindableViewHolder<StatusDisplayItem> holder){
|
||||||
|
// if(holder instanceof ImageLoaderViewHolder){
|
||||||
|
// int count=holder.getItem().getImageCount();
|
||||||
|
// for(int i=0;i<count;i++){
|
||||||
|
// ((ImageLoaderViewHolder) holder).clearImage(i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StatusListItemDecoration extends RecyclerView.ItemDecoration{
|
private class StatusListItemDecoration extends RecyclerView.ItemDecoration{
|
||||||
@@ -785,21 +738,25 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
for(int i=0;i<parent.getChildCount();i++){
|
for(int i=0;i<parent.getChildCount();i++){
|
||||||
View child=parent.getChildAt(i);
|
View child=parent.getChildAt(i);
|
||||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||||
if(!imgHolder.getItem().status.spoilerRevealed && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
if(!imgHolder.getItem().status.spoilerRevealed && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||||
hiddenMediaPaint.setColor(0x80000000);
|
hiddenMediaPaint.setColor(0x80000000);
|
||||||
c.drawRect(child.getX(), child.getY(), child.getX()+child.getWidth(), child.getY()+child.getHeight(), hiddenMediaPaint);
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgHolder.getItem().thisTile;
|
||||||
|
float hGap=tile.startCol>0 ? V.dp(1) : 0;
|
||||||
|
float vGap=tile.startRow>0 ? V.dp(1) : 0;
|
||||||
|
c.drawRect(child.getX()-hGap, child.getY()-vGap, child.getX()+child.getWidth(), child.getY()+child.getHeight(), hiddenMediaPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int i=0;i<parent.getChildCount();i++){
|
for(int i=0;i<parent.getChildCount();i++){
|
||||||
View child=parent.getChildAt(i);
|
View child=parent.getChildAt(i);
|
||||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||||
if(!imgHolder.getItem().status.spoilerRevealed){
|
if(!imgHolder.getItem().status.spoilerRevealed){
|
||||||
if(TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgHolder.getItem().thisTile;
|
||||||
|
if(tile.startCol==0 && tile.startRow==0 && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||||
int listWidth=getListWidthForMediaLayout();
|
int listWidth=getListWidthForMediaLayout();
|
||||||
int width=Math.min(listWidth, UiUtils.MAX_WIDTH);
|
int width=Math.min(listWidth, V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||||
if(currentMediaHiddenLayoutsWidth!=width)
|
if(currentMediaHiddenLayoutsWidth!=width)
|
||||||
rebuildMediaHiddenLayouts(width-V.dp(32));
|
rebuildMediaHiddenLayouts(width-V.dp(32));
|
||||||
c.save();
|
c.save();
|
||||||
@@ -824,6 +781,47 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||||
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
|
||||||
|
if(holder instanceof ImageStatusDisplayItem.Holder){
|
||||||
|
int listWidth=getListWidthForMediaLayout();
|
||||||
|
int width=Math.min(listWidth, V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult layout=((ImageStatusDisplayItem.Holder<?>) holder).getItem().tiledLayout;
|
||||||
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=((ImageStatusDisplayItem.Holder<?>) holder).getItem().thisTile;
|
||||||
|
if(tile.startCol+tile.colSpan<layout.columnSizes.length){
|
||||||
|
outRect.right=V.dp(1);
|
||||||
|
}
|
||||||
|
if(tile.startRow+tile.rowSpan<layout.rowSizes.length){
|
||||||
|
outRect.bottom=V.dp(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a view that spans rows, compensate its additional height so the row it's in stays the right height
|
||||||
|
if(tile.rowSpan>1){
|
||||||
|
outRect.bottom=-(Math.round(tile.height/1000f*width)-Math.round(layout.rowSizes[tile.startRow]/1000f*width));
|
||||||
|
}
|
||||||
|
// ...and for its siblings, offset those on rows below first to the right where they belong
|
||||||
|
if(tile.startCol>0 && layout.tiles[0].rowSpan>1 && tile.startRow>layout.tiles[0].startRow){
|
||||||
|
int xOffset=Math.round(layout.tiles[0].width/1000f*listWidth);
|
||||||
|
outRect.left=xOffset;
|
||||||
|
outRect.right=-xOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the width of the media block is smaller than that of the RecyclerView, offset the views horizontally to center them
|
||||||
|
if(listWidth>width){
|
||||||
|
outRect.left+=(listWidth-V.dp(ImageAttachmentFrameLayout.MAX_WIDTH))/2;
|
||||||
|
if(tile.startCol>0){
|
||||||
|
int spanOffset=0;
|
||||||
|
for(int i=0;i<tile.startCol;i++){
|
||||||
|
spanOffset+=layout.columnSizes[i];
|
||||||
|
}
|
||||||
|
outRect.left-=Math.round(spanOffset/1000f*listWidth);
|
||||||
|
outRect.left+=Math.round(spanOffset/1000f*width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void rebuildMediaHiddenLayouts(int width){
|
private void rebuildMediaHiddenLayouts(int width){
|
||||||
currentMediaHiddenLayoutsWidth=width;
|
currentMediaHiddenLayoutsWidth=width;
|
||||||
String title=getString(R.string.sensitive_content);
|
String title=getString(R.string.sensitive_content);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import static android.os.ext.SdkExtensions.getExtensionVersion;
|
|||||||
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
||||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
|
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
|
||||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
||||||
import static org.joinmastodon.android.ui.utils.UiUtils.isPhotoPickerAvailable;
|
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
||||||
|
|
||||||
@@ -29,7 +28,6 @@ import android.graphics.drawable.LayerDrawable;
|
|||||||
import android.icu.text.BreakIterator;
|
import android.icu.text.BreakIterator;
|
||||||
import android.media.MediaMetadataRetriever;
|
import android.media.MediaMetadataRetriever;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.opengl.Visibility;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -45,7 +43,6 @@ import android.text.TextWatcher;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -70,7 +67,6 @@ import android.widget.ScrollView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
|
||||||
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
@@ -112,7 +108,7 @@ import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
|||||||
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.utils.TransferSpeedTracker;
|
import org.joinmastodon.android.ui.utils.TransferSpeedTracker;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ComposeEditText;
|
import org.joinmastodon.android.ui.views.ComposeEditText;
|
||||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||||
@@ -120,7 +116,6 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
|
|||||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||||
import org.joinmastodon.android.utils.MastodonLanguage;
|
import org.joinmastodon.android.utils.MastodonLanguage;
|
||||||
import org.joinmastodon.android.utils.StatusTextEncoder;
|
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -157,21 +152,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
||||||
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
||||||
private static final int MAX_ATTACHMENTS=4;
|
private static final int MAX_ATTACHMENTS=4;
|
||||||
private static final String GLITCH_LOCAL_ONLY_SUFFIX = "👁";
|
|
||||||
private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*");
|
|
||||||
private static final String TAG="ComposeFragment";
|
private static final String TAG="ComposeFragment";
|
||||||
|
|
||||||
public static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
||||||
public static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
||||||
public static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
||||||
|
|
||||||
@SuppressLint("NewApi") // this class actually exists on 6.0
|
@SuppressLint("NewApi") // this class actually exists on 6.0
|
||||||
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
||||||
|
|
||||||
private SizeListenerLinearLayout contentView;
|
private SizeListenerLinearLayout contentView;
|
||||||
private TextView selfName, selfUsername, selfExtraText, extraText;
|
private TextView selfName, selfUsername;
|
||||||
private ImageView selfAvatar;
|
private ImageView selfAvatar;
|
||||||
private Account self;
|
private Account self;
|
||||||
private String instanceDomain;
|
private String instanceDomain;
|
||||||
@@ -207,7 +200,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private List<EmojiCategory> customEmojis;
|
private List<EmojiCategory> customEmojis;
|
||||||
private CustomEmojiPopupKeyboard emojiKeyboard;
|
private CustomEmojiPopupKeyboard emojiKeyboard;
|
||||||
private Status replyTo;
|
private Status replyTo;
|
||||||
private Status quote;
|
|
||||||
private String initialText;
|
private String initialText;
|
||||||
private String uuid;
|
private String uuid;
|
||||||
private int pollDuration=24*3600;
|
private int pollDuration=24*3600;
|
||||||
@@ -221,7 +213,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private View sendingOverlay;
|
private View sendingOverlay;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
||||||
private boolean localOnly;
|
|
||||||
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
||||||
private FrameLayout mainEditTextWrap;
|
private FrameLayout mainEditTextWrap;
|
||||||
private ComposeAutocompleteViewController autocompleteViewController;
|
private ComposeAutocompleteViewController autocompleteViewController;
|
||||||
@@ -236,7 +227,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private boolean ignoreSelectionChanges=false;
|
private boolean ignoreSelectionChanges=false;
|
||||||
private Runnable updateUploadEtaRunnable;
|
private Runnable updateUploadEtaRunnable;
|
||||||
|
|
||||||
private String language, encoding;
|
private String language;
|
||||||
private MastodonLanguage.LanguageResolver languageResolver;
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
|
|
||||||
private int navigationBarColorBefore;
|
private int navigationBarColorBefore;
|
||||||
@@ -256,12 +247,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
||||||
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
||||||
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
||||||
if(getArguments().containsKey("editStatus"))
|
if(getArguments().containsKey("editStatus")){
|
||||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
||||||
if(getArguments().containsKey("replyTo"))
|
}
|
||||||
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
|
||||||
if(getArguments().containsKey("quote"))
|
|
||||||
quote=Parcels.unwrap(getArguments().getParcelable("quote"));
|
|
||||||
if(instance==null){
|
if(instance==null){
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
return;
|
return;
|
||||||
@@ -335,7 +323,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
selfName=view.findViewById(R.id.self_name);
|
selfName=view.findViewById(R.id.self_name);
|
||||||
selfUsername=view.findViewById(R.id.self_username);
|
selfUsername=view.findViewById(R.id.self_username);
|
||||||
selfAvatar=view.findViewById(R.id.self_avatar);
|
selfAvatar=view.findViewById(R.id.self_avatar);
|
||||||
selfExtraText=view.findViewById(R.id.self_extra_text);
|
|
||||||
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
||||||
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
||||||
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
||||||
@@ -359,30 +346,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
|
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
|
||||||
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
|
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
|
||||||
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
||||||
replyText=view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text : R.id.reply_text_below);
|
replyText=view.findViewById(R.id.reply_text);
|
||||||
view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text_below : R.id.reply_text)
|
|
||||||
.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (isPhotoPickerAvailable()) {
|
mediaBtn.setOnClickListener(v->openFilePicker());
|
||||||
PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn);
|
|
||||||
attachPopup.inflate(R.menu.attach);
|
|
||||||
attachPopup.setOnMenuItemClickListener(i -> {
|
|
||||||
openFilePicker(i.getItemId() == R.id.media);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), attachPopup);
|
|
||||||
mediaBtn.setOnClickListener(v->attachPopup.show());
|
|
||||||
mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener());
|
|
||||||
} else {
|
|
||||||
mediaBtn.setOnClickListener(v -> openFilePicker(false));
|
|
||||||
}
|
|
||||||
pollBtn.setOnClickListener(v->togglePoll());
|
pollBtn.setOnClickListener(v->togglePoll());
|
||||||
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
||||||
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
||||||
|
|
||||||
localOnly = savedInstanceState != null ? savedInstanceState.getBoolean("localOnly") :
|
|
||||||
editingStatus != null ? editingStatus.localOnly : replyTo != null && replyTo.localOnly;
|
|
||||||
|
|
||||||
buildVisibilityPopup(visibilityBtn);
|
buildVisibilityPopup(visibilityBtn);
|
||||||
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||||
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||||
@@ -457,7 +426,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
||||||
spoilerEdit.setBackground(spoilerBg);
|
spoilerEdit.setBackground(spoilerBg);
|
||||||
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
||||||
hasSpoiler=true;
|
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
||||||
@@ -501,9 +469,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
case UNLISTED -> R.id.vis_unlisted;
|
case UNLISTED -> R.id.vis_unlisted;
|
||||||
case PRIVATE -> R.id.vis_followers;
|
case PRIVATE -> R.id.vis_followers;
|
||||||
case DIRECT -> R.id.vis_private;
|
case DIRECT -> R.id.vis_private;
|
||||||
case LOCAL -> R.id.vis_local;
|
|
||||||
}).setChecked(true);
|
}).setChecked(true);
|
||||||
visibilityPopup.getMenu().findItem(R.id.local_only).setChecked(localOnly);
|
|
||||||
|
|
||||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||||
@@ -530,7 +496,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
||||||
}
|
}
|
||||||
outState.putBoolean("sensitive", sensitive);
|
outState.putBoolean("sensitive", sensitive);
|
||||||
outState.putBoolean("localOnly", localOnly);
|
|
||||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||||
outState.putString("language", language);
|
outState.putString("language", language);
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
@@ -633,8 +598,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||||
if(replyTo!=null || quote!=null){
|
if(replyTo!=null){
|
||||||
Status status = quote!=null ? quote : replyTo;
|
|
||||||
View replyWrap = view.findViewById(R.id.reply_wrap);
|
View replyWrap = view.findViewById(R.id.reply_wrap);
|
||||||
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||||
int scrollHeight = scrollView.getHeight();
|
int scrollHeight = scrollView.getHeight();
|
||||||
@@ -655,18 +619,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
View originalPost = view.findViewById(R.id.original_post);
|
View originalPost = view.findViewById(R.id.original_post);
|
||||||
extraText = view.findViewById(R.id.extra_text);
|
|
||||||
originalPost.setVisibility(View.VISIBLE);
|
originalPost.setVisibility(View.VISIBLE);
|
||||||
originalPost.setOnClickListener(v->{
|
originalPost.setOnClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("status", Parcels.wrap(status));
|
args.putParcelable("status", Parcels.wrap(replyTo));
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageView avatar = view.findViewById(R.id.avatar);
|
ImageView avatar = view.findViewById(R.id.avatar);
|
||||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(status.account.avatar));
|
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(replyTo.account.avatar));
|
||||||
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
||||||
@Override
|
@Override
|
||||||
public void getOutline(View view, Outline outline){
|
public void getOutline(View view, Outline outline){
|
||||||
@@ -678,47 +641,38 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
avatar.setOnClickListener(v->{
|
avatar.setOnClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(status.account));
|
args.putParcelable("profileAccount", Parcels.wrap(replyTo.account));
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||||
});
|
});
|
||||||
|
|
||||||
((TextView) view.findViewById(R.id.name)).setText(status.account.displayName);
|
((TextView) view.findViewById(R.id.name)).setText(replyTo.account.displayName);
|
||||||
((TextView) view.findViewById(R.id.username)).setText(status.account.getDisplayUsername());
|
((TextView) view.findViewById(R.id.username)).setText(replyTo.account.getDisplayUsername());
|
||||||
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||||
Drawable visibilityIcon = getActivity().getDrawable(switch(status.visibility){
|
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
||||||
case LOCAL -> R.drawable.ic_fluent_eye_20_regular;
|
|
||||||
});
|
});
|
||||||
ImageView moreBtn = view.findViewById(R.id.more);
|
ImageView moreBtn = view.findViewById(R.id.more);
|
||||||
moreBtn.setImageDrawable(visibilityIcon);
|
moreBtn.setImageDrawable(visibilityIcon);
|
||||||
moreBtn.setBackground(null);
|
moreBtn.setBackground(null);
|
||||||
TextView timestamp = view.findViewById(R.id.timestamp);
|
TextView timestamp = view.findViewById(R.id.timestamp);
|
||||||
if (status.editedAt!=null) timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt)));
|
if (replyTo.editedAt==null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), replyTo.createdAt));
|
||||||
else if (status.createdAt!=null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), status.createdAt));
|
else timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), replyTo.editedAt)));
|
||||||
else timestamp.setText("");
|
if (replyTo.spoilerText != null && !replyTo.spoilerText.isBlank()) {
|
||||||
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
|
|
||||||
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
|
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
|
||||||
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(status.spoilerText);
|
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(replyTo.spoilerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpannableStringBuilder content = HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, accountID);
|
SpannableStringBuilder content = HtmlParser.parse(replyTo.content, replyTo.emojis, replyTo.mentions, replyTo.tags, accountID);
|
||||||
LinkedTextView text = view.findViewById(R.id.text);
|
LinkedTextView text = view.findViewById(R.id.text);
|
||||||
if (content.length() > 0) text.setText(content);
|
if (content.length() > 0) text.setText(content);
|
||||||
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
||||||
|
|
||||||
replyText.setText(getString(quote!=null? R.string.sk_quoting_user : R.string.in_reply_to, status.account.displayName));
|
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||||
int visibilityNameRes = switch (status.visibility) {
|
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + UiUtils.getVisibilityText(replyTo));
|
||||||
case PUBLIC -> R.string.visibility_public;
|
|
||||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
|
||||||
case PRIVATE -> R.string.visibility_followers_only;
|
|
||||||
case DIRECT -> R.string.visibility_private;
|
|
||||||
case LOCAL -> R.string.sk_local_only;
|
|
||||||
};
|
|
||||||
replyText.setContentDescription(getString(R.string.in_reply_to, status.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
|
||||||
replyText.setOnClickListener(v->{
|
replyText.setOnClickListener(v->{
|
||||||
scrollView.smoothScrollTo(0, 0);
|
scrollView.smoothScrollTo(0, 0);
|
||||||
});
|
});
|
||||||
@@ -729,9 +683,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
|
|
||||||
ArrayList<String> mentions=new ArrayList<>();
|
ArrayList<String> mentions=new ArrayList<>();
|
||||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
||||||
if(!status.account.id.equals(ownID))
|
if(!replyTo.account.id.equals(ownID))
|
||||||
mentions.add('@'+status.account.acct);
|
mentions.add('@'+replyTo.account.acct);
|
||||||
for(Mention mention:status.mentions){
|
for(Mention mention:replyTo.mentions){
|
||||||
if(mention.id.equals(ownID))
|
if(mention.id.equals(ownID))
|
||||||
continue;
|
continue;
|
||||||
String m='@'+mention.acct;
|
String m='@'+mention.acct;
|
||||||
@@ -744,17 +698,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=true;
|
ignoreSelectionChanges=true;
|
||||||
mainEditText.setSelection(mainEditText.length());
|
mainEditText.setSelection(mainEditText.length());
|
||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
if(GlobalUserPreferences.prefixRepliesWithRe && !status.spoilerText.startsWith("re: ")){
|
spoilerEdit.setText(replyTo.spoilerText);
|
||||||
spoilerEdit.setText("re: " + status.spoilerText);
|
|
||||||
}else{
|
|
||||||
spoilerEdit.setText(status.spoilerText);
|
|
||||||
}
|
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}
|
}
|
||||||
if (status.language != null && !status.language.isEmpty()) updateLanguage(status.language);
|
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||||
}
|
}
|
||||||
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
||||||
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
||||||
@@ -805,7 +755,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSensitive();
|
updateSensitive();
|
||||||
updateHeaders();
|
|
||||||
|
|
||||||
if(editingStatus!=null){
|
if(editingStatus!=null){
|
||||||
updateCharCounter();
|
updateCharCounter();
|
||||||
@@ -860,11 +809,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
||||||
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
||||||
buildLanguageSelector(languageButton);
|
buildLanguageSelector(languageButton);
|
||||||
|
|
||||||
if (editingStatus != null && scheduledStatus == null) {
|
|
||||||
// editing an already published post
|
|
||||||
draftsBtn.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToUnsentPosts() {
|
private void navigateToUnsentPosts() {
|
||||||
@@ -888,13 +832,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateLanguage(MastodonLanguage loc) {
|
private void updateLanguage(MastodonLanguage loc) {
|
||||||
updateLanguage(loc.getLanguage(), loc.getLanguageName(), loc.getDefaultName());
|
language = loc.getLanguage();
|
||||||
}
|
languageButton.setText(loc.getLanguageName());
|
||||||
|
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
|
||||||
private void updateLanguage(String languageTag, String languageName, String defaultName) {
|
|
||||||
language = languageTag;
|
|
||||||
languageButton.setText(languageName);
|
|
||||||
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, defaultName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@@ -904,19 +844,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
btn.setOnClickListener(v->languagePopup.show());
|
btn.setOnClickListener(v->languagePopup.show());
|
||||||
|
|
||||||
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
if (language != null) updateLanguage(language);
|
updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
||||||
else updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
|
||||||
? languageResolver.from(prefs.postingDefaultLanguage)
|
? languageResolver.from(prefs.postingDefaultLanguage)
|
||||||
: languageResolver.getDefault());
|
: languageResolver.getDefault());
|
||||||
|
|
||||||
Menu languageMenu = languagePopup.getMenu();
|
Menu languageMenu = languagePopup.getMenu();
|
||||||
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
||||||
if (recentLanguage.equals("bottom")) {
|
MastodonLanguage l = languageResolver.from(recentLanguage);
|
||||||
addBottomLanguage(languageMenu);
|
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
} else {
|
|
||||||
MastodonLanguage l = languageResolver.from(recentLanguage);
|
|
||||||
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
||||||
@@ -925,33 +860,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
|
||||||
|
|
||||||
btn.setOnLongClickListener(v->{
|
|
||||||
btn.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
||||||
if (!GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
languagePopup.setOnMenuItemClickListener(i->{
|
languagePopup.setOnMenuItemClickListener(i->{
|
||||||
if (i.hasSubMenu()) return false;
|
if (i.hasSubMenu()) return false;
|
||||||
if (i.getItemId() == allLanguages.size()) {
|
updateLanguage(allLanguages.get(i.getItemId()));
|
||||||
updateLanguage(language, "\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48", "bottom");
|
|
||||||
encoding = "bottom";
|
|
||||||
} else {
|
|
||||||
updateLanguage(allLanguages.get(i.getItemId()));
|
|
||||||
encoding = null;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBottomLanguage(Menu menu) {
|
|
||||||
if (menu.findItem(allLanguages.size()) == null) {
|
|
||||||
menu.add(0, allLanguages.size(), Menu.NONE, "bottom (\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
return true;
|
return true;
|
||||||
@@ -981,9 +896,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(hasSpoiler){
|
if(hasSpoiler){
|
||||||
charCount+=spoilerEdit.length();
|
charCount+=spoilerEdit.length();
|
||||||
}
|
}
|
||||||
if (localOnly && GlobalUserPreferences.accountsInGlitchMode.contains(accountID)) {
|
|
||||||
charCount -= GLITCH_LOCAL_ONLY_SUFFIX.length();
|
|
||||||
}
|
|
||||||
charCounter.setText(String.valueOf(charLimit-charCount));
|
charCounter.setText(String.valueOf(charLimit-charCount));
|
||||||
trimmedCharCount=text.toString().trim().length();
|
trimmedCharCount=text.toString().trim().length();
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
@@ -1016,15 +928,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
nonDoneAttachmentCount++;
|
nonDoneAttachmentCount++;
|
||||||
}
|
}
|
||||||
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
|
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
|
||||||
// sendError.setVisibility(View.GONE);
|
sendError.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCustomEmojiClick(Emoji emoji){
|
private void onCustomEmojiClick(Emoji emoji){
|
||||||
if(getActivity().getCurrentFocus() instanceof EditText edit){
|
int start=mainEditText.getSelectionStart();
|
||||||
int start=edit.getSelectionStart();
|
String prefix=start>0 && !Character.isWhitespace(mainEditText.getText().charAt(start-1)) ? " :" : ":";
|
||||||
String prefix=start>0 && !Character.isWhitespace(edit.getText().charAt(start-1)) ? " :" : ":";
|
mainEditText.getText().replace(start, mainEditText.getSelectionEnd(), prefix+emoji.shortcode+':');
|
||||||
edit.getText().replace(start, edit.getSelectionEnd(), prefix+emoji.shortcode+':');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1086,53 +996,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void publish(){
|
private void publish(){
|
||||||
publish(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publish(boolean force){
|
|
||||||
String text=mainEditText.getText().toString();
|
String text=mainEditText.getText().toString();
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
if ("bottom".equals(encoding)) {
|
|
||||||
text = new StatusTextEncoder(Bottom::encode).encode(text);
|
|
||||||
req.spoilerText = "bottom-encoded emoji spam";
|
|
||||||
}
|
|
||||||
if (localOnly &&
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.contains(accountID) &&
|
|
||||||
!GLITCH_LOCAL_ONLY_PATTERN.matcher(text).matches()) {
|
|
||||||
text += " " + GLITCH_LOCAL_ONLY_SUFFIX;
|
|
||||||
}
|
|
||||||
req.status=text;
|
req.status=text;
|
||||||
req.localOnly=localOnly;
|
req.visibility=statusVisibility;
|
||||||
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
req.language=language;
|
req.language=language;
|
||||||
req.scheduledAt = scheduledAt;
|
req.scheduledAt = scheduledAt;
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||||
Optional<DraftMediaAttachment> withoutAltText = attachments.stream().filter(a -> a.description == null || a.description.isBlank()).findFirst();
|
|
||||||
boolean isDraft = scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT);
|
|
||||||
if (!force && !GlobalUserPreferences.disableAltTextReminder && !isDraft && withoutAltText.isPresent()) {
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_alt_text_missing_title)
|
|
||||||
.setMessage(R.string.sk_alt_text_missing)
|
|
||||||
.setPositiveButton(R.string.add_alt_text, (d, w) -> editMediaDescription(withoutAltText.get()))
|
|
||||||
.setNegativeButton(R.string.sk_publish_anyway, (d, w) -> publish(true))
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ask whether to publish now when editing an existing draft
|
|
||||||
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_save_draft)
|
|
||||||
.setMessage(R.string.sk_save_draft_message)
|
|
||||||
.setPositiveButton(R.string.save, (d, w) -> publish(true))
|
|
||||||
.setNegativeButton(R.string.publish, (d, w) -> {
|
|
||||||
updateScheduledAt(null);
|
|
||||||
publish();
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
||||||
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
||||||
@@ -1147,9 +1019,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(hasSpoiler && spoilerEdit.length()>0){
|
if(hasSpoiler && spoilerEdit.length()>0){
|
||||||
req.spoilerText=spoilerEdit.getText().toString();
|
req.spoilerText=spoilerEdit.getText().toString();
|
||||||
}
|
}
|
||||||
if(quote != null){
|
|
||||||
req.quoteId=quote.id;
|
|
||||||
}
|
|
||||||
if(uuid==null)
|
if(uuid==null)
|
||||||
uuid=UUID.randomUUID().toString();
|
uuid=UUID.randomUUID().toString();
|
||||||
|
|
||||||
@@ -1182,9 +1051,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}else{
|
}else{
|
||||||
E.post(new StatusUpdatedEvent(result));
|
E.post(new StatusUpdatedEvent(result));
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) {
|
Nav.finish(ComposeFragment.this);
|
||||||
Nav.finish(ComposeFragment.this);
|
|
||||||
}
|
|
||||||
if (getArguments().getBoolean("navigateToStatus", false)) {
|
if (getArguments().getBoolean("navigateToStatus", false)) {
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -1243,14 +1110,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
||||||
newRecentLanguages.remove(language);
|
newRecentLanguages.remove(language);
|
||||||
newRecentLanguages.add(0, language);
|
newRecentLanguages.add(0, language);
|
||||||
if (encoding != null) {
|
|
||||||
newRecentLanguages.remove(encoding);
|
|
||||||
newRecentLanguages.add(0, encoding);
|
|
||||||
}
|
|
||||||
if ("bottom".equals(encoding) && !GlobalUserPreferences.bottomEncoding) {
|
|
||||||
GlobalUserPreferences.bottomEncoding = true;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}
|
}
|
||||||
@@ -1316,14 +1175,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void confirmDiscardDraftAndFinish(){
|
private void confirmDiscardDraftAndFinish(){
|
||||||
boolean attachmentsPending = attachments.stream().anyMatch(att -> att.state != AttachmentUploadState.DONE);
|
new M3AlertDialogBuilder(getActivity())
|
||||||
if (attachmentsPending) new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_unfinished_attachments)
|
|
||||||
.setMessage(R.string.sk_unfinished_attachments_message)
|
|
||||||
.setPositiveButton(R.string.edit, (d, w) -> {})
|
|
||||||
.setNegativeButton(R.string.discard, (d, w) -> Nav.finish(this))
|
|
||||||
.show();
|
|
||||||
else new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
||||||
.setPositiveButton(R.string.save, (d, w) -> {
|
.setPositiveButton(R.string.save, (d, w) -> {
|
||||||
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
||||||
@@ -1333,6 +1185,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if Android platform photopicker is available on the device\
|
||||||
|
* @return whether the device supports photopicker intents.
|
||||||
|
*/
|
||||||
|
private boolean isPhotoPickerAvailable() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return true;
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
return getExtensionVersion(Build.VERSION_CODES.R) >= 2;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the correct intent for the device version to select media.
|
* Builds the correct intent for the device version to select media.
|
||||||
@@ -1342,26 +1206,26 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
*
|
*
|
||||||
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
||||||
*/
|
*/
|
||||||
private void openFilePicker(boolean photoPicker){
|
private void openFilePicker(){
|
||||||
Intent intent;
|
Intent intent;
|
||||||
boolean usePhotoPicker=photoPicker && isPhotoPickerAvailable();
|
boolean usePhotoPicker = isPhotoPickerAvailable();
|
||||||
if(usePhotoPicker){
|
if (usePhotoPicker) {
|
||||||
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MAX_ATTACHMENTS-getMediaAttachmentsCount());
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
|
||||||
}else{
|
} else {
|
||||||
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("*/*");
|
intent.setType("*/*");
|
||||||
}
|
}
|
||||||
if(!usePhotoPicker && instance.configuration!=null &&
|
if (!usePhotoPicker && instance.configuration != null &&
|
||||||
instance.configuration.mediaAttachments!=null &&
|
instance.configuration.mediaAttachments != null &&
|
||||||
instance.configuration.mediaAttachments.supportedMimeTypes!=null &&
|
instance.configuration.mediaAttachments.supportedMimeTypes != null &&
|
||||||
!instance.configuration.mediaAttachments.supportedMimeTypes.isEmpty()){
|
!instance.configuration.mediaAttachments.supportedMimeTypes.isEmpty()) {
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES,
|
intent.putExtra(Intent.EXTRA_MIME_TYPES,
|
||||||
instance.configuration.mediaAttachments.supportedMimeTypes.toArray(
|
instance.configuration.mediaAttachments.supportedMimeTypes.toArray(
|
||||||
new String[0]));
|
new String[0]));
|
||||||
}else{
|
} else {
|
||||||
if(!usePhotoPicker){
|
if (!usePhotoPicker) {
|
||||||
// If photo picker is being used these are the default mimetypes.
|
// If photo picker is being used these are the default mimetypes.
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
|
||||||
}
|
}
|
||||||
@@ -1385,7 +1249,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StringFormatInvalid")
|
|
||||||
private boolean addMediaAttachment(Uri uri, String description){
|
private boolean addMediaAttachment(Uri uri, String description){
|
||||||
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS){
|
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS){
|
||||||
showMediaAttachmentError(getResources().getQuantityString(R.plurals.cant_add_more_than_x_attachments, MAX_ATTACHMENTS, MAX_ATTACHMENTS));
|
showMediaAttachmentError(getResources().getQuantityString(R.plurals.cant_add_more_than_x_attachments, MAX_ATTACHMENTS, MAX_ATTACHMENTS));
|
||||||
@@ -1783,20 +1646,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
||||||
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
||||||
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
||||||
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_hours, 12, 12));
|
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
||||||
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
||||||
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
||||||
menu.getMenu().add(0, 8, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
|
||||||
menu.setOnMenuItemClickListener(item->{
|
menu.setOnMenuItemClickListener(item->{
|
||||||
pollDuration=switch(item.getItemId()){
|
pollDuration=switch(item.getItemId()){
|
||||||
case 1 -> 5*60;
|
case 1 -> 5*60;
|
||||||
case 2 -> 30*60;
|
case 2 -> 30*60;
|
||||||
case 3 -> 3600;
|
case 3 -> 3600;
|
||||||
case 4 -> 6*3600;
|
case 4 -> 6*3600;
|
||||||
case 5 -> 12*3600;
|
case 5 -> 24*3600;
|
||||||
case 6 -> 24*3600;
|
case 6 -> 3*24*3600;
|
||||||
case 7 -> 3*24*3600;
|
case 7 -> 7*24*3600;
|
||||||
case 8 -> 7*24*3600;
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
||||||
};
|
};
|
||||||
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
||||||
@@ -1905,33 +1766,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
return attachments.size();
|
return attachments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHeaders() {
|
|
||||||
UiUtils.setExtraTextInfo(getContext(), selfExtraText, statusVisibility, localOnly);
|
|
||||||
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, replyTo.visibility, replyTo.localOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildVisibilityPopup(View v){
|
private void buildVisibilityPopup(View v){
|
||||||
visibilityPopup=new PopupMenu(getActivity(), v);
|
visibilityPopup=new PopupMenu(getActivity(), v);
|
||||||
visibilityPopup.inflate(R.menu.compose_visibility);
|
visibilityPopup.inflate(R.menu.compose_visibility);
|
||||||
Menu m=visibilityPopup.getMenu();
|
Menu m=visibilityPopup.getMenu();
|
||||||
MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only);
|
|
||||||
boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
|
||||||
if (instance.pleroma != null) {
|
|
||||||
m.findItem(R.id.vis_local).setVisible(true);
|
|
||||||
} else if (localOnly || prefsSaysSupported) {
|
|
||||||
localOnlyItem.setVisible(true);
|
|
||||||
localOnlyItem.setChecked(localOnly);
|
|
||||||
Status status = editingStatus != null ? editingStatus : replyTo;
|
|
||||||
if (!prefsSaysSupported) {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
|
||||||
if (GLITCH_LOCAL_ONLY_PATTERN.matcher(status.getStrippedText()).matches()) {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
}
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) m.setGroupDividerEnabled(true);
|
m.setGroupCheckable(0, true, true);
|
||||||
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item){
|
public boolean onMenuItemClick(MenuItem item){
|
||||||
@@ -1944,25 +1784,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility=StatusPrivacy.PRIVATE;
|
statusVisibility=StatusPrivacy.PRIVATE;
|
||||||
}else if(id==R.id.vis_private){
|
}else if(id==R.id.vis_private){
|
||||||
statusVisibility=StatusPrivacy.DIRECT;
|
statusVisibility=StatusPrivacy.DIRECT;
|
||||||
}else if(id==R.id.vis_local){
|
|
||||||
statusVisibility=StatusPrivacy.LOCAL;
|
|
||||||
}
|
|
||||||
if (id == R.id.local_only) {
|
|
||||||
localOnly = !item.isChecked();
|
|
||||||
item.setChecked(localOnly);
|
|
||||||
} else {
|
|
||||||
item.setChecked(true);
|
|
||||||
}
|
}
|
||||||
|
item.setChecked(true);
|
||||||
updateVisibilityIcon();
|
updateVisibilityIcon();
|
||||||
updateHeaders();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||||
if(replyTo != null) {
|
if(getArguments().containsKey("replyTo")){
|
||||||
statusVisibility = (replyTo.visibility == StatusPrivacy.PUBLIC && GlobalUserPreferences.defaultToUnlistedReplies ? StatusPrivacy.UNLISTED : replyTo.visibility);
|
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
||||||
|
statusVisibility = replyTo.visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over the reply visibility
|
// A saved privacy setting from a previous compose session wins over the reply visibility
|
||||||
@@ -1970,20 +1803,22 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
Preferences prefs = asm.getAccount(accountID).preferences;
|
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
// Only override the reply visibility if our preference is more private
|
// Only override the reply visibility if our preference is more private
|
||||||
// (and we're not replying to ourselves, or not at all)
|
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
||||||
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility) &&
|
statusVisibility = switch (prefs.postingDefaultVisibility) {
|
||||||
(replyTo == null || !asm.isSelf(accountID, replyTo.account))) {
|
case PUBLIC -> StatusPrivacy.PUBLIC;
|
||||||
statusVisibility = prefs.postingDefaultVisibility;
|
case UNLISTED -> StatusPrivacy.UNLISTED;
|
||||||
|
case PRIVATE -> StatusPrivacy.PRIVATE;
|
||||||
|
case DIRECT -> StatusPrivacy.DIRECT;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over all
|
// A saved privacy setting from a previous compose session wins over all
|
||||||
if(savedInstanceState !=null){
|
if(savedInstanceState !=null){
|
||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1993,10 +1828,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
visibilityBtn.setImageResource(switch(statusVisibility){
|
visibilityBtn.setImageResource(switch(statusVisibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_lock_open_24_regular;
|
case UNLISTED -> R.drawable.ic_fluent_people_community_24_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_lock_closed_24_filled;
|
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_24_regular;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
||||||
case LOCAL -> R.drawable.ic_fluent_eye_24_regular;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2118,14 +1952,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private void editMediaDescription(DraftMediaAttachment att) {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putString("attachment", att.serverAttachment.id);
|
|
||||||
args.putParcelable("uri", att.uri);
|
|
||||||
args.putString("existingDescription", att.description);
|
|
||||||
Nav.goForResult(getActivity(), ComposeImageDescriptionFragment.class, args, IMAGE_DESCRIPTION_RESULT, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getTitle(){
|
public CharSequence getTitle(){
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.MainActivity;
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
|
||||||
|
|
||||||
public class CustomLocalTimelineFragment extends StatusListFragment {
|
|
||||||
// private String name;
|
|
||||||
private String domain;
|
|
||||||
|
|
||||||
private String maxID;
|
|
||||||
@Override
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity){
|
|
||||||
super.onAttach(activity);
|
|
||||||
domain=getArguments().getString("domain");
|
|
||||||
updateTitle(domain);
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTitle(String domain) {
|
|
||||||
this.domain = domain;
|
|
||||||
setTitle(this.domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){
|
|
||||||
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
|
||||||
.setCallback(new SimpleCallback<>(this){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<Status> result){
|
|
||||||
if(!result.isEmpty())
|
|
||||||
maxID=result.get(result.size()-1).id;
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
|
||||||
result.stream().forEach(status -> {
|
|
||||||
status.account.acct += "@"+domain;
|
|
||||||
status.mentions.forEach(mention -> mention.id = null);
|
|
||||||
status.reloadWhenClicked = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.execNoAuth(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown(){
|
|
||||||
super.onShown();
|
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
|
|
||||||
public interface DomainDisplay {
|
|
||||||
|
|
||||||
default String getDomain(){
|
|
||||||
AccountSession session = AccountSessionManager.getInstance().getLastActiveAccount();
|
|
||||||
if (session != null)
|
|
||||||
return session.domain;
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import static android.view.Menu.NONE;
|
|
||||||
|
|
||||||
import static org.joinmastodon.android.ui.utils.UiUtils.makeBackItem;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.SubMenu;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.PopupMenu;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
|
||||||
import org.joinmastodon.android.model.CustomLocalTimeline;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
|
||||||
import me.grishka.appkit.utils.V;
|
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
|
||||||
|
|
||||||
public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
|
||||||
private String accountID;
|
|
||||||
private TimelinesAdapter adapter;
|
|
||||||
private final ItemTouchHelper itemTouchHelper;
|
|
||||||
private Menu optionsMenu;
|
|
||||||
private boolean updated;
|
|
||||||
private final Map<MenuItem, TimelineDefinition> timelineByMenuItem = new HashMap<>();
|
|
||||||
private final List<ListTimeline> listTimelines = new ArrayList<>();
|
|
||||||
private final List<Hashtag> hashtags = new ArrayList<>();
|
|
||||||
private final List<CustomLocalTimeline> localTimelines = new ArrayList<>();
|
|
||||||
|
|
||||||
public EditTimelinesFragment() {
|
|
||||||
super(10);
|
|
||||||
ItemTouchHelper.SimpleCallback itemTouchCallback = new ItemTouchHelperCallback() ;
|
|
||||||
itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
setTitle(R.string.sk_timelines);
|
|
||||||
accountID = getArguments().getString("account");
|
|
||||||
|
|
||||||
new GetLists().setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<ListTimeline> result) {
|
|
||||||
listTimelines.addAll(result);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
|
|
||||||
new GetFollowedHashtags().setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result) {
|
|
||||||
hashtags.addAll(result);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown(){
|
|
||||||
super.onShown();
|
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading) loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
itemTouchHelper.attachToRecyclerView(list);
|
|
||||||
refreshLayout.setEnabled(false);
|
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
this.optionsMenu = menu;
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.menu_back) {
|
|
||||||
updateOptionsMenu();
|
|
||||||
optionsMenu.performIdentifierAction(R.id.menu_add_timeline, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (item.getItemId() == R.id.menu_add_local_timelines) {
|
|
||||||
addNewLocalTimeline();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
TimelineDefinition tl = timelineByMenuItem.get(item);
|
|
||||||
if (tl != null) {
|
|
||||||
data.add(tl.copy());
|
|
||||||
adapter.notifyItemInserted(data.size());
|
|
||||||
saveTimelines();
|
|
||||||
updateOptionsMenu();
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNewLocalTimeline() {
|
|
||||||
FrameLayout inputWrap = new FrameLayout(getContext());
|
|
||||||
EditText input = new EditText(getContext());
|
|
||||||
input.setHint(R.string.sk_example_domain);
|
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
|
|
||||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.setMargins(V.dp(16), V.dp(4), V.dp(16), V.dp(16));
|
|
||||||
input.setLayoutParams(params);
|
|
||||||
inputWrap.addView(input);
|
|
||||||
new M3AlertDialogBuilder(getContext()).setTitle(R.string.mo_add_custom_server_local_timeline).setView(inputWrap)
|
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
|
||||||
TimelineDefinition tl = TimelineDefinition.ofCustomLocalTimeline(input.getText().toString().trim());
|
|
||||||
data.add(tl);
|
|
||||||
saveTimelines();
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTimelineToOptions(TimelineDefinition tl, Menu menu) {
|
|
||||||
if (data.contains(tl)) return;
|
|
||||||
MenuItem item = menu.add(0, View.generateViewId(), Menu.NONE, tl.getTitle(getContext()));
|
|
||||||
item.setIcon(tl.getIcon().iconRes);
|
|
||||||
timelineByMenuItem.put(item, tl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateOptionsMenu() {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
optionsMenu.clear();
|
|
||||||
timelineByMenuItem.clear();
|
|
||||||
|
|
||||||
SubMenu menu = optionsMenu.addSubMenu(0, R.id.menu_add_timeline, NONE, R.string.sk_timelines_add);
|
|
||||||
menu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
||||||
menu.getItem().setIcon(R.drawable.ic_fluent_add_24_regular);
|
|
||||||
|
|
||||||
SubMenu timelinesMenu = menu.addSubMenu(R.string.sk_timeline);
|
|
||||||
timelinesMenu.getItem().setIcon(R.drawable.ic_fluent_timeline_24_regular);
|
|
||||||
SubMenu listsMenu = menu.addSubMenu(R.string.sk_list);
|
|
||||||
listsMenu.getItem().setIcon(R.drawable.ic_fluent_people_24_regular);
|
|
||||||
SubMenu hashtagsMenu = menu.addSubMenu(R.string.sk_hashtag);
|
|
||||||
hashtagsMenu.getItem().setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
|
||||||
|
|
||||||
MenuItem addLocalTimelines = menu.add(0, R.id.menu_add_local_timelines, NONE, R.string.local_timeline);
|
|
||||||
addLocalTimelines.setIcon(R.drawable.ic_fluent_add_24_regular);
|
|
||||||
|
|
||||||
makeBackItem(timelinesMenu);
|
|
||||||
makeBackItem(listsMenu);
|
|
||||||
makeBackItem(hashtagsMenu);
|
|
||||||
|
|
||||||
TimelineDefinition.ALL_TIMELINES.forEach(tl -> addTimelineToOptions(tl, timelinesMenu));
|
|
||||||
listTimelines.stream().map(TimelineDefinition::ofList).forEach(tl -> addTimelineToOptions(tl, listsMenu));
|
|
||||||
hashtags.stream().map(TimelineDefinition::ofHashtag).forEach(tl -> addTimelineToOptions(tl, hashtagsMenu));
|
|
||||||
|
|
||||||
timelinesMenu.getItem().setVisible(timelinesMenu.size() > 0);
|
|
||||||
listsMenu.getItem().setVisible(listsMenu.size() > 0);
|
|
||||||
hashtagsMenu.getItem().setVisible(hashtagsMenu.size() > 0);
|
|
||||||
|
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.menu_add_timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveTimelines() {
|
|
||||||
updated = true;
|
|
||||||
GlobalUserPreferences.pinnedTimelines.put(accountID, data.size() > 0 ? data : List.of(TimelineDefinition.HOME_TIMELINE));
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeTimeline(int position) {
|
|
||||||
data.remove(position);
|
|
||||||
adapter.notifyItemRemoved(position);
|
|
||||||
saveTimelines();
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){
|
|
||||||
onDataLoaded(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES), false);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RecyclerView.Adapter<TimelineViewHolder> getAdapter() {
|
|
||||||
return adapter = new TimelinesAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void scrollToTop() {
|
|
||||||
smoothScrollRecyclerViewToTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
if (updated) UiUtils.restartApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelinesAdapter extends RecyclerView.Adapter<TimelineViewHolder>{
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public TimelineViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return new TimelineViewHolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull TimelineViewHolder holder, int position) {
|
|
||||||
holder.bind(data.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelineViewHolder extends BindableViewHolder<TimelineDefinition> implements UsableRecyclerView.Clickable{
|
|
||||||
private final TextView title;
|
|
||||||
private final ImageView dragger;
|
|
||||||
|
|
||||||
public TimelineViewHolder(){
|
|
||||||
super(getActivity(), R.layout.item_text, list);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
dragger=findViewById(R.id.dragger_thingy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
@Override
|
|
||||||
public void onBind(TimelineDefinition item) {
|
|
||||||
title.setText(item.getTitle(getContext()));
|
|
||||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(item.getIcon().iconRes), null, null, null);
|
|
||||||
dragger.setVisibility(View.VISIBLE);
|
|
||||||
dragger.setOnTouchListener((View v, MotionEvent event) -> {
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
||||||
itemTouchHelper.startDrag(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
Context ctx = getContext();
|
|
||||||
LinearLayout view = (LinearLayout) getActivity().getLayoutInflater()
|
|
||||||
.inflate(R.layout.edit_timeline, (ViewGroup) itemView, false);
|
|
||||||
|
|
||||||
TextInputFrameLayout inputLayout = view.findViewById(R.id.input);
|
|
||||||
EditText editText = inputLayout.getEditText();
|
|
||||||
editText.setText(item.getCustomTitle());
|
|
||||||
editText.setHint(item.getDefaultTitle(ctx));
|
|
||||||
|
|
||||||
ImageButton btn = view.findViewById(R.id.button);
|
|
||||||
PopupMenu popup = new PopupMenu(ctx, btn);
|
|
||||||
TimelineDefinition.Icon currentIcon = item.getIcon();
|
|
||||||
btn.setImageResource(currentIcon.iconRes);
|
|
||||||
btn.setContentDescription(ctx.getString(currentIcon.nameRes));
|
|
||||||
btn.setOnTouchListener(popup.getDragToOpenListener());
|
|
||||||
btn.setOnClickListener(l -> popup.show());
|
|
||||||
|
|
||||||
Menu menu = popup.getMenu();
|
|
||||||
TimelineDefinition.Icon defaultIcon = item.getDefaultIcon();
|
|
||||||
menu.add(0, currentIcon.ordinal(), NONE, currentIcon.nameRes).setIcon(currentIcon.iconRes);
|
|
||||||
if (!currentIcon.equals(defaultIcon)) {
|
|
||||||
menu.add(0, defaultIcon.ordinal(), NONE, defaultIcon.nameRes).setIcon(defaultIcon.iconRes);
|
|
||||||
}
|
|
||||||
for (TimelineDefinition.Icon icon : TimelineDefinition.Icon.values()) {
|
|
||||||
if (icon.hidden || icon.equals(item.getIcon())) continue;
|
|
||||||
menu.add(0, icon.ordinal(), NONE, icon.nameRes).setIcon(icon.iconRes);
|
|
||||||
}
|
|
||||||
UiUtils.enablePopupMenuIcons(ctx, popup);
|
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener(menuItem -> {
|
|
||||||
TimelineDefinition.Icon icon = TimelineDefinition.Icon.values()[menuItem.getItemId()];
|
|
||||||
btn.setImageResource(icon.iconRes);
|
|
||||||
btn.setContentDescription(ctx.getString(icon.nameRes));
|
|
||||||
item.setIcon(icon);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
new M3AlertDialogBuilder(ctx)
|
|
||||||
.setTitle(R.string.sk_edit_timeline)
|
|
||||||
.setView(view)
|
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
|
||||||
item.setTitle(editText.getText().toString().trim());
|
|
||||||
rebind();
|
|
||||||
saveTimelines();
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.sk_remove, (d, which) ->
|
|
||||||
removeTimeline(getAbsoluteAdapterPosition()))
|
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
|
||||||
.show();
|
|
||||||
|
|
||||||
btn.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
|
|
||||||
public ItemTouchHelperCallback() {
|
|
||||||
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
|
||||||
int fromPosition = viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
int toPosition = target.getAbsoluteAdapterPosition();
|
|
||||||
if (Math.max(fromPosition, toPosition) >= data.size() || Math.min(fromPosition, toPosition) < 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
Collections.swap(data, fromPosition, toPosition);
|
|
||||||
adapter.notifyItemMoved(fromPosition, toPosition);
|
|
||||||
saveTimelines();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
|
|
||||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) {
|
|
||||||
viewHolder.itemView.animate().alpha(0.65f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
|
||||||
super.clearView(recyclerView, viewHolder);
|
|
||||||
viewHolder.itemView.animate().alpha(1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
|
||||||
int position = viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
removeTimeline(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
|
||||||
|
public abstract class FabStatusListFragment extends StatusListFragment {
|
||||||
|
protected ImageButton fab;
|
||||||
|
|
||||||
|
public FabStatusListFragment() {
|
||||||
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab = view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(this::onFabLongClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onFabClick(View v){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean onFabLongClick(View v) {
|
||||||
|
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -46,7 +47,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
|
public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||||
private GetAccountRelationships relationshipsRequest;
|
private GetAccountRelationships relationshipsRequest;
|
||||||
@@ -79,7 +80,6 @@ public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsL
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -148,11 +148,6 @@ public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsL
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public AccountsAdapter(){
|
public AccountsAdapter(){
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ import org.joinmastodon.android.ui.DividerItemDecoration;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implements ScrollableToTop {
|
public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
|
|
||||||
@@ -54,7 +55,6 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -75,11 +75,6 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -11,22 +10,15 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -34,29 +26,23 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
public class HashtagTimelineFragment extends StatusListFragment{
|
||||||
private String hashtag;
|
private String hashtag;
|
||||||
private boolean following;
|
private boolean following;
|
||||||
|
private ImageButton fab;
|
||||||
private MenuItem followButton;
|
private MenuItem followButton;
|
||||||
|
|
||||||
@Override
|
public HashtagTimelineFragment(){
|
||||||
protected boolean withComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/tags/" + hashtag;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
updateTitle(getArguments().getString("hashtag"));
|
updateTitle(getArguments().getString("hashtag"));
|
||||||
following=getArguments().getBoolean("following", false);
|
following=getArguments().getBoolean("following", false);
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
DomainManager.getInstance().setCurrentDomain(getDomain());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTitle(String hashtagName) {
|
private void updateTitle(String hashtagName) {
|
||||||
@@ -68,42 +54,19 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
this.following = newFollowing;
|
this.following = newFollowing;
|
||||||
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||||
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||||
E.post(new HashtagUpdatedEvent(hashtag, following));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
followButton = menu.findItem(R.id.follow_hashtag);
|
followButton = menu.findItem(R.id.follow_hashtag);
|
||||||
updateFollowingState(following);
|
updateFollowingState(following);
|
||||||
|
|
||||||
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
followButton.setOnMenuItemClickListener(i -> {
|
||||||
@Override
|
|
||||||
public void onSuccess(Hashtag hashtag) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
updateTitle(hashtag.name);
|
|
||||||
updateFollowingState(hashtag.following);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (super.onOptionsItemSelected(item)) return true;
|
|
||||||
if (item.getItemId() == R.id.follow_hashtag) {
|
|
||||||
updateFollowingState(!following);
|
updateFollowingState(!following);
|
||||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Hashtag i) {
|
public void onSuccess(Hashtag i) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||||
updateFollowingState(i.following);
|
updateFollowingState(i.following);
|
||||||
}
|
}
|
||||||
@@ -115,13 +78,20 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
return true;
|
return true;
|
||||||
}
|
});
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||||
protected TimelineDefinition makeTimelineDefinition() {
|
@Override
|
||||||
return TimelineDefinition.ofHashtag(hashtag);
|
public void onSuccess(Hashtag hashtag) {
|
||||||
|
updateTitle(hashtag.name);
|
||||||
|
updateFollowingState(hashtag.following);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,8 +100,6 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -146,12 +114,14 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onFabLongClick(View v) {
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onFabClick(View v){
|
||||||
protected void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("prefilledText", '#'+hashtag+' ');
|
args.putString("prefilledText", '#'+hashtag+' ');
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -17,41 +16,21 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.IdRes;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.MainActivity;
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
|
||||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
|
||||||
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
|
||||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.TabBar;
|
import org.joinmastodon.android.ui.views.TabBar;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.annotation.IdRes;
|
import androidx.annotation.IdRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
|
||||||
|
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
import me.grishka.appkit.fragments.AppKitFragment;
|
import me.grishka.appkit.fragments.AppKitFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
@@ -65,13 +44,14 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
|
|
||||||
private HomeTabFragment homeTabFragment;
|
private HomeTabFragment homeTabFragment;
|
||||||
|
|
||||||
|
// private HomeTimelineFragment homeTimelineFragment;
|
||||||
|
|
||||||
private NotificationsFragment notificationsFragment;
|
private NotificationsFragment notificationsFragment;
|
||||||
private DiscoverFragment searchFragment;
|
private DiscoverFragment searchFragment;
|
||||||
private ProfileFragment profileFragment;
|
private ProfileFragment profileFragment;
|
||||||
private TabBar tabBar;
|
private TabBar tabBar;
|
||||||
private View tabBarWrap;
|
private View tabBarWrap;
|
||||||
private ImageView tabBarAvatar;
|
private ImageView tabBarAvatar;
|
||||||
private ImageView notificationTabIcon;
|
|
||||||
@IdRes
|
@IdRes
|
||||||
private int currentTab=R.id.tab_home;
|
private int currentTab=R.id.tab_home;
|
||||||
|
|
||||||
@@ -80,7 +60,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
E.register(this);
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
setTitle(R.string.mo_app_name);
|
setTitle(R.string.mo_app_name);
|
||||||
|
|
||||||
@@ -94,6 +73,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
homeTabFragment=new HomeTabFragment();
|
homeTabFragment=new HomeTabFragment();
|
||||||
homeTabFragment.setArguments(args);
|
homeTabFragment.setArguments(args);
|
||||||
|
|
||||||
|
// homeTimelineFragment=new HomeTimelineFragment();
|
||||||
|
// homeTimelineFragment.setArguments(args);
|
||||||
|
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
args.putBoolean("noAutoLoad", true);
|
args.putBoolean("noAutoLoad", true);
|
||||||
searchFragment=new DiscoverFragment();
|
searchFragment=new DiscoverFragment();
|
||||||
@@ -135,9 +117,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28)));
|
ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28)));
|
||||||
|
|
||||||
notificationTabIcon=content.findViewById(R.id.tab_notifications);
|
|
||||||
updateNotificationBadge();
|
|
||||||
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.add(R.id.fragment_wrap, homeTabFragment)
|
.add(R.id.fragment_wrap, homeTabFragment)
|
||||||
@@ -146,6 +125,12 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
|
// getChildFragmentManager().beginTransaction()
|
||||||
|
// .add(R.id.fragment_wrap, homeTimelineFragment)
|
||||||
|
// .add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||||
|
// .add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||||
|
// .add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||||
|
// .commit();
|
||||||
|
|
||||||
String defaultTab=getArguments().getString("tab");
|
String defaultTab=getArguments().getString("tab");
|
||||||
if("notifications".equals(defaultTab)){
|
if("notifications".equals(defaultTab)){
|
||||||
@@ -170,9 +155,12 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
|
|
||||||
if(savedInstanceState==null) return;
|
if(savedInstanceState==null) return;
|
||||||
|
|
||||||
|
// if(savedInstanceState==null || homeTimelineFragment!=null)
|
||||||
|
// return;
|
||||||
|
|
||||||
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
|
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
|
||||||
|
|
||||||
|
// homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
|
||||||
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
||||||
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
||||||
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
||||||
@@ -187,14 +175,19 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
.show(current)
|
.show(current)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
|
// getChildFragmentManager().beginTransaction()
|
||||||
|
// .hide(homeTimelineFragment)
|
||||||
|
// .hide(searchFragment)
|
||||||
|
// .hide(notificationsFragment)
|
||||||
|
// .hide(profileFragment)
|
||||||
|
// .show(current)
|
||||||
|
// .commit();
|
||||||
maybeTriggerLoading(current);
|
maybeTriggerLoading(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHiddenChanged(boolean hidden){
|
public void onHiddenChanged(boolean hidden){
|
||||||
super.onHiddenChanged(hidden);
|
super.onHiddenChanged(hidden);
|
||||||
if (!hidden && fragmentForTab(currentTab) instanceof DomainDisplay display)
|
|
||||||
DomainManager.getInstance().setCurrentDomain(display.getDomain());
|
|
||||||
fragmentForTab(currentTab).onHiddenChanged(hidden);
|
fragmentForTab(currentTab).onHiddenChanged(hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +214,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
|
|
||||||
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
|
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
|
|
||||||
|
// homeTimelineFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
|
|
||||||
searchFragment.onApplyWindowInsets(topOnlyInsets);
|
searchFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
|
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
profileFragment.onApplyWindowInsets(topOnlyInsets);
|
profileFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
@@ -229,6 +224,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
private Fragment fragmentForTab(@IdRes int tab){
|
private Fragment fragmentForTab(@IdRes int tab){
|
||||||
if(tab==R.id.tab_home){
|
if(tab==R.id.tab_home){
|
||||||
return homeTabFragment;
|
return homeTabFragment;
|
||||||
|
|
||||||
|
// if(tab==R.id.tab_home){
|
||||||
|
// return homeTimelineFragment;
|
||||||
}else if(tab==R.id.tab_search){
|
}else if(tab==R.id.tab_search){
|
||||||
return searchFragment;
|
return searchFragment;
|
||||||
}else if(tab==R.id.tab_notifications){
|
}else if(tab==R.id.tab_notifications){
|
||||||
@@ -257,11 +255,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
scrollable.scrollToTop();
|
scrollable.scrollToTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newFragment instanceof DomainDisplay display) {
|
|
||||||
DomainManager.getInstance().setCurrentDomain(display.getDomain());
|
|
||||||
}
|
|
||||||
|
|
||||||
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
|
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
|
||||||
maybeTriggerLoading(newFragment);
|
maybeTriggerLoading(newFragment);
|
||||||
currentTab=tab;
|
currentTab=tab;
|
||||||
@@ -292,10 +285,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||||
options.add(session.self.displayName+"\n("+session.self.username+"@"+session.domain+")");
|
options.add(session.self.displayName+"\n("+session.self.username+"@"+session.domain+")");
|
||||||
}
|
}
|
||||||
new AccountSwitcherSheet(getActivity(), true, true, false, accountSession -> {
|
new AccountSwitcherSheet(getActivity()).show();
|
||||||
getActivity().finish();
|
|
||||||
getActivity().startActivity(new Intent(getActivity(), MainActivity.class));
|
|
||||||
}).show();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(tab==R.id.tab_search){
|
if(tab==R.id.tab_search){
|
||||||
@@ -331,50 +321,10 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||||
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||||
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||||
}
|
|
||||||
|
|
||||||
public void updateNotificationBadge() {
|
// getChildFragmentManager().putFragment(outState, "homeTimelineFragment", homeTimelineFragment);
|
||||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
// getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
// getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||||
if (instance == null) return;
|
// getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||||
|
|
||||||
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance != null && instance.pleroma != null)
|
|
||||||
.setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<Notification> notifications) {
|
|
||||||
if (notifications.size() > 0) {
|
|
||||||
try {
|
|
||||||
long newestId = Long.parseLong(notifications.get(0).id);
|
|
||||||
long lastSeenId = Long.parseLong(session.markers.notifications.lastReadId);
|
|
||||||
System.out.println("NEWEST: " + newestId);
|
|
||||||
System.out.println("LAST SEEN: " + lastSeenId);
|
|
||||||
|
|
||||||
setNotificationBadge(newestId > lastSeenId);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
setNotificationBadge(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
setNotificationBadge(false);
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
public void setNotificationBadge(boolean badge) {
|
|
||||||
notificationTabIcon.setImageResource(badge
|
|
||||||
? R.drawable.ic_fluent_alert_28_selector_badged
|
|
||||||
: R.drawable.ic_fluent_alert_28_selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onNotificationReceived(NotificationReceivedEvent notificationReceivedEvent) {
|
|
||||||
if (notificationReceivedEvent.account.equals(accountID)) setNotificationBadge(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onAllNotificationsSeen(AllNotificationsSeenEvent allNotificationsSeenEvent) {
|
|
||||||
setNotificationBadge(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.reduceMotion;
|
import static org.joinmastodon.android.GlobalUserPreferences.showFederatedTimeline;
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.showNewPostsButton;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -36,33 +35,27 @@ import androidx.viewpager2.widget.ViewPager2;
|
|||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
|
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
||||||
import org.joinmastodon.android.model.Announcement;
|
import org.joinmastodon.android.model.Announcement;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -72,16 +65,18 @@ import me.grishka.appkit.fragments.OnBackPressedListener;
|
|||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener, DomainDisplay {
|
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener {
|
||||||
private static final int ANNOUNCEMENTS_RESULT = 654;
|
private static final int ANNOUNCEMENTS_RESULT = 654;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private MenuItem announcements, announcementsAction, settings, settingsAction;
|
private MenuItem announcements;
|
||||||
// private ImageView toolbarLogo;
|
// private ImageView toolbarLogo;
|
||||||
private Button toolbarShowNewPostsBtn;
|
private Button toolbarShowNewPostsBtn;
|
||||||
private boolean newPostsBtnShown;
|
private boolean newPostsBtnShown;
|
||||||
private AnimatorSet currentNewPostsAnim;
|
private AnimatorSet currentNewPostsAnim;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
|
private final List<Fragment> fragments = new ArrayList<>();
|
||||||
|
private final List<FrameLayout> tabViews = new ArrayList<>();
|
||||||
private View switcher;
|
private View switcher;
|
||||||
private FrameLayout toolbarFrame;
|
private FrameLayout toolbarFrame;
|
||||||
private ImageView timelineIcon;
|
private ImageView timelineIcon;
|
||||||
@@ -90,29 +85,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
private PopupMenu switcherPopup;
|
private PopupMenu switcherPopup;
|
||||||
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
||||||
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
||||||
private List<TimelineDefinition> timelineDefinitions;
|
|
||||||
private int count;
|
|
||||||
private Fragment[] fragments;
|
|
||||||
private FrameLayout[] tabViews;
|
|
||||||
private TimelineDefinition[] timelines;
|
|
||||||
private final Map<Integer, TimelineDefinition> timelinesByMenuItem = new HashMap<>();
|
|
||||||
private SubMenu hashtagsMenu, listsMenu;
|
|
||||||
private PopupMenu overflowPopup;
|
|
||||||
private View overflowActionView = null;
|
|
||||||
private boolean announcementsBadged, settingsBadged;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
E.register(this);
|
|
||||||
accountID = getArguments().getString("account");
|
accountID = getArguments().getString("account");
|
||||||
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES);
|
|
||||||
assert timelineDefinitions != null;
|
|
||||||
if (timelineDefinitions.size() == 0) timelineDefinitions = List.of(TimelineDefinition.HOME_TIMELINE);
|
|
||||||
count = timelineDefinitions.size();
|
|
||||||
fragments = new Fragment[count];
|
|
||||||
tabViews = new FrameLayout[count];
|
|
||||||
timelines = new TimelineDefinition[count];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,40 +104,36 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
pager = new ViewPager2(getContext());
|
pager = new ViewPager2(getContext());
|
||||||
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
||||||
|
|
||||||
if (fragments[0] == null) {
|
if (fragments.size() == 0) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putBoolean("__is_tab", true);
|
args.putBoolean("__is_tab", true);
|
||||||
args.putBoolean("onlyPosts", true);
|
|
||||||
|
|
||||||
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
fragments.add(new HomeTimelineFragment());
|
||||||
TimelineDefinition tl = timelineDefinitions.get(i);
|
fragments.add(new LocalTimelineFragment());
|
||||||
fragments[i] = tl.getFragment();
|
if (showFederatedTimeline) fragments.add(new FederatedTimelineFragment());
|
||||||
timelines[i] = tl;
|
args=new Bundle(args);
|
||||||
}
|
args.putBoolean("onlyPosts", true);
|
||||||
|
NotificationsListFragment postsFragment=new NotificationsListFragment();
|
||||||
|
postsFragment.setArguments(args);
|
||||||
|
fragments.add(postsFragment);
|
||||||
|
|
||||||
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < fragments.size(); i++) {
|
||||||
fragments[i].setArguments(timelines[i].populateArguments(new Bundle(args)));
|
fragments.get(i).setArguments(args);
|
||||||
FrameLayout tabView = new FrameLayout(getActivity());
|
FrameLayout tabView = new FrameLayout(getActivity());
|
||||||
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
tabView.setVisibility(View.GONE);
|
tabView.setVisibility(View.GONE);
|
||||||
tabView.setId(i + 1);
|
tabView.setId(i + 1);
|
||||||
transaction.add(i + 1, fragments[i]);
|
transaction.add(i + 1, fragments.get(i));
|
||||||
view.addView(tabView);
|
view.addView(tabView);
|
||||||
tabViews[i] = tabView;
|
tabViews.add(tabView);
|
||||||
}
|
}
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
overflowActionView = UiUtils.makeOverflowActionView(getContext());
|
|
||||||
overflowPopup = new PopupMenu(getContext(), overflowActionView);
|
|
||||||
overflowPopup.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
|
||||||
overflowActionView.setOnClickListener(l -> overflowPopup.show());
|
|
||||||
overflowActionView.setOnTouchListener(overflowPopup.getDragToOpenListener());
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,40 +147,37 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
||||||
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
||||||
switcherPopup = new PopupMenu(getContext(), switcher);
|
switcherPopup = new PopupMenu(getContext(), switcher);
|
||||||
|
switcherPopup.inflate(R.menu.home_switcher);
|
||||||
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
||||||
switcher.setOnClickListener(v->switcherPopup.show());
|
switcher.setOnClickListener(v->{
|
||||||
switcher.setOnTouchListener(switcherPopup.getDragToOpenListener());
|
updateSwitcherMenu();
|
||||||
updateSwitcherMenu();
|
switcherPopup.show();
|
||||||
|
});
|
||||||
|
View.OnTouchListener listener = switcherPopup.getDragToOpenListener();
|
||||||
|
switcher.setOnTouchListener((v, m)-> {
|
||||||
|
updateSwitcherMenu();
|
||||||
|
return listener.onTouch(v, m);
|
||||||
|
});
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new HomePagerAdapter());
|
pager.setAdapter(new HomePagerAdapter());
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position){
|
public void onPageSelected(int position){
|
||||||
if (!reduceMotion) {
|
|
||||||
// setting this here because page transformer appears to fire too late so the
|
|
||||||
// animation can appear bumpy, especially when navigating to a further-away tab
|
|
||||||
switcher.setScaleY(0.85f);
|
|
||||||
switcher.setScaleX(0.85f);
|
|
||||||
switcher.setAlpha(0.65f);
|
|
||||||
}
|
|
||||||
updateSwitcherIcon(position);
|
updateSwitcherIcon(position);
|
||||||
if (!timelines[position].equals(TimelineDefinition.HOME_TIMELINE)) hideNewPostsButton();
|
if (position==0) return;
|
||||||
if (fragments[position] instanceof BaseRecyclerFragment<?> page){
|
hideNewPostsButton();
|
||||||
|
if (fragments.get(position) instanceof BaseRecyclerFragment<?> page){
|
||||||
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
//update recent app list url
|
|
||||||
if (fragments[position] instanceof DomainDisplay page)
|
|
||||||
DomainManager.getInstance().setCurrentDomain(page.getDomain());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!reduceMotion) {
|
if (!GlobalUserPreferences.reduceMotion) {
|
||||||
pager.setPageTransformer((v, pos) -> {
|
pager.setPageTransformer((v, pos) -> {
|
||||||
if (reduceMotion || tabViews[pager.getCurrentItem()] != v) return;
|
if (tabViews.get(pager.getCurrentItem()) != v) return;
|
||||||
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
||||||
switcher.setScaleY(scaleFactor);
|
switcher.setScaleY(scaleFactor);
|
||||||
switcher.setScaleX(scaleFactor);
|
switcher.setScaleX(scaleFactor);
|
||||||
@@ -217,37 +187,15 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
updateToolbarLogo();
|
updateToolbarLogo();
|
||||||
|
|
||||||
ViewTreeObserver vto = getToolbar().getViewTreeObserver();
|
|
||||||
if (vto.isAlive()) {
|
|
||||||
vto.addOnGlobalLayoutListener(() -> {
|
|
||||||
Toolbar t = getToolbar();
|
|
||||||
if (t == null) return;
|
|
||||||
int toolbarWidth = t.getWidth();
|
|
||||||
if (toolbarWidth == 0) return;
|
|
||||||
|
|
||||||
int toolbarFrameWidth = toolbarFrame.getWidth();
|
|
||||||
int padding = toolbarWidth - toolbarFrameWidth;
|
|
||||||
FrameLayout parent = ((FrameLayout) toolbarShowNewPostsBtn.getParent());
|
|
||||||
if (padding == parent.getPaddingStart()) return;
|
|
||||||
|
|
||||||
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
|
||||||
// centering button by applying the same space on the left
|
|
||||||
parent.setPaddingRelative(padding, 0, 0, 0);
|
|
||||||
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
|
||||||
|
|
||||||
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
|
||||||
switcher.setPivotY(switcher.getHeight() / 2f);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
|
E.register(this);
|
||||||
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetLists().setCallback(new Callback<>() {
|
new GetLists().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
updateList(lists, listItems);
|
addItemsToMap(lists, listItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -259,7 +207,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
new GetFollowedHashtags().setCallback(new Callback<>() {
|
new GetFollowedHashtags().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
||||||
updateList(hashtags, hashtagsItems);
|
addItemsToMap(hashtags, hashtagsItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -267,55 +215,6 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|
||||||
new GetAnnouncements(false).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<Announcement> result) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
if (result.stream().anyMatch(a -> !a.read)) {
|
|
||||||
announcementsBadged = true;
|
|
||||||
announcements.setVisible(false);
|
|
||||||
announcementsAction.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
if (fragments[pager.getCurrentItem()] instanceof DomainDisplay page) {
|
|
||||||
return page.getDomain();
|
|
||||||
}
|
|
||||||
return DomainDisplay.super.getDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addListsToOverflowMenu() {
|
|
||||||
Context ctx = getContext();
|
|
||||||
listsMenu.clear();
|
|
||||||
listsMenu.getItem().setVisible(listItems.size() > 0);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(listsMenu));
|
|
||||||
listItems.forEach((id, list) -> {
|
|
||||||
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_people_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addHashtagsToOverflowMenu() {
|
|
||||||
Context ctx = getContext();
|
|
||||||
hashtagsMenu.clear();
|
|
||||||
hashtagsMenu.getItem().setVisible(hashtagsItems.size() > 0);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(hashtagsMenu));
|
|
||||||
hashtagsItems.forEach((id, hashtag) -> {
|
|
||||||
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, item);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateToolbarLogo(){
|
public void updateToolbarLogo(){
|
||||||
@@ -330,6 +229,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
updateSwitcherIcon(pager.getCurrentItem());
|
updateSwitcherIcon(pager.getCurrentItem());
|
||||||
|
|
||||||
|
// toolbarLogo=new ImageView(getActivity());
|
||||||
|
// toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
|
// toolbarLogo.setImageResource(R.drawable.logo);
|
||||||
|
// toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
|
||||||
|
|
||||||
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
||||||
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
||||||
@@ -350,90 +254,118 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
toolbarShowNewPostsBtn.setScaleY(.8f);
|
toolbarShowNewPostsBtn.setScaleY(.8f);
|
||||||
timelineTitle.setVisibility(View.VISIBLE);
|
timelineTitle.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void updateOverflowMenu() {
|
ViewTreeObserver vto = toolbar.getViewTreeObserver();
|
||||||
if (getActivity() == null) return;
|
if (vto.isAlive()) {
|
||||||
Menu m = overflowPopup.getMenu();
|
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
m.clear();
|
@Override
|
||||||
overflowPopup.inflate(R.menu.home_overflow);
|
public void onGlobalLayout() {
|
||||||
announcements = m.findItem(R.id.announcements);
|
Toolbar t = getToolbar();
|
||||||
settings = m.findItem(R.id.settings);
|
if (t == null) return;
|
||||||
hashtagsMenu = m.findItem(R.id.hashtags).getSubMenu();
|
int toolbarWidth = t.getWidth();
|
||||||
listsMenu = m.findItem(R.id.lists).getSubMenu();
|
if (toolbarWidth == 0) return;
|
||||||
|
t.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
|
||||||
announcements.setVisible(!announcementsBadged);
|
int toolbarFrameWidth = toolbarFrame.getWidth();
|
||||||
announcementsAction.setVisible(announcementsBadged);
|
int padding = toolbarWidth - toolbarFrameWidth;
|
||||||
settings.setVisible(!settingsBadged);
|
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
||||||
settingsAction.setVisible(settingsBadged);
|
// centering button by applying the same space on the left
|
||||||
|
((FrameLayout) toolbarShowNewPostsBtn.getParent()).setPaddingRelative(padding, 0, 0, 0);
|
||||||
|
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
||||||
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), overflowPopup);
|
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
||||||
|
switcher.setPivotY(switcher.getHeight() / 2f);
|
||||||
addListsToOverflowMenu();
|
timelineTitle.setPivotX(timelineTitle.getWidth() - V.dp(8));
|
||||||
addHashtagsToOverflowMenu();
|
timelineTitle.setPivotY(timelineTitle.getHeight() / 2f);
|
||||||
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !UiUtils.isEMUI()) {
|
});
|
||||||
m.setGroupDividerEnabled(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||||
inflater.inflate(R.menu.home, menu);
|
inflater.inflate(R.menu.home, menu);
|
||||||
|
announcements = menu.findItem(R.id.announcements);
|
||||||
|
|
||||||
menu.findItem(R.id.overflow).setActionView(overflowActionView);
|
new GetAnnouncements(false).setCallback(new Callback<>() {
|
||||||
announcementsAction = menu.findItem(R.id.announcements_action);
|
@Override
|
||||||
settingsAction = menu.findItem(R.id.settings_action);
|
public void onSuccess(List<Announcement> result) {
|
||||||
|
boolean hasUnread = result.stream().anyMatch(a -> !a.read);
|
||||||
|
announcements.setIcon(hasUnread ? R.drawable.ic_announcements_24_badged : R.drawable.ic_fluent_megaphone_24_regular);
|
||||||
|
}
|
||||||
|
|
||||||
updateOverflowMenu();
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void updateList(List<T> addItems, Map<Integer, T> items) {
|
private <T> void addItemsToMap(List<T> addItems, Map<Integer, T> items) {
|
||||||
if (addItems.size() == 0 || getActivity() == null) return;
|
if (addItems.size() == 0) return;
|
||||||
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
||||||
updateOverflowMenu();
|
updateSwitcherMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwitcherMenu() {
|
private void updateSwitcherMenu() {
|
||||||
Menu switcherMenu = switcherPopup.getMenu();
|
Context context = getContext();
|
||||||
switcherMenu.clear();
|
switcherPopup.getMenu().findItem(R.id.federated).setVisible(showFederatedTimeline);
|
||||||
timelinesByMenuItem.clear();
|
|
||||||
|
|
||||||
for (TimelineDefinition tl : timelines) {
|
if (!listItems.isEmpty()) {
|
||||||
int menuItemId = View.generateViewId();
|
MenuItem listsItem = switcherPopup.getMenu().findItem(R.id.lists);
|
||||||
timelinesByMenuItem.put(menuItemId, tl);
|
listsItem.setVisible(true);
|
||||||
MenuItem item = switcherMenu.add(0, menuItemId, 0, tl.getTitle(getContext()));
|
SubMenu listsMenu = listsItem.getSubMenu();
|
||||||
item.setIcon(tl.getIcon().iconRes);
|
listsMenu.clear();
|
||||||
|
listItems.forEach((id, list) -> {
|
||||||
|
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_people_list_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(context, item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
if (!hashtagsItems.isEmpty()) {
|
||||||
|
MenuItem hashtagsItem = switcherPopup.getMenu().findItem(R.id.followed_hashtags);
|
||||||
|
hashtagsItem.setVisible(true);
|
||||||
|
SubMenu hashtagsMenu = hashtagsItem.getSubMenu();
|
||||||
|
hashtagsMenu.clear();
|
||||||
|
hashtagsItems.forEach((id, hashtag) -> {
|
||||||
|
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(context, item);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onSwitcherItemSelected(MenuItem item) {
|
private boolean onSwitcherItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
ListTimeline list;
|
||||||
Bundle args = new Bundle();
|
Hashtag hashtag;
|
||||||
args.putString("account", accountID);
|
if (id == R.id.home) {
|
||||||
|
navigateTo(0);
|
||||||
if (id == R.id.menu_back) {
|
|
||||||
switcher.post(() -> switcherPopup.show());
|
|
||||||
return true;
|
return true;
|
||||||
|
} else if (id == R.id.local) {
|
||||||
|
navigateTo(1);
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.federated) {
|
||||||
|
navigateTo(2);
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.post_notifications) {
|
||||||
|
navigateTo(showFederatedTimeline ? 3 : 2);
|
||||||
|
} else if ((list = listItems.get(id)) != null) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("listID", list.id);
|
||||||
|
args.putString("listTitle", list.title);
|
||||||
|
args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||||
|
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||||
|
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||||
|
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag.name, hashtag.following);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineDefinition tl = timelinesByMenuItem.get(id);
|
|
||||||
if (tl != null) {
|
|
||||||
for (int i = 0; i < timelines.length; i++) {
|
|
||||||
if (timelines[i] == tl) {
|
|
||||||
navigateTo(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i) {
|
private void navigateTo(int i) {
|
||||||
navigateTo(i, !reduceMotion);
|
navigateTo(i, !GlobalUserPreferences.reduceMotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i, boolean smooth) {
|
private void navigateTo(int i, boolean smooth) {
|
||||||
@@ -442,54 +374,38 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwitcherIcon(int i) {
|
private void updateSwitcherIcon(int i) {
|
||||||
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
// todo: refactor when implementing pinned tabs
|
||||||
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
if (i == (showFederatedTimeline ? 3 : 2)) {
|
||||||
|
timelineIcon.setImageResource(R.drawable.ic_fluent_alert_24_regular);
|
||||||
|
timelineTitle.setText(R.string.sk_notify_posts);
|
||||||
|
} else {
|
||||||
|
timelineIcon.setImageResource(switch (i) {
|
||||||
|
default -> R.drawable.ic_fluent_home_24_regular;
|
||||||
|
case 1 -> R.drawable.ic_fluent_people_community_24_regular;
|
||||||
|
case 2 -> R.drawable.ic_fluent_earth_24_regular;
|
||||||
|
});
|
||||||
|
timelineTitle.setText(switch (i) {
|
||||||
|
default -> R.string.sk_timeline_home;
|
||||||
|
case 1 -> R.string.sk_timeline_local;
|
||||||
|
case 2 -> R.string.sk_timeline_federated;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
int id = item.getItemId();
|
if (item.getItemId() == R.id.settings) Nav.go(getActivity(), SettingsFragment.class, args);
|
||||||
ListTimeline list;
|
if (item.getItemId() == R.id.announcements) {
|
||||||
Hashtag hashtag;
|
|
||||||
|
|
||||||
if (item.getItemId() == R.id.menu_back) {
|
|
||||||
getToolbar().post(() -> overflowPopup.show());
|
|
||||||
return true;
|
|
||||||
} else if (id == R.id.settings || id == R.id.settings_action) {
|
|
||||||
Nav.go(getActivity(), SettingsFragment.class, args);
|
|
||||||
} else if (id == R.id.announcements || id == R.id.announcements_action) {
|
|
||||||
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
||||||
} else if (id == R.id.edit_timelines) {
|
|
||||||
Nav.go(getActivity(), EditTimelinesFragment.class, args);
|
|
||||||
} else if ((list = listItems.get(id)) != null) {
|
|
||||||
args.putString("listID", list.id);
|
|
||||||
args.putString("listTitle", list.title);
|
|
||||||
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
|
||||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
|
||||||
args.putString("hashtag", hashtag.name);
|
|
||||||
args.putBoolean("following", hashtag.following);
|
|
||||||
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollToTop(){
|
public void scrollToTop(){
|
||||||
if (((ScrollableToTop) fragments[pager.getCurrentItem()]).isScrolledToTop() &&
|
((ScrollableToTop) fragments.get(pager.getCurrentItem())).scrollToTop();
|
||||||
!GlobalUserPreferences.disableDoubleTapToSwipe && !newPostsBtnShown) {
|
|
||||||
int nextPage = (pager.getCurrentItem() + 1) % count;
|
|
||||||
navigateTo(nextPage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((ScrollableToTop) fragments[pager.getCurrentItem()]).scrollToTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return ((ScrollableToTop) fragments[pager.getCurrentItem()]).isScrolledToTop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideNewPostsButton(){
|
public void hideNewPostsButton(){
|
||||||
@@ -510,7 +426,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
||||||
);
|
);
|
||||||
set.setDuration(reduceMotion ? 0 : 300);
|
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
@@ -525,7 +441,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showNewPostsButton(){
|
public void showNewPostsButton(){
|
||||||
if(newPostsBtnShown || pager == null || !timelines[pager.getCurrentItem()].equals(TimelineDefinition.HOME_TIMELINE))
|
if(newPostsBtnShown || pager == null || pager.getCurrentItem() != 0)
|
||||||
return;
|
return;
|
||||||
newPostsBtnShown=true;
|
newPostsBtnShown=true;
|
||||||
if(currentNewPostsAnim!=null){
|
if(currentNewPostsAnim!=null){
|
||||||
@@ -543,7 +459,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
||||||
);
|
);
|
||||||
set.setDuration(reduceMotion ? 0 : 300);
|
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
@@ -562,26 +478,21 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
private void onNewPostsBtnClick(View view) {
|
private void onNewPostsBtnClick(View view) {
|
||||||
if(newPostsBtnShown){
|
if(newPostsBtnShown){
|
||||||
scrollToTop();
|
|
||||||
hideNewPostsButton();
|
hideNewPostsButton();
|
||||||
|
scrollToTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
public void onFragmentResult(int reqCode, boolean noMoreUnread, Bundle result){
|
||||||
if (reqCode == ANNOUNCEMENTS_RESULT && success) {
|
if (reqCode == ANNOUNCEMENTS_RESULT && noMoreUnread) {
|
||||||
announcementsBadged = false;
|
announcements.setIcon(R.drawable.ic_fluent_megaphone_24_regular);
|
||||||
announcements.setVisible(true);
|
|
||||||
announcementsAction.setVisible(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
||||||
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING) {
|
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING)
|
||||||
settingsBadged = true;
|
getToolbar().getMenu().findItem(R.id.settings).setIcon(R.drawable.ic_settings_24_badged);
|
||||||
settingsAction.setVisible(true);
|
|
||||||
settings.setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -601,26 +512,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView(){
|
public void onDestroyView(){
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
if (overflowPopup != null) {
|
|
||||||
overflowPopup.dismiss();
|
|
||||||
overflowPopup = null;
|
|
||||||
}
|
|
||||||
if (switcherPopup != null) {
|
|
||||||
switcherPopup.dismiss();
|
|
||||||
switcherPopup = null;
|
|
||||||
}
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
E.unregister(this);
|
E.unregister(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown() {
|
|
||||||
super.onShown();
|
|
||||||
Object pinnedTimelines = GlobalUserPreferences.pinnedTimelines.get(accountID);
|
|
||||||
if (pinnedTimelines != null && timelineDefinitions != pinnedTimelines) UiUtils.restartApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewStateRestored(Bundle savedInstanceState) {
|
public void onViewStateRestored(Bundle savedInstanceState) {
|
||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
@@ -634,61 +530,12 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
outState.putInt("selectedTab", pager.getCurrentItem());
|
outState.putInt("selectedTab", pager.getCurrentItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onHashtagUpdatedEvent(HashtagUpdatedEvent event) {
|
|
||||||
handleListEvent(hashtagsItems, h -> h.name.equalsIgnoreCase(event.name), event.following, () -> {
|
|
||||||
Hashtag hashtag = new Hashtag();
|
|
||||||
hashtag.name = event.name;
|
|
||||||
hashtag.following = true;
|
|
||||||
return hashtag;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListDeletedEvent(ListDeletedEvent event) {
|
|
||||||
handleListEvent(listItems, l -> l.id.equals(event.id), false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
|
||||||
handleListEvent(listItems, l -> l.id.equals(event.id), true, () -> {
|
|
||||||
ListTimeline list = new ListTimeline();
|
|
||||||
list.id = event.id;
|
|
||||||
list.title = event.title;
|
|
||||||
list.repliesPolicy = event.repliesPolicy;
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void handleListEvent(
|
|
||||||
Map<Integer, T> existingThings,
|
|
||||||
Predicate<T> matchExisting,
|
|
||||||
boolean shouldBeInList,
|
|
||||||
Supplier<T> makeNewThing
|
|
||||||
) {
|
|
||||||
Optional<Map.Entry<Integer, T>> existingThing = existingThings.entrySet().stream()
|
|
||||||
.filter(e -> matchExisting.test(e.getValue())).findFirst();
|
|
||||||
if (shouldBeInList) {
|
|
||||||
existingThings.put(existingThing.isPresent()
|
|
||||||
? existingThing.get().getKey() : View.generateViewId(), makeNewThing.get());
|
|
||||||
updateOverflowMenu();
|
|
||||||
} else if (existingThing.isPresent() && !shouldBeInList) {
|
|
||||||
existingThings.remove(existingThing.get().getKey());
|
|
||||||
updateOverflowMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Hashtag> getHashtags() {
|
|
||||||
return hashtagsItems.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
FrameLayout tabView = tabViews[viewType % getItemCount()];
|
FrameLayout tabView = tabViews.get(viewType % getItemCount());
|
||||||
ViewGroup tabParent = (ViewGroup) tabView.getParent();
|
((ViewGroup)tabView.getParent()).removeView(tabView);
|
||||||
if (tabParent != null) tabParent.removeView(tabView);
|
|
||||||
tabView.setVisibility(View.VISIBLE);
|
tabView.setVisibility(View.VISIBLE);
|
||||||
return new SimpleViewHolder(tabView);
|
return new SimpleViewHolder(tabView);
|
||||||
}
|
}
|
||||||
@@ -698,7 +545,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
return count;
|
return fragments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -32,21 +32,11 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTimelineFragment extends StatusListFragment {
|
public class HomeTimelineFragment extends FabStatusListFragment {
|
||||||
private HomeTabFragment parent;
|
private HomeTabFragment parent;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
private String lastSavedMarkerID;
|
private String lastSavedMarkerID;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/home";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
@@ -55,7 +45,6 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Status> filterPosts(List<Status> items) {
|
private List<Status> filterPosts(List<Status> items) {
|
||||||
// This is the function I must use to solve the filters thing for real
|
|
||||||
return items.stream().filter(i ->
|
return items.stream().filter(i ->
|
||||||
(GlobalUserPreferences.showReplies || i.inReplyToId == null) &&
|
(GlobalUserPreferences.showReplies || i.inReplyToId == null) &&
|
||||||
(GlobalUserPreferences.showBoosts || i.reblog == null)
|
(GlobalUserPreferences.showBoosts || i.reblog == null)
|
||||||
@@ -69,7 +58,8 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
List<Status> filteredItems = filterPosts(result.items);
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
onDataLoaded(filteredItems, !result.items.isEmpty());
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
@@ -160,7 +150,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||||
if(!toAdd.isEmpty()){
|
if(!toAdd.isEmpty()){
|
||||||
prependItems(toAdd, true);
|
prependItems(toAdd, true);
|
||||||
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
|
if (parent != null) parent.showNewPostsButton();
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,10 +162,6 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|
||||||
if (parent.getParentFragment() instanceof HomeFragment homeFragment) {
|
|
||||||
homeFragment.updateNotificationBadge();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
public interface IsOnTop {
|
|
||||||
boolean isOnTop();
|
|
||||||
|
|
||||||
default boolean isRecyclerViewOnTop(@Nullable RecyclerView list) {
|
|
||||||
if (list == null) return true;
|
|
||||||
return !list.canScrollVertically(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,26 +9,17 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetList;
|
import org.joinmastodon.android.api.requests.lists.CreateList;
|
||||||
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -37,15 +28,14 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
|
||||||
public class ListTimelineFragment extends PinnableStatusListFragment {
|
public class ListTimelineFragment extends StatusListFragment {
|
||||||
private String listID;
|
private String listID;
|
||||||
private String listTitle;
|
private String listTitle;
|
||||||
@Nullable
|
|
||||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
|
private ImageButton fab;
|
||||||
|
|
||||||
@Override
|
public ListTimelineFragment() {
|
||||||
protected boolean withComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,58 +48,39 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
|
|
||||||
setTitle(listTitle);
|
setTitle(listTitle);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
new GetList(listID).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(ListTimeline listTimeline) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
// TODO: save updated info
|
|
||||||
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
|
|
||||||
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
|
|
||||||
repliesPolicy = listTimeline.repliesPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.list, menu);
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), menu, R.id.pin);
|
inflater.inflate(R.menu.list, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (super.onOptionsItemSelected(item)) return true;
|
Bundle args = new Bundle();
|
||||||
|
args.putString("listID", listID);
|
||||||
if (item.getItemId() == R.id.edit) {
|
if (item.getItemId() == R.id.edit) {
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
editor.applyList(listTitle, repliesPolicy);
|
editor.applyList(listTitle, repliesPolicy);
|
||||||
new M3AlertDialogBuilder(getActivity())
|
new M3AlertDialogBuilder(getActivity())
|
||||||
.setTitle(R.string.sk_edit_list_title)
|
.setTitle(R.string.sk_edit_list_title)
|
||||||
.setIcon(R.drawable.ic_fluent_people_28_regular)
|
.setIcon(R.drawable.ic_fluent_people_list_28_regular)
|
||||||
.setView(editor)
|
.setView(editor)
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
String newTitle = editor.getTitle().trim();
|
new UpdateList(listID, editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
setTitle(newTitle);
|
|
||||||
new UpdateList(listID, newTitle, editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline list) {
|
public void onSuccess(ListTimeline list) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
setTitle(list.title);
|
setTitle(list.title);
|
||||||
listTitle = list.title;
|
listTitle = list.title;
|
||||||
repliesPolicy = list.repliesPolicy;
|
repliesPolicy = list.repliesPolicy;
|
||||||
E.post(new ListUpdatedCreatedEvent(listID, listTitle, repliesPolicy));
|
args.putString("listTitle", listTitle);
|
||||||
|
args.putInt("repliesPolicy", repliesPolicy.ordinal());
|
||||||
|
setResult(true, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {
|
||||||
setTitle(listTitle);
|
|
||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
@@ -118,30 +89,24 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
.show();
|
.show();
|
||||||
} else if (item.getItemId() == R.id.delete) {
|
} else if (item.getItemId() == R.id.delete) {
|
||||||
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
||||||
E.post(new ListDeletedEvent(listID));
|
args.putBoolean("deleted", true);
|
||||||
|
setResult(true, args);
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TimelineDefinition makeTimelineDefinition() {
|
|
||||||
return TimelineDefinition.ofList(listID, listTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count) {
|
protected void doLoadData(int offset, int count) {
|
||||||
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result) {
|
public void onSuccess(List<Status> result) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.HOME)).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,7 +117,14 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFabClick(View v){
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
|||||||
@@ -12,17 +12,12 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
||||||
import org.joinmastodon.android.api.requests.lists.CreateList;
|
import org.joinmastodon.android.api.requests.lists.CreateList;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
@@ -37,227 +32,215 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class ListTimelinesFragment extends RecyclerFragment<ListTimeline> implements ScrollableToTop {
|
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
|
||||||
private String accountId;
|
private static final int LIST_CHANGED_RESULT = 987;
|
||||||
private String profileAccountId;
|
|
||||||
private final HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
|
||||||
private final HashMap<String, Boolean> userInList = new HashMap<>();
|
|
||||||
private ListsAdapter adapter;
|
|
||||||
|
|
||||||
public ListTimelinesFragment() {
|
private String accountId;
|
||||||
super(10);
|
private String profileAccountId;
|
||||||
}
|
private String profileDisplayUsername;
|
||||||
|
private HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
||||||
|
private HashMap<String, Boolean> userInList = new HashMap<>();
|
||||||
|
private int inProgress = 0;
|
||||||
|
private ListsAdapter adapter;
|
||||||
|
|
||||||
@Override
|
public ListTimelinesFragment() {
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
super(10);
|
||||||
super.onCreate(savedInstanceState);
|
}
|
||||||
Bundle args=getArguments();
|
|
||||||
accountId=args.getString("account");
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
E.register(this);
|
|
||||||
|
|
||||||
if(args.containsKey("profileAccount")){
|
@Override
|
||||||
profileAccountId=args.getString("profileAccount");
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
String profileDisplayUsername = args.getString("profileDisplayUsername");
|
super.onCreate(savedInstanceState);
|
||||||
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
Bundle args=getArguments();
|
||||||
} else {
|
accountId=args.getString("account");
|
||||||
setTitle(R.string.sk_your_lists);
|
setHasOptionsMenu(true);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if(args.containsKey("profileAccount")){
|
||||||
protected void onShown(){
|
profileAccountId=args.getString("profileAccount");
|
||||||
super.onShown();
|
profileDisplayUsername=args.getString("profileDisplayUsername");
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
||||||
loadData();
|
} else {
|
||||||
}
|
setTitle(R.string.sk_your_lists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
protected void onShown(){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onShown();
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
}
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
inflater.inflate(R.menu.menu_list, menu);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
}
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
if (item.getItemId() == R.id.create) {
|
inflater.inflate(R.menu.menu_list, menu);
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
}
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_create_list_title)
|
|
||||||
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
|
||||||
.setView(editor)
|
|
||||||
.setPositiveButton(R.string.sk_create, (d, which) ->
|
|
||||||
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(ListTimeline list) {
|
|
||||||
data.add(0, list);
|
|
||||||
adapter.notifyItemRangeInserted(0, 1);
|
|
||||||
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
error.showToast(getContext());
|
if (item.getItemId() == R.id.create) {
|
||||||
}
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
}).exec(accountId)
|
new M3AlertDialogBuilder(getActivity())
|
||||||
)
|
.setTitle(R.string.sk_create_list_title)
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
||||||
.show();
|
.setView(editor)
|
||||||
}
|
.setPositiveButton(R.string.sk_create, (d, which) -> {
|
||||||
return true;
|
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
}
|
@Override
|
||||||
|
public void onSuccess(ListTimeline list) {
|
||||||
|
saveListMembership(list.id, true);
|
||||||
|
data.add(0, list);
|
||||||
|
adapter.notifyItemRangeInserted(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
private void saveListMembership(String listId, boolean isMember) {
|
@Override
|
||||||
userInList.put(listId, isMember);
|
public void onError(ErrorResponse error) {
|
||||||
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
error.showToast(getContext());
|
||||||
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
}
|
||||||
req.setCallback(new Callback<>() {
|
}).exec(accountId);
|
||||||
@Override
|
})
|
||||||
public void onSuccess(Object o) {}
|
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private void saveListMembership(String listId, boolean isMember) {
|
||||||
public void onError(ErrorResponse error) {
|
userInList.put(listId, isMember);
|
||||||
error.showToast(getContext());
|
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
||||||
}
|
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
||||||
}).exec(accountId);
|
req.setCallback(new SimpleCallback<>(this) {
|
||||||
}
|
@Override
|
||||||
|
public void onSuccess(Object o) {}
|
||||||
|
}).exec(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
userInListBefore.clear();
|
userInListBefore.clear();
|
||||||
userInList.clear();
|
userInList.clear();
|
||||||
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
if (getActivity() == null) return;
|
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
||||||
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
userInList.putAll(userInListBefore);
|
||||||
userInList.putAll(userInListBefore);
|
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
||||||
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
if (profileAccountId == null) return;
|
||||||
if (profileAccountId == null) return;
|
|
||||||
|
|
||||||
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) {
|
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> allLists) {
|
public void onSuccess(List<ListTimeline> allLists) {
|
||||||
if (getActivity() == null) return;
|
List<ListTimeline> newLists = new ArrayList<>();
|
||||||
List<ListTimeline> newLists = new ArrayList<>();
|
for (ListTimeline l : allLists) {
|
||||||
for (ListTimeline l : allLists) {
|
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
||||||
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
if (!userInListBefore.containsKey(l.id)) {
|
||||||
if (!userInListBefore.containsKey(l.id)) {
|
userInListBefore.put(l.id, false);
|
||||||
userInListBefore.put(l.id, false);
|
}
|
||||||
}
|
}
|
||||||
}
|
userInList.putAll(userInListBefore);
|
||||||
userInList.putAll(userInListBefore);
|
onDataLoaded(newLists, false);
|
||||||
onDataLoaded(newLists, false);
|
}
|
||||||
}
|
}).exec(accountId);
|
||||||
}).exec(accountId);
|
}
|
||||||
}
|
})
|
||||||
})
|
.exec(accountId);
|
||||||
.exec(accountId);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
@Override
|
||||||
public void onListDeletedEvent(ListDeletedEvent event) {
|
public void onFragmentResult(int reqCode, boolean listChanged, Bundle result){
|
||||||
for (int i = 0; i < data.size(); i++) {
|
if (reqCode == LIST_CHANGED_RESULT && listChanged) {
|
||||||
ListTimeline item = data.get(i);
|
String listID = result.getString("listID");
|
||||||
if (item.id.equals(event.id)) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
data.remove(i);
|
ListTimeline item = data.get(i);
|
||||||
adapter.notifyItemRemoved(i);
|
if (item.id.equals(listID)) {
|
||||||
break;
|
if (result.getBoolean("deleted")) {
|
||||||
}
|
data.remove(i);
|
||||||
}
|
adapter.notifyItemRemoved(i);
|
||||||
}
|
} else {
|
||||||
|
item.title = result.getString("listTitle", item.title);
|
||||||
|
item.repliesPolicy = ListTimeline.RepliesPolicy.values()[result.getInt("repliesPolicy")];
|
||||||
|
adapter.notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Override
|
||||||
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
||||||
for (int i = 0; i < data.size(); i++) {
|
return adapter = new ListsAdapter();
|
||||||
ListTimeline item = data.get(i);
|
}
|
||||||
if (item.id.equals(event.id)) {
|
|
||||||
item.title = event.title;
|
|
||||||
item.repliesPolicy = event.repliesPolicy;
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
public void scrollToTop() {
|
||||||
return adapter = new ListsAdapter();
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
||||||
public void scrollToTop() {
|
@NonNull
|
||||||
smoothScrollRecyclerViewToTop(list);
|
@Override
|
||||||
}
|
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new ListViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isScrolledToTop() {
|
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
||||||
return list.getChildAt(0).getTop() == 0;
|
holder.bind(data.get(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
@Override
|
||||||
@NonNull
|
public int getItemCount() {
|
||||||
@Override
|
return data.size();
|
||||||
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
}
|
||||||
return new ListViewHolder();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
||||||
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
private final TextView title;
|
||||||
holder.bind(data.get(position));
|
private final CheckBox listToggle;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ListViewHolder(){
|
||||||
public int getItemCount() {
|
super(getActivity(), R.layout.item_text, list);
|
||||||
return data.size();
|
title=findViewById(R.id.title);
|
||||||
}
|
listToggle=findViewById(R.id.list_toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
@Override
|
||||||
private final TextView title;
|
public void onBind(ListTimeline item) {
|
||||||
private final CheckBox listToggle;
|
title.setText(item.title);
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_list_24_regular), null, null, null);
|
||||||
|
if (profileAccountId != null) {
|
||||||
|
Boolean checked = userInList.get(item.id);
|
||||||
|
listToggle.setVisibility(View.VISIBLE);
|
||||||
|
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
||||||
|
listToggle.setOnClickListener(this::onClickToggle);
|
||||||
|
} else {
|
||||||
|
listToggle.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListViewHolder(){
|
private void onClickToggle(View view) {
|
||||||
super(getActivity(), R.layout.item_text, list);
|
saveListMembership(item.id, listToggle.isChecked());
|
||||||
title=findViewById(R.id.title);
|
}
|
||||||
listToggle=findViewById(R.id.list_toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(ListTimeline item) {
|
public void onClick() {
|
||||||
title.setText(item.title);
|
Bundle args=new Bundle();
|
||||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_24_regular), null, null, null);
|
args.putString("account", accountId);
|
||||||
if (profileAccountId != null) {
|
args.putString("listID", item.id);
|
||||||
Boolean checked = userInList.get(item.id);
|
args.putString("listTitle", item.title);
|
||||||
listToggle.setVisibility(View.VISIBLE);
|
args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
||||||
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
Nav.goForResult(getActivity(), ListTimelineFragment.class, args, LIST_CHANGED_RESULT, ListTimelinesFragment.this);
|
||||||
listToggle.setOnClickListener(this::onClickToggle);
|
}
|
||||||
} else {
|
}
|
||||||
listToggle.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onClickToggle(View view) {
|
|
||||||
saveListMembership(item.id, listToggle.isChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountId);
|
|
||||||
args.putString("listID", item.id);
|
|
||||||
args.putString("listTitle", item.title);
|
|
||||||
if (item.repliesPolicy != null) args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop, DomainDisplay{
|
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop{
|
||||||
|
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
@@ -48,11 +48,6 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return DomainDisplay.super.getDomain() + "/notifications";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -107,6 +102,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
|
|
||||||
tabLayout=view.findViewById(R.id.tabbar);
|
tabLayout=view.findViewById(R.id.tabbar);
|
||||||
pager=view.findViewById(R.id.pager);
|
pager=view.findViewById(R.id.pager);
|
||||||
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
|
|
||||||
tabViews=new FrameLayout[3];
|
tabViews=new FrameLayout[3];
|
||||||
for(int i=0;i<tabViews.length;i++){
|
for(int i=0;i<tabViews.length;i++){
|
||||||
@@ -145,20 +141,17 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putBoolean("__is_tab", true);
|
args.putBoolean("__is_tab", true);
|
||||||
args.putBoolean("noAutoLoad", true);
|
|
||||||
|
|
||||||
allNotificationsFragment=new NotificationsListFragment();
|
allNotificationsFragment=new NotificationsListFragment();
|
||||||
allNotificationsFragment.setArguments(args);
|
allNotificationsFragment.setArguments(args);
|
||||||
|
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
args.putBoolean("onlyMentions", true);
|
args.putBoolean("onlyMentions", true);
|
||||||
args.putBoolean("noAutoLoad", true);
|
|
||||||
mentionsFragment=new NotificationsListFragment();
|
mentionsFragment=new NotificationsListFragment();
|
||||||
mentionsFragment.setArguments(args);
|
mentionsFragment.setArguments(args);
|
||||||
|
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
args.putBoolean("onlyPosts", true);
|
args.putBoolean("onlyPosts", true);
|
||||||
args.putBoolean("noAutoLoad", true);
|
|
||||||
postsFragment=new NotificationsListFragment();
|
postsFragment=new NotificationsListFragment();
|
||||||
postsFragment.setArguments(args);
|
postsFragment.setArguments(args);
|
||||||
|
|
||||||
@@ -205,19 +198,9 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollToTop(){
|
public void scrollToTop(){
|
||||||
if (getFragmentForPage(pager.getCurrentItem()).isScrolledToTop() && !GlobalUserPreferences.disableDoubleTapToSwipe) {
|
|
||||||
int nextPage = (pager.getCurrentItem() + 1) % tabViews.length;
|
|
||||||
pager.setCurrentItem(nextPage, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getFragmentForPage(pager.getCurrentItem()).scrollToTop();
|
getFragmentForPage(pager.getCurrentItem()).scrollToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return getFragmentForPage(pager.getCurrentItem()).isScrolledToTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadData(){
|
public void loadData(){
|
||||||
refreshFollowRequestsBadge();
|
refreshFollowRequestsBadge();
|
||||||
if(allNotificationsFragment!=null && !allNotificationsFragment.loaded && !allNotificationsFragment.dataLoading)
|
if(allNotificationsFragment!=null && !allNotificationsFragment.loaded && !allNotificationsFragment.dataLoading)
|
||||||
@@ -244,7 +227,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=tabViews[viewType];
|
FrameLayout view=tabViews[viewType];
|
||||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
view.setVisibility(View.VISIBLE);
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
@@ -263,4 +246,4 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
@@ -11,22 +12,16 @@ import org.joinmastodon.android.E;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
|
||||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -40,22 +35,12 @@ import java.util.stream.Stream;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||||
private boolean onlyMentions;
|
private boolean onlyMentions;
|
||||||
private boolean onlyPosts;
|
private boolean onlyPosts;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/notifications";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -86,8 +71,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
||||||
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
|
|
||||||
n.report.targetAccount;
|
|
||||||
String extraText=switch(n.type){
|
String extraText=switch(n.type){
|
||||||
case FOLLOW -> getString(R.string.user_followed_you);
|
case FOLLOW -> getString(R.string.user_followed_you);
|
||||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||||
@@ -95,24 +78,23 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
case REBLOG -> getString(R.string.notification_boosted);
|
case REBLOG -> getString(R.string.notification_boosted);
|
||||||
case FAVORITE -> getString(R.string.user_favorited);
|
case FAVORITE -> getString(R.string.user_favorited);
|
||||||
case POLL -> getString(R.string.poll_ended);
|
case POLL -> getString(R.string.poll_ended);
|
||||||
case UPDATE -> getString(R.string.sk_post_edited);
|
|
||||||
case SIGN_UP -> getString(R.string.sk_signed_up);
|
|
||||||
case REPORT -> getString(R.string.sk_reported);
|
|
||||||
};
|
};
|
||||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText, n, null) : null;
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, false, Filter.FilterContext.NOTIFICATIONS);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n);
|
||||||
|
if(titleItem!=null){
|
||||||
|
for(StatusDisplayItem item:items){
|
||||||
|
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||||
|
imgItem.horizontalInset=V.dp(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(titleItem!=null)
|
if(titleItem!=null)
|
||||||
items.add(0, titleItem);
|
items.add(0, titleItem);
|
||||||
return items;
|
return items;
|
||||||
}else if(titleItem!=null){
|
}else if(titleItem!=null){
|
||||||
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this,
|
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this, n.account, n);
|
||||||
reportTarget != null ? reportTarget : n.account, n);
|
return Arrays.asList(titleItem, card);
|
||||||
TextStatusDisplayItem text = n.report != null && !TextUtils.isEmpty(n.report.comment) ?
|
|
||||||
new TextStatusDisplayItem(n.id, n.report.comment, this,
|
|
||||||
Status.ofFake(n.id, n.report.comment, n.createdAt), true) :
|
|
||||||
null;
|
|
||||||
return text == null ? Arrays.asList(titleItem, card) : Arrays.asList(titleItem, text, card);
|
|
||||||
}else{
|
}else{
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -124,8 +106,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
knownAccounts.put(s.account.id, s.account);
|
knownAccounts.put(s.account.id, s.account);
|
||||||
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
|
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
|
||||||
knownAccounts.put(s.status.account.id, s.status.account);
|
knownAccounts.put(s.status.account.id, s.status.account);
|
||||||
if(s.status!=null && s.status.reblog!=null && !knownAccounts.containsKey(s.status.reblog.account.id))
|
|
||||||
knownAccounts.put(s.status.reblog.account.id, s.status.reblog.account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,8 +114,9 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
.getAccount(accountID).getCacheController()
|
.getAccount(accountID).getCacheController()
|
||||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Notification>> result){
|
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
if(refreshing)
|
if(refreshing)
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
||||||
@@ -146,12 +127,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
loadRelationships(needRelationships);
|
loadRelationships(needRelationships);
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
|
|
||||||
if(offset==0 && !result.items.isEmpty() && !result.isFromCache()){
|
if(offset==0 && !result.items.isEmpty()){
|
||||||
E.post(new AllNotificationsSeenEvent());
|
|
||||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).markers
|
|
||||||
.notifications.lastReadId = result.items.get(0).id;
|
|
||||||
AccountSessionManager.getInstance().writeAccountsFile();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -171,11 +148,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
@Override
|
@Override
|
||||||
protected void onShown(){
|
protected void onShown(){
|
||||||
super.onShown();
|
super.onShown();
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading){
|
// if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
refreshing=true;
|
// loadData();
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -189,9 +163,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
||||||
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
}else if(n.report != null){
|
|
||||||
String domain = AccountSessionManager.getInstance().getAccount(accountID).domain;
|
|
||||||
UiUtils.launchWebBrowser(getActivity(), "https://"+domain+"/admin/reports/"+n.report.id);
|
|
||||||
}else{
|
}else{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -204,8 +175,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||||
if (getParentFragment() instanceof NotificationsFragment) fab.setVisibility(View.GONE);
|
|
||||||
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification getNotificationByID(String id){
|
private Notification getNotificationByID(String id){
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class PinnableStatusListFragment extends StatusListFragment implements DomainDisplay {
|
|
||||||
protected boolean pinnedUpdated;
|
|
||||||
protected List<TimelineDefinition> pinnedTimelines;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
updatePinButton(menu.findItem(R.id.pin));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isPinned() {
|
|
||||||
return pinnedTimelines.contains(makeTimelineDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updatePinButton(MenuItem pin) {
|
|
||||||
boolean pinned = isPinned();
|
|
||||||
pin.setIcon(pinned ?
|
|
||||||
R.drawable.ic_fluent_pin_24_filled :
|
|
||||||
R.drawable.ic_fluent_pin_24_regular);
|
|
||||||
pin.setTitle(pinned ? R.string.sk_unpin_timeline : R.string.sk_pin_timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract TimelineDefinition makeTimelineDefinition();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.pin) {
|
|
||||||
togglePin(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void togglePin(MenuItem pin) {
|
|
||||||
pinnedUpdated = true;
|
|
||||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
TimelineDefinition def = makeTimelineDefinition();
|
|
||||||
boolean pinned = isPinned();
|
|
||||||
if (pinned) pinnedTimelines.remove(def);
|
|
||||||
else pinnedTimelines.add(def);
|
|
||||||
Toast.makeText(getContext(), pinned ? R.string.sk_unpinned_timeline : R.string.sk_pinned_timeline, Toast.LENGTH_SHORT).show();
|
|
||||||
GlobalUserPreferences.pinnedTimelines.put(accountID, pinnedTimelines);
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
updatePinButton(pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Bundle getResultArgs() {
|
|
||||||
return new Bundle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
Bundle resultArgs = getResultArgs();
|
|
||||||
if (pinnedUpdated) {
|
|
||||||
resultArgs.putBoolean("pinnedUpdated", true);
|
|
||||||
setResult(true, resultArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,11 +8,9 @@ import android.app.Activity;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -24,7 +22,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.SubMenu;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
@@ -48,9 +45,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.MainActivity;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
@@ -67,7 +62,6 @@ import org.joinmastodon.android.model.Account;
|
|||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||||
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
||||||
@@ -76,10 +70,8 @@ import org.joinmastodon.android.ui.tabs.TabLayout;
|
|||||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.CoverImageView;
|
import org.joinmastodon.android.ui.views.CoverImageView;
|
||||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
|
||||||
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
@@ -92,10 +84,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -103,17 +91,10 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
|
||||||
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
|
|
||||||
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
|
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
|
||||||
|
|
||||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
||||||
private static final int AVATAR_RESULT=722;
|
private static final int AVATAR_RESULT=722;
|
||||||
@@ -121,24 +102,23 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
private ImageView avatar;
|
private ImageView avatar;
|
||||||
private CoverImageView cover;
|
private CoverImageView cover;
|
||||||
private View avatarBorder, nameWrap;
|
private View avatarBorder;
|
||||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
||||||
private ProgressBarButton actionButton, notifyButton;
|
private ProgressBarButton actionButton, notifyButton;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private NestedRecyclerScrollView scrollView;
|
private NestedRecyclerScrollView scrollView;
|
||||||
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
||||||
// private ProfileAboutFragment aboutFragment;
|
private ProfileAboutFragment aboutFragment;
|
||||||
private TabLayout tabbar;
|
private TabLayout tabbar;
|
||||||
private SwipeRefreshLayout refreshLayout;
|
private SwipeRefreshLayout refreshLayout;
|
||||||
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
||||||
private float titleTransY;
|
private float titleTransY;
|
||||||
private View postsBtn, followersBtn, followingBtn, profileCounters;
|
private View postsBtn, followersBtn, followingBtn;
|
||||||
private EditText nameEdit, bioEdit;
|
private EditText nameEdit, bioEdit;
|
||||||
private ProgressBar actionProgress, notifyProgress;
|
private ProgressBar actionProgress, notifyProgress;
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
private TabLayoutMediator tabLayoutMediator;
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
private TextView followsYouView;
|
private TextView followsYouView;
|
||||||
private ViewGroup rolesView;
|
|
||||||
|
|
||||||
public FrameLayout noteWrap;
|
public FrameLayout noteWrap;
|
||||||
public EditText noteEdit;
|
public EditText noteEdit;
|
||||||
@@ -158,17 +138,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private WindowInsets childInsets;
|
private WindowInsets childInsets;
|
||||||
private PhotoViewer currentPhotoViewer;
|
private PhotoViewer currentPhotoViewer;
|
||||||
private boolean editModeLoading;
|
private boolean editModeLoading;
|
||||||
protected int scrollDiff = 0;
|
private boolean isScrollingUp = false;
|
||||||
|
|
||||||
private static final int MAX_FIELDS=4;
|
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
public UsableRecyclerView list;
|
|
||||||
private List<AccountField> metadataListData=Collections.emptyList();
|
|
||||||
private MetadataAdapter adapter;
|
|
||||||
private ItemTouchHelper dragHelper=new ItemTouchHelper(new ReorderCallback());
|
|
||||||
private RecyclerView.ViewHolder draggedViewHolder;
|
|
||||||
private ListImageLoaderWrapper imgLoader;
|
|
||||||
|
|
||||||
public ProfileFragment(){
|
public ProfileFragment(){
|
||||||
super(R.layout.loader_fragment_overlay_toolbar);
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
@@ -206,14 +176,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHiddenChanged(boolean hidden) {
|
|
||||||
super.onHiddenChanged(hidden);
|
|
||||||
if (!hidden) {
|
|
||||||
DomainManager.getInstance().setCurrentDomain(account.url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
View content=inflater.inflate(R.layout.fragment_profile, container, false);
|
View content=inflater.inflate(R.layout.fragment_profile, container, false);
|
||||||
@@ -222,10 +184,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
cover=content.findViewById(R.id.cover);
|
cover=content.findViewById(R.id.cover);
|
||||||
avatarBorder=content.findViewById(R.id.avatar_border);
|
avatarBorder=content.findViewById(R.id.avatar_border);
|
||||||
name=content.findViewById(R.id.name);
|
name=content.findViewById(R.id.name);
|
||||||
nameWrap=content.findViewById(R.id.name_wrap);
|
|
||||||
username=content.findViewById(R.id.username);
|
username=content.findViewById(R.id.username);
|
||||||
bio=content.findViewById(R.id.bio);
|
bio=content.findViewById(R.id.bio);
|
||||||
profileCounters=content.findViewById(R.id.profile_counters);
|
|
||||||
followersCount=content.findViewById(R.id.followers_count);
|
followersCount=content.findViewById(R.id.followers_count);
|
||||||
followersLabel=content.findViewById(R.id.followers_label);
|
followersLabel=content.findViewById(R.id.followers_label);
|
||||||
followersBtn=content.findViewById(R.id.followers_btn);
|
followersBtn=content.findViewById(R.id.followers_btn);
|
||||||
@@ -248,8 +208,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
notifyProgress=content.findViewById(R.id.notify_progress);
|
notifyProgress=content.findViewById(R.id.notify_progress);
|
||||||
fab=content.findViewById(R.id.fab);
|
fab=content.findViewById(R.id.fab);
|
||||||
followsYouView=content.findViewById(R.id.follows_you);
|
followsYouView=content.findViewById(R.id.follows_you);
|
||||||
list=content.findViewById(R.id.metadata);
|
|
||||||
rolesView=content.findViewById(R.id.roles);
|
|
||||||
|
|
||||||
noteEdit = content.findViewById(R.id.note_edit);
|
noteEdit = content.findViewById(R.id.note_edit);
|
||||||
noteWrap = content.findViewById(R.id.note_edit_wrap);
|
noteWrap = content.findViewById(R.id.note_edit_wrap);
|
||||||
@@ -316,7 +274,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tabViews=new FrameLayout[4];
|
tabViews=new FrameLayout[5];
|
||||||
for(int i=0;i<tabViews.length;i++){
|
for(int i=0;i<tabViews.length;i++){
|
||||||
FrameLayout tabView=new FrameLayout(getActivity());
|
FrameLayout tabView=new FrameLayout(getActivity());
|
||||||
tabView.setId(switch(i){
|
tabView.setId(switch(i){
|
||||||
@@ -333,7 +291,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setOffscreenPageLimit(4);
|
pager.setOffscreenPageLimit(5);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new ProfilePagerAdapter());
|
pager.setAdapter(new ProfilePagerAdapter());
|
||||||
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
||||||
@@ -367,7 +325,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
});
|
});
|
||||||
|
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
|
||||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||||
avatar.setOnClickListener(this::onAvatarClick);
|
avatar.setOnClickListener(this::onAvatarClick);
|
||||||
cover.setOnClickListener(this::onCoverClick);
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
@@ -395,14 +352,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
list.setItemAnimator(new BetterItemAnimator());
|
|
||||||
list.setDrawSelectorOnTop(true);
|
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
|
||||||
imgLoader=new ListImageLoaderWrapper(getActivity(), list, new RecyclerViewDelegate(list), null);
|
|
||||||
list.setAdapter(adapter=new MetadataAdapter());
|
|
||||||
list.setClipToPadding(false);
|
|
||||||
|
|
||||||
return sizeWrapper;
|
return sizeWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +380,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
account=result;
|
account=result;
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
@@ -462,11 +410,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh(){
|
public void onRefresh(){
|
||||||
if(isInEditMode){
|
|
||||||
refreshing=false;
|
|
||||||
refreshLayout.setRefreshing(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(refreshing)
|
if(refreshing)
|
||||||
return;
|
return;
|
||||||
refreshing=true;
|
refreshing=true;
|
||||||
@@ -482,8 +425,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
||||||
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
||||||
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
||||||
// aboutFragment=new ProfileAboutFragment();
|
aboutFragment=new ProfileAboutFragment();
|
||||||
setFields(fields);
|
aboutFragment.setFields(fields);
|
||||||
}
|
}
|
||||||
pager.getAdapter().notifyDataSetChanged();
|
pager.getAdapter().notifyDataSetChanged();
|
||||||
super.dataLoaded();
|
super.dataLoaded();
|
||||||
@@ -525,7 +468,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
toolbarSubtitleView.setTranslationY(titleTransY);
|
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||||
}
|
}
|
||||||
RecyclerFragment.setRefreshLayoutColors(refreshLayout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -577,21 +519,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
name.setText(ssb);
|
name.setText(ssb);
|
||||||
setTitle(ssb);
|
setTitle(ssb);
|
||||||
|
|
||||||
if (account.roles != null && !account.roles.isEmpty()) {
|
|
||||||
rolesView.setVisibility(View.VISIBLE);
|
|
||||||
rolesView.removeAllViews();
|
|
||||||
name.setPadding(0, 0, V.dp(12), 0);
|
|
||||||
for (Account.Role role : account.roles) {
|
|
||||||
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
|
||||||
roleText.setText(role.name);
|
|
||||||
if (!TextUtils.isEmpty(role.color) && role.color.startsWith("#")) try {
|
|
||||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
|
||||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
rolesView.addView(roleText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
|
||||||
|
|
||||||
@@ -653,12 +580,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
fields.clear();
|
fields.clear();
|
||||||
|
|
||||||
if (account.createdAt != null) {
|
AccountField joined=new AccountField();
|
||||||
AccountField joined=new AccountField();
|
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
||||||
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
||||||
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
fields.add(joined);
|
||||||
fields.add(joined);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(AccountField field:account.fields){
|
for(AccountField field:account.fields){
|
||||||
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
@@ -677,7 +602,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
fields.add(field);
|
fields.add(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFields(fields);
|
if(aboutFragment!=null){
|
||||||
|
aboutFragment.setFields(fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToolbar(){
|
private void updateToolbar(){
|
||||||
@@ -718,16 +645,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}else{
|
}else{
|
||||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
||||||
}
|
}
|
||||||
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
|
||||||
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
|
|
||||||
openWithAccounts.setVisible(hasMultipleAccounts);
|
|
||||||
SubMenu accountsMenu = openWithAccounts.getSubMenu();
|
|
||||||
if (hasMultipleAccounts) {
|
|
||||||
accountsMenu.clear();
|
|
||||||
UiUtils.populateAccountsMenu(accountID, accountsMenu, s-> UiUtils.openURL(
|
|
||||||
getActivity(), s.getID(), account.url, false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||||
if(isOwnProfile)
|
if(isOwnProfile)
|
||||||
return;
|
return;
|
||||||
@@ -850,7 +767,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelationship(){
|
private void updateRelationship(){
|
||||||
if (getActivity() == null) return;
|
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
actionButton.setVisibility(View.VISIBLE);
|
actionButton.setVisibility(View.VISIBLE);
|
||||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||||
@@ -862,10 +778,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
notifyButton.setSelected(relationship.notifying);
|
notifyButton.setSelected(relationship.notifying);
|
||||||
if (!isOwnProfile) {
|
if (!isOwnProfile) {
|
||||||
setNote(relationship.note);
|
setNote(relationship.note);
|
||||||
// aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
||||||
}
|
}
|
||||||
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||||
DomainManager.getInstance().setCurrentDomain(account.url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageButton getFab() {
|
public ImageButton getFab() {
|
||||||
@@ -892,8 +807,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
coverGradient.setTopOffset(scrollY);
|
coverGradient.setTopOffset(scrollY);
|
||||||
cover.invalidate();
|
cover.invalidate();
|
||||||
titleTransY=getToolbar().getHeight();
|
titleTransY=getToolbar().getHeight();
|
||||||
if(scrollY>nameWrap.getTop()-topBarsH){
|
if(scrollY>name.getTop()-topBarsH){
|
||||||
titleTransY=Math.max(0f, titleTransY-(scrollY-(nameWrap.getTop()-topBarsH)));
|
titleTransY=Math.max(0f, titleTransY-(scrollY-(name.getTop()-topBarsH)));
|
||||||
}
|
}
|
||||||
if(toolbarTitleView!=null){
|
if(toolbarTitleView!=null){
|
||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
@@ -902,36 +817,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(currentPhotoViewer!=null){
|
if(currentPhotoViewer!=null){
|
||||||
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(GlobalUserPreferences.enableFabAutoHide){
|
|
||||||
int dy = scrollY - oldScrollY;
|
|
||||||
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2);
|
|
||||||
animate.setDuration(300);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
fab.setVisibility(View.INVISIBLE);
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
|
||||||
if (scrollDiff > 400) {
|
|
||||||
fab.setVisibility(View.VISIBLE);
|
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2,
|
|
||||||
0);
|
|
||||||
animate.setDuration(300);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else {
|
|
||||||
scrollDiff += Math.abs(dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
@@ -940,7 +825,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
case 1 -> postsWithRepliesFragment;
|
case 1 -> postsWithRepliesFragment;
|
||||||
case 2 -> pinnedPostsFragment;
|
case 2 -> pinnedPostsFragment;
|
||||||
case 3 -> mediaFragment;
|
case 3 -> mediaFragment;
|
||||||
// case 4 -> aboutFragment;
|
case 4 -> aboutFragment;
|
||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -960,31 +845,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onActionButtonLongClick(View v) {
|
|
||||||
if (isOwnProfile || AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
|
|
||||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
|
||||||
UiUtils.lookupAccount(getActivity(), account, session.getID(), accountID, acc -> {
|
|
||||||
if (acc == null) return;
|
|
||||||
new SetAccountFollowed(acc.id, true, true).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Relationship relationship) {
|
|
||||||
Toast.makeText(
|
|
||||||
getActivity(),
|
|
||||||
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(session.getID());
|
|
||||||
});
|
|
||||||
}, null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setActionProgressVisible(boolean visible){
|
private void setActionProgressVisible(boolean visible){
|
||||||
actionButton.setTextVisible(!visible);
|
actionButton.setTextVisible(!visible);
|
||||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
@@ -1007,7 +867,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
enterEditMode(result);
|
enterEditMode(result);
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1015,7 +876,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
error.showToast(getActivity());
|
error.showToast(getActivity());
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1030,12 +892,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
pager.setUserInputEnabled(false);
|
pager.setUserInputEnabled(false);
|
||||||
actionButton.setText(R.string.done);
|
actionButton.setText(R.string.done);
|
||||||
|
pager.setCurrentItem(4);
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay, getActivity().getTheme()).mutate();
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, .3f));
|
||||||
|
tabbar.getTabAt(i).view.setEnabled(false);
|
||||||
|
}
|
||||||
|
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
||||||
avatar.setForeground(overlay);
|
avatar.setForeground(overlay);
|
||||||
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
||||||
|
|
||||||
nameWrap.setVisibility(View.GONE);
|
|
||||||
nameEdit.setVisibility(View.VISIBLE);
|
nameEdit.setVisibility(View.VISIBLE);
|
||||||
nameEdit.setText(account.displayName);
|
nameEdit.setText(account.displayName);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
@@ -1047,9 +913,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
bioEdit.setText(account.source.note);
|
bioEdit.setText(account.source.note);
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
||||||
profileCounters.setVisibility(View.GONE);
|
|
||||||
pager.setVisibility(View.GONE);
|
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, .3f));
|
||||||
tabbar.setVisibility(View.GONE);
|
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, .3f));
|
||||||
|
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, .3f));
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
@@ -1057,12 +924,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
// aboutFragment.enterEditMode(account.source.fields);
|
aboutFragment.enterEditMode(account.source.fields);
|
||||||
|
|
||||||
V.setVisibilityAnimated(fab, View.GONE);
|
|
||||||
metadataListData=account.source.fields;
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
dragHelper.attachToRecyclerView(list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exitEditMode(){
|
private void exitEditMode(){
|
||||||
@@ -1073,14 +935,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
actionButton.setText(R.string.edit_profile);
|
actionButton.setText(R.string.edit_profile);
|
||||||
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, 1f));
|
||||||
|
}
|
||||||
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
||||||
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
||||||
profileCounters.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, 1f));
|
||||||
pager.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, 1f));
|
||||||
tabbar.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, 1f));
|
||||||
V.setVisibilityAnimated(nameWrap, View.VISIBLE);
|
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
@@ -1089,21 +953,20 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public void onAnimationEnd(Animator animation){
|
||||||
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
tabbar.getTabAt(i).view.setEnabled(true);
|
||||||
|
}
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
nameEdit.setVisibility(View.GONE);
|
nameEdit.setVisibility(View.GONE);
|
||||||
bioEdit.setVisibility(View.GONE);
|
bioEdit.setVisibility(View.GONE);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
lp.addRule(RelativeLayout.BELOW, R.id.name_wrap);
|
lp.addRule(RelativeLayout.BELOW, R.id.name);
|
||||||
username.getParent().requestLayout();
|
username.getParent().requestLayout();
|
||||||
avatar.setForeground(null);
|
avatar.setForeground(null);
|
||||||
scrollToTop();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
|
|
||||||
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
|
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1111,13 +974,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(!isInEditMode)
|
if(!isInEditMode)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
setActionProgressVisible(true);
|
setActionProgressVisible(true);
|
||||||
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, metadataListData)
|
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, aboutFragment.getFields())
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
account=result;
|
account=result;
|
||||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
||||||
if (getActivity() == null) return;
|
|
||||||
exitEditMode();
|
exitEditMode();
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1232,11 +1094,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
scrollView.smoothScrollTo(0, 0);
|
scrollView.smoothScrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onFollowersOrFollowingClick(View v){
|
private void onFollowersOrFollowingClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -1256,7 +1113,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=tabViews[viewType];
|
FrameLayout view=tabViews[viewType];
|
||||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
view.setVisibility(View.VISIBLE);
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
@@ -1290,244 +1147,4 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
public void setFields(ArrayList<AccountField> fields){
|
|
||||||
metadataListData=fields;
|
|
||||||
if (isInEditMode) {
|
|
||||||
isInEditMode=false;
|
|
||||||
dragHelper.attachToRecyclerView(null);
|
|
||||||
}
|
|
||||||
if (adapter != null) adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MetadataAdapter extends UsableRecyclerView.Adapter<BaseViewHolder> implements ImageLoaderRecyclerAdapter {
|
|
||||||
public MetadataAdapter(){
|
|
||||||
super(imgLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return switch(viewType){
|
|
||||||
case 0 -> new AboutViewHolder();
|
|
||||||
case 1 -> new EditableAboutViewHolder();
|
|
||||||
case 2 -> new AddRowViewHolder();
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+viewType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(BaseViewHolder holder, int position){
|
|
||||||
if(position<metadataListData.size()){
|
|
||||||
holder.bind(metadataListData.get(position));
|
|
||||||
}else{
|
|
||||||
holder.bind(null);
|
|
||||||
}
|
|
||||||
super.onBindViewHolder(holder, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount(){
|
|
||||||
if(isInEditMode){
|
|
||||||
int size=metadataListData.size();
|
|
||||||
if(size<MAX_FIELDS)
|
|
||||||
size++;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return metadataListData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position){
|
|
||||||
if(isInEditMode){
|
|
||||||
return position==metadataListData.size() ? 2 : 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getImageCountForItem(int position){
|
|
||||||
return isInEditMode || metadataListData.get(position).emojiRequests==null
|
|
||||||
? 0 : metadataListData.get(position).emojiRequests.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
|
||||||
return metadataListData.get(position).emojiRequests.get(image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class BaseViewHolder extends BindableViewHolder<AccountField> {
|
|
||||||
public BaseViewHolder(int layout){
|
|
||||||
super(getActivity(), layout, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
|
|
||||||
private TextView title;
|
|
||||||
private LinkedTextView value;
|
|
||||||
|
|
||||||
public AboutViewHolder(){
|
|
||||||
super(R.layout.item_profile_about);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
value=findViewById(R.id.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item){
|
|
||||||
title.setText(item.parsedName);
|
|
||||||
value.setText(item.parsedValue);
|
|
||||||
if(item.verifiedAt!=null){
|
|
||||||
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
|
|
||||||
value.setTextColor(textColor);
|
|
||||||
value.setLinkTextColor(textColor);
|
|
||||||
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_24_regular, getActivity().getTheme()).mutate();
|
|
||||||
check.setTint(textColor);
|
|
||||||
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
|
|
||||||
}else{
|
|
||||||
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
|
||||||
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
|
|
||||||
value.setCompoundDrawables(null, null, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setImage(int index, Drawable image){
|
|
||||||
CustomEmojiSpan span=index>=item.nameEmojis.length ? item.valueEmojis[index-item.nameEmojis.length] : item.nameEmojis[index];
|
|
||||||
span.setDrawable(image);
|
|
||||||
title.invalidate();
|
|
||||||
value.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearImage(int index){
|
|
||||||
setImage(index, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EditableAboutViewHolder extends BaseViewHolder {
|
|
||||||
private EditText title;
|
|
||||||
private boolean titleHasFocus, valueHasFocus;
|
|
||||||
private EditText value;
|
|
||||||
|
|
||||||
public EditableAboutViewHolder(){
|
|
||||||
super(R.layout.item_profile_about_editable);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
value=findViewById(R.id.value);
|
|
||||||
findViewById(R.id.dragger_thingy).setOnLongClickListener(v->{
|
|
||||||
dragHelper.startDrag(this);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
title.addTextChangedListener(new SimpleTextWatcher(e->item.name=e.toString()));
|
|
||||||
title.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
|
||||||
titleHasFocus = hasFocus;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
value.addTextChangedListener(new SimpleTextWatcher(e->item.value=e.toString()));
|
|
||||||
value.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
|
||||||
valueHasFocus = hasFocus;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
findViewById(R.id.remove_row_btn).setOnClickListener(this::onRemoveRowClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item){
|
|
||||||
title.setText(item.name);
|
|
||||||
value.setText(item.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onRemoveRowClick(View v){
|
|
||||||
if(titleHasFocus || valueHasFocus){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos=getAbsoluteAdapterPosition();
|
|
||||||
metadataListData.remove(pos);
|
|
||||||
adapter.notifyItemRemoved(pos);
|
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
|
||||||
BaseViewHolder vh=(BaseViewHolder) list.getChildViewHolder(list.getChildAt(i));
|
|
||||||
vh.rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AddRowViewHolder extends BaseViewHolder implements UsableRecyclerView.Clickable{
|
|
||||||
public AddRowViewHolder(){
|
|
||||||
super(R.layout.item_profile_about_add_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(){
|
|
||||||
metadataListData.add(new AccountField());
|
|
||||||
if(metadataListData.size()==MAX_FIELDS){ // replace this row with new row
|
|
||||||
adapter.notifyItemChanged(metadataListData.size()-1);
|
|
||||||
}else{
|
|
||||||
adapter.notifyItemInserted(metadataListData.size()-1);
|
|
||||||
rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReorderCallback extends ItemTouchHelper.SimpleCallback{
|
|
||||||
public ReorderCallback(){
|
|
||||||
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target){
|
|
||||||
if(target instanceof AddRowViewHolder)
|
|
||||||
return false;
|
|
||||||
int fromPosition=viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
int toPosition=target.getAbsoluteAdapterPosition();
|
|
||||||
if (fromPosition<toPosition) {
|
|
||||||
for (int i=fromPosition;i<toPosition;i++) {
|
|
||||||
Collections.swap(metadataListData, i, i+1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i=fromPosition;i>toPosition;i--) {
|
|
||||||
Collections.swap(metadataListData, i, i-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.notifyItemMoved(fromPosition, toPosition);
|
|
||||||
((BindableViewHolder)viewHolder).rebind();
|
|
||||||
((BindableViewHolder)target).rebind();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
|
||||||
super.onSelectedChanged(viewHolder, actionState);
|
|
||||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
|
||||||
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
|
||||||
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
|
||||||
draggedViewHolder=viewHolder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder){
|
|
||||||
super.clearView(recyclerView, viewHolder);
|
|
||||||
viewHolder.itemView.animate().translationZ(0).setDuration(100).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
|
||||||
draggedViewHolder=null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLongPressDragEnabled(){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class RecyclerFragment<T> extends BaseRecyclerFragment<T> {
|
|
||||||
public RecyclerFragment(int perPage) {
|
|
||||||
super(perPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecyclerFragment(int layout, int perPage) {
|
|
||||||
super(layout, perPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
if (refreshLayout != null) setRefreshLayoutColors(refreshLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setRefreshLayoutColors(SwipeRefreshLayout l) {
|
|
||||||
List<Integer> colors = new ArrayList<>(Arrays.asList(
|
|
||||||
R.color.primary_600,
|
|
||||||
R.color.red_primary_600,
|
|
||||||
R.color.green_primary_600,
|
|
||||||
R.color.blue_primary_600,
|
|
||||||
R.color.purple_600
|
|
||||||
));
|
|
||||||
int primary = UiUtils.getThemeColorRes(l.getContext(), R.attr.colorPrimary600);
|
|
||||||
if (!colors.contains(primary)) colors.add(0, primary);
|
|
||||||
int offset = colors.indexOf(primary);
|
|
||||||
int[] sorted = new int[colors.size()];
|
|
||||||
for (int i = 0; i < colors.size(); i++) {
|
|
||||||
sorted[i] = colors.get((i + offset) % colors.size());
|
|
||||||
}
|
|
||||||
l.setColorSchemeResources(sorted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
|||||||
import org.joinmastodon.android.api.requests.statuses.GetScheduledStatuses;
|
import org.joinmastodon.android.api.requests.statuses.GetScheduledStatuses;
|
||||||
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
|
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
|
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -29,11 +28,11 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
|
|
||||||
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
|
private ImageButton fab;
|
||||||
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
||||||
|
|
||||||
@Override
|
public ScheduledStatusListFragment() {
|
||||||
protected boolean withComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,31 +55,21 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFabClick(View v) {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onFabLongClick(View v) {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
|
fab.setOnClickListener(v -> Nav.go(getActivity(), ComposeFragment.class, args));
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, args));
|
||||||
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
||||||
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, Filter.FilterContext.HOME);
|
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,7 +109,6 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result, nextMaxID!=null);
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public interface ScrollableToTop{
|
public interface ScrollableToTop{
|
||||||
boolean isScrolledToTop();
|
|
||||||
|
|
||||||
void scrollToTop();
|
void scrollToTop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.LruCache;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -36,7 +34,6 @@ import android.widget.Toast;
|
|||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
import org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
||||||
@@ -46,13 +43,11 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||||
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
@@ -69,6 +64,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -82,7 +78,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private ThemeItem themeItem;
|
private ThemeItem themeItem;
|
||||||
private NotificationPolicyItem notificationPolicyItem;
|
private NotificationPolicyItem notificationPolicyItem;
|
||||||
private SwitchItem showNewPostsButtonItem, glitchModeItem, compactReblogReplyLineItem;
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean needUpdateNotificationSettings;
|
private boolean needUpdateNotificationSettings;
|
||||||
private boolean needAppRestart;
|
private boolean needAppRestart;
|
||||||
@@ -104,8 +99,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||||
String instanceName = UiUtils.getInstanceName(accountID);
|
String instanceName = UiUtils.getInstanceName(accountID);
|
||||||
|
|
||||||
DomainManager.getInstance().setCurrentDomain(session.domain + "/settings");
|
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
|
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
|
||||||
GithubSelfUpdater.UpdateState state=updater.getState();
|
GithubSelfUpdater.UpdateState state=updater.getState();
|
||||||
@@ -115,8 +108,21 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_theme));
|
items.add(new HeaderItem(R.string.settings_theme));
|
||||||
|
|
||||||
items.add(themeItem=new ThemeItem());
|
items.add(themeItem=new ThemeItem());
|
||||||
|
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
|
||||||
|
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||||
|
GlobalUserPreferences.disableMarquee=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
||||||
|
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
|
||||||
|
GlobalUserPreferences.reduceMotion=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
|
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
|
||||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||||
popupMenu.inflate(R.menu.color_palettes);
|
popupMenu.inflate(R.menu.color_palettes);
|
||||||
@@ -136,71 +142,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
case NORD -> R.string.mo_color_palette_nord;
|
case NORD -> R.string.mo_color_palette_nord;
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
|
|
||||||
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
|
||||||
GlobalUserPreferences.disableMarquee=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
|
||||||
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
|
|
||||||
GlobalUserPreferences.reduceMotion=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_behavior));
|
|
||||||
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
|
||||||
GlobalUserPreferences.playGifs=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.settings_custom_tabs, R.drawable.ic_fluent_link_24_regular, GlobalUserPreferences.useCustomTabs, i->{
|
|
||||||
GlobalUserPreferences.useCustomTabs=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
|
|
||||||
GlobalUserPreferences.showInteractionCounts=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
|
||||||
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
|
||||||
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
|
||||||
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{
|
|
||||||
GlobalUserPreferences.disableSwipe=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.mo_disable_double_tap_to_swipe_between_tabs, R.drawable.ic_fluent_double_tap_swipe_right_24_regular, GlobalUserPreferences.disableDoubleTapToSwipe, i->{
|
|
||||||
GlobalUserPreferences.disableDoubleTapToSwipe=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_confirm_before_reblog, R.drawable.ic_fluent_checkmark_circle_24_regular, GlobalUserPreferences.confirmBeforeReblog, i->{
|
|
||||||
GlobalUserPreferences.confirmBeforeReblog=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.mo_swap_bookmark_with_reblog, R.drawable.ic_boost, GlobalUserPreferences.swapBookmarkWithBoostAction, i -> {
|
|
||||||
GlobalUserPreferences.swapBookmarkWithBoostAction=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.mo_composer_behavior));
|
|
||||||
items.add(new ButtonItem(R.string.sk_settings_publish_button_text, R.drawable.ic_fluent_send_24_regular, b-> {
|
items.add(new ButtonItem(R.string.sk_settings_publish_button_text, R.drawable.ic_fluent_send_24_regular, b-> {
|
||||||
updatePublishText(b);
|
updatePublishText(b);
|
||||||
b.setOnClickListener(l -> {
|
if (GlobalUserPreferences.relocatePublishButton) {
|
||||||
if(!GlobalUserPreferences.relocatePublishButton) {
|
b.setOnClickListener(l -> {
|
||||||
|
Toast.makeText(getActivity(), R.string.mo_disable_relocate_publish_button_to_enable_customization,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
b.setOnClickListener(l -> {
|
||||||
FrameLayout inputWrap = new FrameLayout(getContext());
|
FrameLayout inputWrap = new FrameLayout(getContext());
|
||||||
EditText input = new EditText(getContext());
|
EditText input = new EditText(getContext());
|
||||||
input.setHint(R.string.publish);
|
input.setHint(R.string.publish);
|
||||||
@@ -223,147 +173,90 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
.setNegativeButton(R.string.cancel, (d, which) -> {
|
.setNegativeButton(R.string.cancel, (d, which) -> {
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
|
});}
|
||||||
} else {
|
|
||||||
Toast.makeText(getActivity(), R.string.mo_disable_relocate_publish_button_to_enable_customization,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
|
||||||
GlobalUserPreferences.relocatePublishButton=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.mo_change_default_reply_visibility_to_unlisted, R.drawable.ic_fluent_lock_open_24_regular, GlobalUserPreferences.defaultToUnlistedReplies, i->{
|
|
||||||
GlobalUserPreferences.defaultToUnlistedReplies=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.mo_disable_reminder_to_add_alt_text, R.drawable.ic_fluent_image_alt_text_24_regular, GlobalUserPreferences.disableAltTextReminder, i->{
|
|
||||||
GlobalUserPreferences.disableAltTextReminder=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_prefix_reply_cw_with_re, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.prefixRepliesWithRe, i->{
|
|
||||||
GlobalUserPreferences.prefixRepliesWithRe=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_timelines));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
|
||||||
GlobalUserPreferences.showReplies=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
if (instance.pleroma != null) {
|
|
||||||
items.add(new ButtonItem(R.string.sk_settings_reply_visibility, R.drawable.ic_fluent_chat_24_regular, b->{
|
|
||||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
|
||||||
popupMenu.inflate(R.menu.reply_visibility);
|
|
||||||
popupMenu.setOnMenuItemClickListener(item -> this.onReplyVisibilityChanged(item, b));
|
|
||||||
b.setOnTouchListener(popupMenu.getDragToOpenListener());
|
|
||||||
b.setOnClickListener(v->popupMenu.show());
|
|
||||||
b.setText(GlobalUserPreferences.replyVisibility == null ?
|
|
||||||
R.string.sk_settings_reply_visibility_all :
|
|
||||||
switch(GlobalUserPreferences.replyVisibility){
|
|
||||||
case "following" -> R.string.sk_settings_reply_visibility_following;
|
|
||||||
case "self" -> R.string.sk_settings_reply_visibility_self;
|
|
||||||
default -> R.string.sk_settings_reply_visibility_all;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
|
|
||||||
GlobalUserPreferences.showBoosts=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
|
||||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
|
||||||
showNewPostsButtonItem.enabled = i.checked;
|
|
||||||
if (!i.checked) {
|
|
||||||
GlobalUserPreferences.showNewPostsButton = false;
|
|
||||||
showNewPostsButtonItem.checked = false;
|
|
||||||
}
|
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsButtonItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(showNewPostsButtonItem = new SwitchItem(R.string.sk_settings_show_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
|
||||||
GlobalUserPreferences.showNewPostsButton=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_alt_indicator, R.drawable.ic_fluent_scan_text_24_regular, GlobalUserPreferences.showAltIndicator, i->{
|
items.add(new HeaderItem(R.string.settings_behavior));
|
||||||
GlobalUserPreferences.showAltIndicator=i.checked;
|
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
|
||||||
|
GlobalUserPreferences.showFederatedTimeline=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
|
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
||||||
GlobalUserPreferences.showNoAltIndicator=i.checked;
|
GlobalUserPreferences.playGifs=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.settings_custom_tabs, R.drawable.ic_fluent_link_24_regular, GlobalUserPreferences.useCustomTabs, i->{
|
||||||
|
GlobalUserPreferences.useCustomTabs=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
|
||||||
|
GlobalUserPreferences.showInteractionCounts=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
||||||
|
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{
|
||||||
|
GlobalUserPreferences.disableSwipe=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_collapse_long_posts, R.drawable.ic_fluent_chevron_down_24_regular, GlobalUserPreferences.collapseLongPosts, i->{
|
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
||||||
GlobalUserPreferences.collapseLongPosts=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
|
||||||
GlobalUserPreferences.spectatorMode=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
|
|
||||||
GlobalUserPreferences.autoHideFab=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_reply_line_above_avatar, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.replyLineAboveHeader, i->{
|
|
||||||
GlobalUserPreferences.replyLineAboveHeader=i.checked;
|
|
||||||
GlobalUserPreferences.compactReblogReplyLine=i.checked;
|
|
||||||
compactReblogReplyLineItem.enabled=i.checked;
|
|
||||||
compactReblogReplyLineItem.checked= GlobalUserPreferences.replyLineAboveHeader;
|
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(compactReblogReplyLineItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(compactReblogReplyLineItem=new SwitchItem(R.string.sk_compact_reblog_reply_line, R.drawable.ic_fluent_re_order_24_regular, GlobalUserPreferences.compactReblogReplyLine, i->{
|
|
||||||
GlobalUserPreferences.compactReblogReplyLine=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
compactReblogReplyLineItem.enabled=GlobalUserPreferences.replyLineAboveHeader;
|
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
|
||||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
|
||||||
// GlobalUserPreferences.save();
|
|
||||||
// needAppRestart=true;
|
|
||||||
// }));
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_notifications));
|
|
||||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
|
||||||
PushSubscription pushSubscription=getPushSubscription();
|
|
||||||
boolean switchEnabled=pushSubscription.policy!=PushSubscription.Policy.NONE;
|
|
||||||
|
|
||||||
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_chat_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.sk_notify_update, R.drawable.ic_fluent_history_24_regular, pushSubscription.alerts.update, i->onNotificationsChanged(PushNotification.Type.UPDATE, i.checked), switchEnabled));
|
|
||||||
items.add(new SwitchItem(R.string.sk_notify_poll_results, R.drawable.ic_fluent_poll_24_regular, pushSubscription.alerts.poll, i->onNotificationsChanged(PushNotification.Type.POLL, i.checked), switchEnabled));
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.mo_miscellaneous_settings));
|
|
||||||
items.add(new SwitchItem(R.string.mo_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
|
items.add(new SwitchItem(R.string.mo_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
|
||||||
GlobalUserPreferences.disableDividers=i.checked;
|
GlobalUserPreferences.disableDividers=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
|
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
||||||
GlobalUserPreferences.enableDeleteNotifications=i.checked;
|
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
|
// items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_delete_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
|
||||||
|
// GlobalUserPreferences.enableDeleteNotifications=i.checked;
|
||||||
|
// GlobalUserPreferences.save();
|
||||||
|
// needAppRestart=true;
|
||||||
|
// }));
|
||||||
|
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
||||||
|
GlobalUserPreferences.relocatePublishButton=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
// items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
|
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
|
// GlobalUserPreferences.save();
|
||||||
|
// needAppRestart=true;
|
||||||
|
// }));
|
||||||
|
|
||||||
|
items.add(new HeaderItem(R.string.home_timeline));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||||
|
GlobalUserPreferences.showReplies=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
|
||||||
|
GlobalUserPreferences.showBoosts=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||||
|
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add(new HeaderItem(R.string.settings_notifications));
|
||||||
|
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||||
|
PushSubscription pushSubscription=getPushSubscription();
|
||||||
|
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked)));
|
||||||
|
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked)));
|
||||||
|
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked)));
|
||||||
|
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
|
||||||
|
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_alert_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked)));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
||||||
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_account));
|
items.add(new HeaderItem(R.string.settings_account));
|
||||||
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.sk_settings_posting, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/preferences/other"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.sk_settings_posting, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/preferences/other"), R.drawable.ic_fluent_open_24_regular));
|
||||||
@@ -380,35 +273,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
||||||
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_instance_features));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
|
|
||||||
glitchModeItem.enabled = i.checked;
|
|
||||||
if (i.checked) {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
|
||||||
if (instance.pleroma == null) GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
} else {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.remove(accountID);
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
|
||||||
}
|
|
||||||
glitchModeItem.checked = GlobalUserPreferences.accountsInGlitchMode.contains(accountID);
|
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(glitchModeItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SmallTextItem(getString(R.string.sk_settings_local_only_explanation)));
|
|
||||||
items.add(glitchModeItem = new SwitchItem(R.string.sk_settings_glitch_instance, 0, GlobalUserPreferences.accountsInGlitchMode.contains(accountID), i->{
|
|
||||||
if (i.checked) {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
} else {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
|
||||||
}
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
glitchModeItem.enabled = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
|
||||||
items.add(new SmallTextItem(getString(R.string.sk_settings_glitch_mode_explanation)));
|
|
||||||
|
|
||||||
|
|
||||||
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
||||||
items.add(new SmallTextItem(getString(translationAvailable ?
|
items.add(new SmallTextItem(getString(translationAvailable ?
|
||||||
R.string.sk_settings_translation_availability_note_available :
|
R.string.sk_settings_translation_availability_note_available :
|
||||||
@@ -416,50 +280,27 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_settings_about));
|
items.add(new HeaderItem(R.string.sk_settings_about));
|
||||||
|
// items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon"), R.drawable.ic_fluent_open_24_regular));
|
||||||
// if(BuildConfig.BUILD_TYPE.equals("nightly")){
|
// items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://ko-fi.com/xsk22"), R.drawable.ic_fluent_heart_24_regular));
|
||||||
// items.add(new TextItem(R.string.mo_download_latest_nightly_release, ()->UiUtils.launchWebBrowser(getActivity(), "https://nightly.link/LucasGGamerM/moshidon/workflows/nightly-builds/master/moshidon-nightly.apk.zip"), R.drawable.ic_fluent_open_24_regular));
|
|
||||||
// }
|
|
||||||
|
|
||||||
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
|
|
||||||
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
|
|
||||||
|
|
||||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||||
items.add(checkForUpdateItem);
|
items.add(checkForUpdateItem);
|
||||||
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
|
|
||||||
GlobalUserPreferences.enablePreReleases=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
|
||||||
LruCache<?, ?> cache = imageCache == null ? null : imageCache.getLruCache();
|
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
|
||||||
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), cache != null ? cache.size() : 0, true), this::clearImageCache, 0);
|
// items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
|
||||||
|
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), imageCache.getDiskCache().size(), true), this::clearImageCache, 0);
|
||||||
items.add(clearImageCacheItem);
|
items.add(clearImageCacheItem);
|
||||||
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
||||||
GlobalUserPreferences.recentLanguages.remove(accountID);
|
GlobalUserPreferences.recentLanguages.remove(accountID);
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
})));
|
})));
|
||||||
|
|
||||||
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
|
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
|
||||||
GlobalUserPreferences.recentEmojis.clear();
|
GlobalUserPreferences.recentEmojis.clear();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||||
|
|
||||||
if(BuildConfig.DEBUG){
|
|
||||||
items.add(new RedHeaderItem("Debug options"));
|
|
||||||
items.add(new TextItem("Test e-mail confirmation flow", ()->{
|
|
||||||
AccountSession sess=AccountSessionManager.getInstance().getAccount(accountID);
|
|
||||||
sess.activated=false;
|
|
||||||
sess.activationInfo=new AccountActivationInfo("test@email", System.currentTimeMillis());
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putBoolean("debug", true);
|
|
||||||
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +354,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
||||||
}
|
}
|
||||||
if(needAppRestart) UiUtils.restartApp();
|
if(needAppRestart){
|
||||||
|
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
|
||||||
|
MastodonApp.context.startActivity(intent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -575,25 +420,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onReplyVisibilityChanged(MenuItem item, Button btn){
|
|
||||||
String pref = null;
|
|
||||||
int id = item.getItemId();
|
|
||||||
|
|
||||||
if (id == R.id.reply_visibility_following) pref = "following";
|
|
||||||
else if (id == R.id.reply_visibility_self) pref = "self";
|
|
||||||
|
|
||||||
GlobalUserPreferences.replyVisibility=pref;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
btn.setText(GlobalUserPreferences.replyVisibility == null ?
|
|
||||||
R.string.sk_settings_reply_visibility_all :
|
|
||||||
switch(GlobalUserPreferences.replyVisibility){
|
|
||||||
case "following" -> R.string.sk_settings_reply_visibility_following;
|
|
||||||
case "self" -> R.string.sk_settings_reply_visibility_self;
|
|
||||||
default -> R.string.sk_settings_reply_visibility_all;
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restartActivityToApplyNewTheme(){
|
private void restartActivityToApplyNewTheme(){
|
||||||
// Calling activity.recreate() causes a black screen for like half a second.
|
// Calling activity.recreate() causes a black screen for like half a second.
|
||||||
// So, let's take a screenshot and overlay it on top to create the illusion of a smoother transition.
|
// So, let's take a screenshot and overlay it on top to create the illusion of a smoother transition.
|
||||||
@@ -636,10 +462,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
case FAVORITE -> subscription.alerts.favourite=enabled;
|
case FAVORITE -> subscription.alerts.favourite=enabled;
|
||||||
case FOLLOW -> subscription.alerts.follow=enabled;
|
case FOLLOW -> subscription.alerts.follow=enabled;
|
||||||
case REBLOG -> subscription.alerts.reblog=enabled;
|
case REBLOG -> subscription.alerts.reblog=enabled;
|
||||||
case MENTION -> subscription.alerts.mention=enabled;
|
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
||||||
case POLL -> subscription.alerts.poll=enabled;
|
|
||||||
case STATUS -> subscription.alerts.status=enabled;
|
case STATUS -> subscription.alerts.status=enabled;
|
||||||
case UPDATE -> subscription.alerts.update=enabled;
|
|
||||||
}
|
}
|
||||||
needUpdateNotificationSettings=true;
|
needUpdateNotificationSettings=true;
|
||||||
}
|
}
|
||||||
@@ -664,13 +488,9 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
list.getAdapter().notifyItemChanged(index);
|
list.getAdapter().notifyItemChanged(index);
|
||||||
}
|
}
|
||||||
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
||||||
boolean newState=policy!=PushSubscription.Policy.NONE;
|
|
||||||
for(PushNotification.Type value : PushNotification.Type.values()){
|
|
||||||
onNotificationsChanged(value, newState);
|
|
||||||
}
|
|
||||||
index++;
|
index++;
|
||||||
while(items.get(index) instanceof SwitchItem si){
|
while(items.get(index) instanceof SwitchItem si){
|
||||||
si.enabled=si.checked=newState;
|
si.enabled=si.checked=policy!=PushSubscription.Policy.NONE;
|
||||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
((BindableViewHolder<?>)holder).rebind();
|
((BindableViewHolder<?>)holder).rebind();
|
||||||
@@ -710,7 +530,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onLoggedOut(){
|
private void onLoggedOut(){
|
||||||
if (getActivity() == null) return;
|
|
||||||
AccountSessionManager.getInstance().removeAccount(accountID);
|
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
@@ -764,7 +583,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
this.text=getString(text);
|
this.text=getString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderItem(String text){
|
public HeaderItem(String text) {
|
||||||
this.text=text;
|
this.text=text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,7 +607,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
this.onChanged=onChanged;
|
this.onChanged=onChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchItem(@StringRes int text, @DrawableRes int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
public SwitchItem(@StringRes int text, int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
||||||
this.text=getString(text);
|
this.text=getString(text);
|
||||||
this.icon=icon;
|
this.icon=icon;
|
||||||
this.checked=checked;
|
this.checked=checked;
|
||||||
@@ -882,11 +701,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
this.secondaryText = secondaryText;
|
this.secondaryText = secondaryText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextItem(String text, Runnable onClick){
|
|
||||||
this.text=text;
|
|
||||||
this.onClick=onClick;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 4;
|
return 4;
|
||||||
@@ -899,10 +713,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
super(text);
|
super(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedHeaderItem(String text){
|
|
||||||
super(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 5;
|
return 5;
|
||||||
@@ -996,12 +806,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(SwitchItem item){
|
public void onBind(SwitchItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
if (item.icon == 0) {
|
icon.setImageResource(item.icon);
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
icon.setVisibility(View.VISIBLE);
|
|
||||||
icon.setImageResource(item.icon);
|
|
||||||
}
|
|
||||||
checkbox.setChecked(item.checked && item.enabled);
|
checkbox.setChecked(item.checked && item.enabled);
|
||||||
checkbox.setEnabled(item.enabled);
|
checkbox.setEnabled(item.enabled);
|
||||||
}
|
}
|
||||||
@@ -1176,19 +981,21 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
|
|
||||||
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
|
;
|
||||||
|
|
||||||
public SmallTextViewHolder(){
|
public SmallTextViewHolder(){
|
||||||
super(getActivity(), R.layout.item_settings_text, list);
|
super(getActivity(), R.layout.item_settings_text, list);
|
||||||
text = itemView.findViewById(R.id.text);
|
text = itemView.findViewById(R.id.text);
|
||||||
text.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary));
|
|
||||||
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
|
||||||
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
text.setPaddingRelative(text.getPaddingStart(), 0, text.getPaddingEnd(), text.getPaddingBottom());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(SmallTextItem item){
|
public void onBind(SmallTextItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
|
TypedValue val = new TypedValue();
|
||||||
|
getContext().getTheme().resolveAttribute(android.R.attr.textColorSecondary, val, true);
|
||||||
|
text.setTextColor(getResources().getColor(val.resourceId, getContext().getTheme()));
|
||||||
|
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
|
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,7 +173,16 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
TextView title=new TextView(getActivity());
|
TextView title=new TextView(getActivity());
|
||||||
title.setTextAppearance(R.style.m3_headline_medium);
|
title.setTextAppearance(R.style.m3_headline_medium);
|
||||||
title.setText(switch(page){
|
title.setText(switch(page){
|
||||||
case 0 -> getString(R.string.welcome_page1_title);
|
case 0 -> {
|
||||||
|
String src=getString(R.string.welcome_page1_title);
|
||||||
|
SpannableString ss=new SpannableString(src);
|
||||||
|
int start=src.indexOf("{logo}");
|
||||||
|
if(start!=-1){
|
||||||
|
LogoSpan span=new LogoSpan(getResources().getDrawable(R.drawable.splash_logo, getActivity().getTheme()));
|
||||||
|
ss.setSpan(span, start, start+6, 0);
|
||||||
|
}
|
||||||
|
yield ss;
|
||||||
|
}
|
||||||
case 1 -> getString(R.string.welcome_page2_title);
|
case 1 -> getString(R.string.welcome_page2_title);
|
||||||
case 2 -> getString(R.string.welcome_page3_title);
|
case 2 -> getString(R.string.welcome_page3_title);
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
@@ -195,4 +204,26 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class LogoSpan extends ReplacementSpan{
|
||||||
|
private final Drawable drawable;
|
||||||
|
|
||||||
|
private LogoSpan(Drawable drawable){
|
||||||
|
this.drawable=drawable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
|
||||||
|
return drawable.getIntrinsicWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint){
|
||||||
|
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(x, y-V.dp(20));
|
||||||
|
drawable.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
@@ -47,7 +46,6 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -56,7 +54,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||||
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, Filter.FilterContext.HOME);
|
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null);
|
||||||
int idx=data.indexOf(s);
|
int idx=data.indexOf(s);
|
||||||
if(idx>=0){
|
if(idx>=0){
|
||||||
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
||||||
@@ -141,8 +139,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
action=getString(R.string.edit_multiple_changed);
|
action=getString(R.string.edit_multiple_changed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String sep = getString(R.string.sk_separator);
|
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" · "+date, Collections.emptyList(), 0, null, null));
|
||||||
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null));
|
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,16 @@ import android.os.Bundle;
|
|||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.MainActivity;
|
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusDeletedEvent;
|
import org.joinmastodon.android.events.StatusDeletedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,21 +26,17 @@ import java.util.stream.Stream;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
|
||||||
public abstract class StatusListFragment extends BaseStatusListFragment<Status> implements DomainDisplay{
|
public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
||||||
protected EventListener eventListener=new EventListener();
|
protected EventListener eventListener=new EventListener();
|
||||||
|
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||||
boolean addFooter = !GlobalUserPreferences.spectatorMode ||
|
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, true, null);
|
||||||
(this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id));
|
|
||||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, Filter.FilterContext.HOME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addAccountToKnown(Status s){
|
protected void addAccountToKnown(Status s){
|
||||||
if(!knownAccounts.containsKey(s.account.id))
|
if(!knownAccounts.containsKey(s.account.id))
|
||||||
knownAccounts.put(s.account.id, s.account);
|
knownAccounts.put(s.account.id, s.account);
|
||||||
if(s.reblog!=null && !knownAccounts.containsKey(s.reblog.account.id))
|
|
||||||
knownAccounts.put(s.reblog.account.id, s.reblog.account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,19 +56,6 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
|||||||
Status status=getContentStatusByID(id);
|
Status status=getContentStatusByID(id);
|
||||||
if(status==null)
|
if(status==null)
|
||||||
return;
|
return;
|
||||||
if(status.reloadWhenClicked){
|
|
||||||
UiUtils.lookupStatus(getContext(), status, accountID, null, status1 -> {
|
|
||||||
status1.filterRevealed = true;
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putParcelable("status", Parcels.wrap(status1));
|
|
||||||
if(status1.inReplyToAccountId!=null && knownAccounts.containsKey(status1.inReplyToAccountId))
|
|
||||||
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status1.inReplyToAccountId)));
|
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
status.filterRevealed = true;
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("status", Parcels.wrap(status));
|
args.putParcelable("status", Parcels.wrap(status));
|
||||||
@@ -165,7 +144,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
|||||||
|
|
||||||
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
||||||
List<Status> toRemove=Stream.concat(data.stream(), preloadedData.stream())
|
List<Status> toRemove=Stream.concat(data.stream(), preloadedData.stream())
|
||||||
.filter(s->s.account.id.equals(ev.postsByAccountID) || (!ev.isUnfollow && s.reblog!=null && s.reblog.account.id.equals(ev.postsByAccountID)))
|
.filter(s->s.account.id.equals(ev.postsByAccountID) || (s.reblog!=null && s.reblog.account.id.equals(ev.postsByAccountID)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
for(Status s:toRemove){
|
for(Status s:toRemove){
|
||||||
removeStatus(s);
|
removeStatus(s);
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ package org.joinmastodon.android.fragments;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusContext;
|
import org.joinmastodon.android.model.StatusContext;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
@@ -23,20 +19,14 @@ import org.joinmastodon.android.ui.utils.UiUtils;
|
|||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class ThreadFragment extends StatusListFragment implements DomainDisplay{
|
public class ThreadFragment extends StatusListFragment{
|
||||||
protected Status mainStatus;
|
private Status mainStatus;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return mainStatus.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -48,8 +38,6 @@ public class ThreadFragment extends StatusListFragment implements DomainDisplay{
|
|||||||
data.add(mainStatus);
|
data.add(mainStatus);
|
||||||
onAppendItems(Collections.singletonList(mainStatus));
|
onAppendItems(Collections.singletonList(mainStatus));
|
||||||
setTitle(HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.displayName), mainStatus.account.emojis));
|
setTitle(HtmlParser.parseCustomEmoji(getString(R.string.post_from_user, mainStatus.account.displayName), mainStatus.account.emojis));
|
||||||
|
|
||||||
DomainManager.getInstance().setCurrentDomain(getDomain());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,37 +61,14 @@ public class ThreadFragment extends StatusListFragment implements DomainDisplay{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(StatusContext result){
|
public void onSuccess(StatusContext result){
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
if(refreshing){
|
if(refreshing){
|
||||||
data.clear();
|
data.clear();
|
||||||
displayItems.clear();
|
displayItems.clear();
|
||||||
data.add(mainStatus);
|
data.add(mainStatus);
|
||||||
onAppendItems(Collections.singletonList(mainStatus));
|
onAppendItems(Collections.singletonList(mainStatus));
|
||||||
}
|
}
|
||||||
AccountSession account=AccountSessionManager.getInstance().getAccount(accountID);
|
|
||||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
|
||||||
if(instance.pleroma != null){
|
|
||||||
List<String> threadIds=new ArrayList<>();
|
|
||||||
threadIds.add(mainStatus.id);
|
|
||||||
for(Status s:result.descendants){
|
|
||||||
if(threadIds.contains(s.inReplyToId)){
|
|
||||||
threadIds.add(s.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
threadIds.add(mainStatus.inReplyToId);
|
|
||||||
for(int i=result.ancestors.size()-1; i >= 0; i--){
|
|
||||||
Status s=result.ancestors.get(i);
|
|
||||||
if(s.inReplyToId != null && threadIds.contains(s.id)){
|
|
||||||
threadIds.add(s.inReplyToId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.ancestors=result.ancestors.stream().filter(s -> threadIds.contains(s.id)).collect(Collectors.toList());
|
|
||||||
result.descendants=getDescendantsOrdered(mainStatus.id,
|
|
||||||
result.descendants.stream()
|
|
||||||
.filter(s -> threadIds.contains(s.id))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
result.descendants=filterStatuses(result.descendants);
|
result.descendants=filterStatuses(result.descendants);
|
||||||
result.ancestors=filterStatuses(result.ancestors);
|
result.ancestors=filterStatuses(result.ancestors);
|
||||||
if(footerProgress!=null)
|
if(footerProgress!=null)
|
||||||
@@ -126,24 +91,6 @@ public class ThreadFragment extends StatusListFragment implements DomainDisplay{
|
|||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Status> getDescendantsOrdered(String id, List<Status> statuses){
|
|
||||||
List<Status> out=new ArrayList<>();
|
|
||||||
for(Status s:getDirectDescendants(id, statuses)){
|
|
||||||
out.add(s);
|
|
||||||
getDirectDescendants(s.id, statuses).forEach(d ->{
|
|
||||||
out.add(d);
|
|
||||||
out.addAll(getDescendantsOrdered(d.id, statuses));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Status> getDirectDescendants(String id, List<Status> statuses){
|
|
||||||
return statuses.stream()
|
|
||||||
.filter(s -> s.inReplyToId.equals(id))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Status> filterStatuses(List<Status> statuses){
|
private List<Status> filterStatuses(List<Status> statuses){
|
||||||
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD);
|
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD);
|
||||||
return statuses.stream()
|
return statuses.stream()
|
||||||
@@ -179,14 +126,4 @@ public class ThreadFragment extends StatusListFragment implements DomainDisplay{
|
|||||||
public boolean isItemEnabled(String id){
|
public boolean isItemEnabled(String id){
|
||||||
return !id.equals(mainStatus.id);
|
return !id.equals(mainStatus.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean wantsLightStatusBar(){
|
|
||||||
return !UiUtils.isDarkTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean wantsLightNavigationBar(){
|
|
||||||
return !UiUtils.isDarkTheme();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
|||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
|
||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
@@ -49,6 +48,7 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -57,7 +57,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccountListFragment.AccountItem> {
|
public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseAccountListFragment.AccountItem>{
|
||||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||||
protected String accountID;
|
protected String accountID;
|
||||||
protected ArrayList<APIRequest<?>> relationshipsRequests=new ArrayList<>();
|
protected ArrayList<APIRequest<?>> relationshipsRequests=new ArrayList<>();
|
||||||
@@ -74,8 +74,6 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDataLoaded(List<AccountItem> d, boolean more){
|
protected void onDataLoaded(List<AccountItem> d, boolean more){
|
||||||
if (getActivity() == null)
|
|
||||||
return;
|
|
||||||
if(refreshing){
|
if(refreshing){
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
}
|
}
|
||||||
@@ -103,7 +101,6 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
for(Relationship rel:result){
|
for(Relationship rel:result){
|
||||||
relationships.put(rel.id, rel);
|
relationships.put(rel.id, rel);
|
||||||
}
|
}
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
@@ -131,8 +128,7 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
// list.setPadding(0, V.dp(16), 0, V.dp(16));
|
// list.setPadding(0, V.dp(16), 0, V.dp(16));
|
||||||
list.setClipToPadding(false);
|
list.setClipToPadding(false);
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1,
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 72, 16));
|
||||||
Math.round(16f + 56f * getResources().getConfiguration().fontScale), 16));
|
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,15 +265,6 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
if(item.account.reloadWhenClicked){
|
|
||||||
UiUtils.lookupAccount(getContext(), item.account, accountID, null, account -> {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(account));
|
|
||||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
||||||
@@ -306,6 +293,7 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
|
|
||||||
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
|
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
|
||||||
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
|
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
|
||||||
|
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername())).setVisible(relationship.following);
|
||||||
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
|
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
|
||||||
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
||||||
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||||
@@ -319,7 +307,7 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
manageUserLists.setVisible(true);
|
manageUserLists.setVisible(true);
|
||||||
}else{
|
}else{
|
||||||
hideBoosts.setVisible(false);
|
hideBoosts.setVisible(false);
|
||||||
manageUserLists.setVisible(false);
|
manageUserLists.setVisible(true);
|
||||||
}
|
}
|
||||||
menu.findItem(R.id.block_domain).setVisible(false);
|
menu.findItem(R.id.block_domain).setVisible(false);
|
||||||
|
|
||||||
@@ -382,7 +370,6 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Relationship result){
|
public void onSuccess(Relationship result){
|
||||||
relationships.put(AccountViewHolder.this.item.account.id, result);
|
relationships.put(AccountViewHolder.this.item.account.id, result);
|
||||||
if (getActivity() == null) return;
|
|
||||||
bindRelationship();
|
bindRelationship();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class FollowerListFragment extends AccountRelatedAccountListFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
targetAccount = account;
|
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, (int)(account.followersCount%1000), account.followersCount));
|
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, (int)(account.followersCount%1000), account.followersCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,9 +19,4 @@ public class FollowerListFragment extends AccountRelatedAccountListFragment{
|
|||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetAccountFollowers(account.id, maxID, count);
|
return new GetAccountFollowers(account.id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count){
|
|
||||||
return new GetAccountFollowers(id, maxID, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class FollowingListFragment extends AccountRelatedAccountListFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
targetAccount = account;
|
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount));
|
setSubtitle(getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,9 +19,4 @@ public class FollowingListFragment extends AccountRelatedAccountListFragment{
|
|||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetAccountFollowing(account.id, maxID, count);
|
return new GetAccountFollowing(account.id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count){
|
|
||||||
return new GetAccountFollowing(id, maxID, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,20 @@
|
|||||||
package org.joinmastodon.android.fragments.account_list;
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{
|
public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
|
|
||||||
protected Account targetAccount;
|
|
||||||
|
|
||||||
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
||||||
|
|
||||||
public abstract HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
if (shouldLoadRemote()) {
|
|
||||||
UiUtils.lookupRemoteAccount(getContext(), targetAccount, accountID, null, account -> {
|
|
||||||
if(account != null){
|
|
||||||
loadRemoteFollower(offset, count, account);
|
|
||||||
} else {
|
|
||||||
loadFollower(offset, count);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
loadFollower(offset, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldLoadRemote() {
|
|
||||||
if (!GlobalUserPreferences.loadRemoteAccountFollowers && (this instanceof FollowingListFragment || this instanceof FollowerListFragment)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return targetAccount != null && targetAccount.getDomain() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadFollower(int offset, int count) {
|
|
||||||
currentRequest=onCreateRequest(offset==0 ? null : nextMaxID, count)
|
currentRequest=onCreateRequest(offset==0 ? null : nextMaxID, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
@@ -61,36 +29,6 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
|
|||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRemoteFollower(int offset, int count, Account account) {
|
|
||||||
String ownDomain = AccountSessionManager.getInstance().getLastActiveAccount().domain;
|
|
||||||
currentRequest=onCreateRemoteRequest(account.id, offset==0 ? null : nextMaxID, count)
|
|
||||||
.setCallback(new Callback<>(){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
|
||||||
if(result.nextPageUri!=null)
|
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
|
||||||
else
|
|
||||||
nextMaxID=null;
|
|
||||||
result.stream().forEach(remoteAccount -> {
|
|
||||||
remoteAccount.reloadWhenClicked = true;
|
|
||||||
if (remoteAccount.getDomain() == null) {
|
|
||||||
remoteAccount.acct += "@" + Uri.parse(remoteAccount.url).getHost();
|
|
||||||
} else if (remoteAccount.getDomain().equals(ownDomain)) {
|
|
||||||
remoteAccount.acct = remoteAccount.username;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
loadFollower(offset, count);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.execNoAuth(targetAccount.getDomain());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume(){
|
public void onResume(){
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|||||||
@@ -18,9 +18,4 @@ public class StatusFavoritesListFragment extends StatusRelatedAccountListFragmen
|
|||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetStatusFavorites(status.id, maxID, count);
|
return new GetStatusFavorites(status.id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,4 @@ public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{
|
|||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetStatusReblogs(status.id, maxID, count);
|
return new GetStatusReblogs(status.id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ import android.widget.TextView;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||||
import org.joinmastodon.android.fragments.DomainDisplay;
|
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.FollowSuggestion;
|
import org.joinmastodon.android.model.FollowSuggestion;
|
||||||
@@ -51,7 +48,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccountsFragment.AccountWrapper> implements ScrollableToTop, IsOnTop, DomainDisplay {
|
public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccountsFragment.AccountWrapper> implements ScrollableToTop{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||||
private GetAccountRelationships relationshipsRequest;
|
private GetAccountRelationships relationshipsRequest;
|
||||||
@@ -60,11 +57,6 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
|||||||
super(20);
|
super(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return DomainDisplay.super.getDomain() + "/explore/suggestions";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -81,7 +73,6 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<FollowSuggestion> result){
|
public void onSuccess(List<FollowSuggestion> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
||||||
loadRelationships();
|
loadRelationships();
|
||||||
}
|
}
|
||||||
@@ -116,7 +107,6 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
|||||||
public void onSuccess(List<Relationship> result){
|
public void onSuccess(List<Relationship> result){
|
||||||
relationshipsRequest=null;
|
relationshipsRequest=null;
|
||||||
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
@@ -147,16 +137,6 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOnTop() {
|
|
||||||
return isRecyclerViewOnTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public AccountsAdapter(){
|
public AccountsAdapter(){
|
||||||
|
|||||||
@@ -19,10 +19,8 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.DomainManager;
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.DomainDisplay;
|
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
@@ -40,7 +38,7 @@ import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
|||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener, DomainDisplay {
|
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener{
|
||||||
|
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
@@ -64,18 +62,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
private String accountID;
|
private String accountID;
|
||||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||||
|
|
||||||
// private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
if (searchActive) {
|
|
||||||
return searchFragment.getDomain();
|
|
||||||
}
|
|
||||||
if (tabViews[tabLayout.getSelectedTabPosition()] instanceof DomainDisplay page) {
|
|
||||||
return page.getDomain();
|
|
||||||
}
|
|
||||||
return DomainDisplay.super.getDomain() + "/explore";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -140,9 +127,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
if(!page.loaded && !page.isDataLoading())
|
if(!page.loaded && !page.isDataLoading())
|
||||||
page.loadData();
|
page.loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_page instanceof DomainDisplay display)
|
|
||||||
DomainManager.getInstance().setCurrentDomain(display.getDomain());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -224,9 +208,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
tabLayoutMediator.attach();
|
tabLayoutMediator.attach();
|
||||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
||||||
@Override
|
@Override
|
||||||
public void onTabSelected(TabLayout.Tab tab){
|
public void onTabSelected(TabLayout.Tab tab){}
|
||||||
DomainManager.getInstance().setCurrentDomain(getDomain());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabUnselected(TabLayout.Tab tab){}
|
public void onTabUnselected(TabLayout.Tab tab){}
|
||||||
@@ -302,15 +284,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
if(!searchActive){
|
|
||||||
return ((ScrollableToTop)getFragmentForPage(pager.getCurrentItem())).isScrolledToTop();
|
|
||||||
}else{
|
|
||||||
return searchFragment.isScrolledToTop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadData(){
|
public void loadData(){
|
||||||
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
|
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
|
||||||
hashtagsFragment.loadData();
|
hashtagsFragment.loadData();
|
||||||
@@ -351,7 +324,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
// if (noFederated && page > 0) page++;
|
if (noFederated && page > 0) page++;
|
||||||
|
|
||||||
// return switch(page){
|
// return switch(page){
|
||||||
// case 0 -> localTimelineFragment;
|
// case 0 -> localTimelineFragment;
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.trends.GetTrendingLinks;
|
import org.joinmastodon.android.api.requests.trends.GetTrendingLinks;
|
||||||
import org.joinmastodon.android.fragments.DomainDisplay;
|
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
|
||||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.model.Card;
|
import org.joinmastodon.android.model.Card;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
@@ -37,7 +34,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements ScrollableToTop, IsOnTop, DomainDisplay {
|
public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements ScrollableToTop{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private List<ImageLoaderRequest> imageRequests=Collections.emptyList();
|
private List<ImageLoaderRequest> imageRequests=Collections.emptyList();
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_LINKS);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_LINKS);
|
||||||
@@ -46,11 +43,6 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements
|
|||||||
super(10);
|
super(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return DomainDisplay.super.getDomain() + "/explore/links";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -66,7 +58,6 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements
|
|||||||
imageRequests=result.stream()
|
imageRequests=result.stream()
|
||||||
.map(card->TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(150), V.dp(150)))
|
.map(card->TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(150), V.dp(150)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -90,16 +81,6 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOnTop() {
|
|
||||||
return isRecyclerViewOnTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LinksAdapter extends UsableRecyclerView.Adapter<LinkViewHolder> implements ImageLoaderRecyclerAdapter{
|
private class LinksAdapter extends UsableRecyclerView.Adapter<LinkViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
public LinksAdapter(){
|
public LinksAdapter(){
|
||||||
super(imgLoader);
|
super(imgLoader);
|
||||||
|
|||||||
@@ -4,34 +4,23 @@ import android.os.Bundle;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop {
|
public class DiscoverPostsFragment extends StatusListFragment{
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_POSTS);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_POSTS);
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/explore/posts";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetTrendingStatuses(offset, count)
|
currentRequest=new GetTrendingStatuses(offset, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
@@ -42,9 +31,4 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop
|
|||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
bannerHelper.maybeAddBanner(contentWrap);
|
bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOnTop() {
|
|
||||||
return isRecyclerViewOnTop(list);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
|
import org.joinmastodon.android.fragments.FabStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -16,21 +17,10 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class FederatedTimelineFragment extends StatusListFragment {
|
public class FederatedTimelineFragment extends FabStatusListFragment {
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.FEDERATED_TIMELINE);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.FEDERATED_TIMELINE);
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/public";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count)
|
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count)
|
||||||
@@ -39,9 +29,7 @@ public class FederatedTimelineFragment extends StatusListFragment {
|
|||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
if (getActivity() == null) return;
|
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package org.joinmastodon.android.fragments.discover;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
|
import org.joinmastodon.android.fragments.FabStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -15,20 +17,10 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class LocalTimelineFragment extends StatusListFragment {
|
public class LocalTimelineFragment extends FabStatusListFragment {
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
|
// private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean withComposeButton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/public/local";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
||||||
@@ -37,9 +29,7 @@ public class LocalTimelineFragment extends StatusListFragment {
|
|||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
if (getActivity() == null) return;
|
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
@@ -48,6 +38,6 @@ public class LocalTimelineFragment extends StatusListFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
bannerHelper.maybeAddBanner(contentWrap);
|
// bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
|||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.SearchResult;
|
import org.joinmastodon.android.model.SearchResult;
|
||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
@@ -57,18 +56,12 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
setLayout(R.layout.fragment_search);
|
setLayout(R.layout.fragment_search);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return super.getDomain() + "/search";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
loadData();
|
loadData();
|
||||||
resetEmptyText();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,16 +70,12 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
imm=activity.getSystemService(InputMethodManager.class);
|
imm=activity.getSystemService(InputMethodManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetEmptyText() {
|
|
||||||
setEmptyText(R.string.sk_recent_searches_placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
|
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
|
||||||
return switch(s.type){
|
return switch(s.type){
|
||||||
case ACCOUNT -> Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account));
|
case ACCOUNT -> Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account));
|
||||||
case HASHTAG -> Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag));
|
case HASHTAG -> Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag));
|
||||||
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true, null, Filter.FilterContext.PUBLIC);
|
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true, null);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +119,6 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
if (getActivity() == null) return;
|
|
||||||
resetEmptyText();
|
|
||||||
if(isInRecentMode()){
|
if(isInRecentMode()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().getRecentSearches(sr->{
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().getRecentSearches(sr->{
|
||||||
if(getActivity()==null)
|
if(getActivity()==null)
|
||||||
@@ -141,13 +128,11 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
onDataLoaded(sr, false);
|
onDataLoaded(sr, false);
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
setEmptyText(R.string.sk_searching);
|
|
||||||
progressVisibilityListener.onProgressVisibilityChanged(true);
|
progressVisibilityListener.onProgressVisibilityChanged(true);
|
||||||
currentRequest=new GetSearchResults(currentQuery, null, true)
|
currentRequest=new GetSearchResults(currentQuery, null, true)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
setEmptyText(R.string.sk_no_results);
|
|
||||||
ArrayList<SearchResult> results=new ArrayList<>();
|
ArrayList<SearchResult> results=new ArrayList<>();
|
||||||
if(result.accounts!=null){
|
if(result.accounts!=null){
|
||||||
for(Account acc:result.accounts)
|
for(Account acc:result.accounts)
|
||||||
@@ -163,13 +148,11 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}
|
}
|
||||||
prevDisplayItems=new ArrayList<>(displayItems);
|
prevDisplayItems=new ArrayList<>(displayItems);
|
||||||
unfilteredResults=results;
|
unfilteredResults=results;
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(filterSearchResults(results), false);
|
onDataLoaded(filterSearchResults(results), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
resetEmptyText();
|
|
||||||
currentRequest=null;
|
currentRequest=null;
|
||||||
Activity a=getActivity();
|
Activity a=getActivity();
|
||||||
if(a==null)
|
if(a==null)
|
||||||
@@ -253,7 +236,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setQuery(String q){
|
public void setQuery(String q){
|
||||||
if(Objects.equals(q, currentQuery) || q.isBlank())
|
if(Objects.equals(q, currentQuery))
|
||||||
return;
|
return;
|
||||||
if(currentRequest!=null){
|
if(currentRequest!=null){
|
||||||
currentRequest.cancel();
|
currentRequest.cancel();
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.trends.GetTrendingHashtags;
|
import org.joinmastodon.android.api.requests.trends.GetTrendingHashtags;
|
||||||
import org.joinmastodon.android.fragments.DomainDisplay;
|
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
|
||||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
@@ -26,7 +23,7 @@ import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
|||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop, IsOnTop, DomainDisplay {
|
public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_HASHTAGS);
|
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_HASHTAGS);
|
||||||
|
|
||||||
@@ -34,11 +31,6 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
|||||||
super(10);
|
super(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDomain() {
|
|
||||||
return DomainDisplay.super.getDomain() + "/explore/tags";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -51,7 +43,6 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Hashtag> result){
|
public void onSuccess(List<Hashtag> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -75,16 +66,6 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isScrolledToTop() {
|
|
||||||
return list.getChildAt(0).getTop() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOnTop() {
|
|
||||||
return isRecyclerViewOnTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|||||||