Merge branch 'master' into click_notifications
This commit is contained in:
commit
321d7ca7ca
34
.cirrus.yml
34
.cirrus.yml
@ -1,34 +0,0 @@
|
|||||||
connected_check_task:
|
|
||||||
name: Run Android instrumented tests
|
|
||||||
env:
|
|
||||||
API_LEVEL: 23
|
|
||||||
TARGET: default
|
|
||||||
ARCH: x86
|
|
||||||
CC_TEST_REPORTER_ID: ENCRYPTED[!b71004b17b92e8fb7a3fecc3e2a9cc28c4e5f07f55e2f20cdfc641c57487cd21c7df6e7930318f8d87bc4675e63b260d!]
|
|
||||||
container:
|
|
||||||
image: reactivecircus/android-emulator-23:latest
|
|
||||||
kvm: true
|
|
||||||
cpu: 8
|
|
||||||
memory: 16G
|
|
||||||
create_device_script:
|
|
||||||
echo no | avdmanager create avd --force --name "api-${API_LEVEL}" --abi "${TARGET}/${ARCH}" --package "system-images;android-${API_LEVEL};${TARGET};${ARCH}"
|
|
||||||
start_emulator_background_script:
|
|
||||||
$ANDROID_HOME/emulator/emulator -avd "api-${API_LEVEL}" -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
|
|
||||||
wait_for_emulator_script:
|
|
||||||
- chmod +x android-wait-for-emulator.sh
|
|
||||||
- ./android-wait-for-emulator.sh
|
|
||||||
disable_animations_script: |
|
|
||||||
adb shell settings put global window_animation_scale 0.0
|
|
||||||
adb shell settings put global transition_animation_scale 0.0
|
|
||||||
adb shell settings put global animator_duration_scale 0.0
|
|
||||||
prepare_codeclimate_script:
|
|
||||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
||||||
- chmod +x ./cc-test-reporter
|
|
||||||
- ./cc-test-reporter before-build
|
|
||||||
run_instrumented_tests_script:
|
|
||||||
./gradlew build connectedCheck jacocoTestReport
|
|
||||||
report_codeclimate_script:
|
|
||||||
# Report test coverage to Code Climate
|
|
||||||
- export JACOCO_SOURCE_PATH=app/src/main/java/
|
|
||||||
- ./cc-test-reporter format-coverage ./app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml --input-type jacoco
|
|
||||||
- ./cc-test-reporter upload-coverage
|
|
29
.fdroid.yml.template
Normal file
29
.fdroid.yml.template
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Categories:
|
||||||
|
- Internet
|
||||||
|
License: GPL-3.0-or-later
|
||||||
|
AuthorName: PixelDroid team
|
||||||
|
AuthorEmail: contact@pixeldroid.org
|
||||||
|
WebSite: https://pixeldroid.org/
|
||||||
|
SourceCode: https://gitlab.shinice.net/pixeldroid/PixelDroid
|
||||||
|
IssueTracker: https://gitlab.shinice.net/pixeldroid/PixelDroid/issues
|
||||||
|
Translation: https://weblate.pixeldroid.org/projects/pixeldroid/
|
||||||
|
Changelog: https://gitlab.shinice.net/pixeldroid/PixelDroid/-/releases
|
||||||
|
Liberapay: PixelDroid
|
||||||
|
|
||||||
|
AutoName: PixelDroid
|
||||||
|
|
||||||
|
RepoType: git
|
||||||
|
Repo: https://gitlab.shinice.net/pixeldroid/PixelDroid.git
|
||||||
|
|
||||||
|
Builds:
|
||||||
|
- versionName: ${versionName}
|
||||||
|
versionCode: ${versionCode}
|
||||||
|
commit: HEAD
|
||||||
|
subdir: app
|
||||||
|
gradle:
|
||||||
|
- yes
|
||||||
|
|
||||||
|
AutoUpdateMode: Version %v
|
||||||
|
UpdateCheckMode: Tags
|
||||||
|
CurrentVersion: 1.0.beta1
|
||||||
|
CurrentVersionCode: 1
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
app/src/main/assets/licenses.html linguist-generated
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,5 +14,6 @@
|
|||||||
.cxx
|
.cxx
|
||||||
.idea
|
.idea
|
||||||
app/release
|
app/release
|
||||||
|
app/debug
|
||||||
app/lint
|
app/lint
|
||||||
lint
|
lint
|
||||||
|
@ -7,6 +7,14 @@ variables:
|
|||||||
TARGET: "default"
|
TARGET: "default"
|
||||||
|
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
# Basic android and gradle stuff
|
# Basic android and gradle stuff
|
||||||
# Check linting
|
# Check linting
|
||||||
lintDebug:
|
lintDebug:
|
||||||
@ -52,6 +60,62 @@ emulatorTest:
|
|||||||
- cat app/build/reports/jacoco/jacocoTestReport/html/index.html | grep -o 'Total[^%]*%'
|
- cat app/build/reports/jacoco/jacocoTestReport/html/index.html | grep -o 'Total[^%]*%'
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
paths:
|
paths:
|
||||||
- ./app/build/reports/jacoco/jacocoTestReport/
|
- ./app/build/reports/jacoco/jacocoTestReport/
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
|
|
||||||
|
|
||||||
|
fdroid build:
|
||||||
|
image: registry.gitlab.com/fdroid/ci-images-client:latest
|
||||||
|
allow_failure: true
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- unsigned/
|
||||||
|
when: always
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
cache:
|
||||||
|
key: "$CI_JOB_NAME"
|
||||||
|
paths:
|
||||||
|
- .gradle
|
||||||
|
script:
|
||||||
|
# Put the correct versionName and versionCode in the .fdroid.yml
|
||||||
|
- sed -e "s/\${versionName}/$(grep "versionName " app/build.gradle | awk '{print $2}')/" -e "s/\${versionCode}/$(grep "versionCode" app/build.gradle | awk '{print $2}')/" .fdroid.yml.template > .fdroid.yml
|
||||||
|
- rm .fdroid.yml.template
|
||||||
|
- test -d build || mkdir build
|
||||||
|
- test -d fdroidserver || mkdir fdroidserver
|
||||||
|
- git ls-remote https://gitlab.com/fdroid/fdroidserver.git master
|
||||||
|
- curl --silent https://gitlab.com/fdroid/fdroidserver/-/archive/master/fdroidserver-master.tar.gz
|
||||||
|
| tar -xz --directory=fdroidserver --strip-components=1
|
||||||
|
- export PATH="`pwd`/fdroidserver:$PATH"
|
||||||
|
- export PYTHONPATH="$CI_PROJECT_DIR/fdroidserver:$CI_PROJECT_DIR/fdroidserver/examples"
|
||||||
|
- export PYTHONUNBUFFERED=true
|
||||||
|
|
||||||
|
- bash fdroidserver/buildserver/setup-env-vars $ANDROID_HOME
|
||||||
|
- adduser --disabled-password --gecos "" vagrant
|
||||||
|
- ln -s $CI_PROJECT_DIR/fdroidserver /home/vagrant/fdroidserver
|
||||||
|
- mkdir -p /vagrant/cache
|
||||||
|
- wget -q https://services.gradle.org/distributions/gradle-5.6.2-bin.zip
|
||||||
|
--output-document=/vagrant/cache/gradle-5.6.2-bin.zip
|
||||||
|
- bash fdroidserver/buildserver/provision-gradle
|
||||||
|
- bash fdroidserver/buildserver/provision-apt-get-install http://deb.debian.org/debian
|
||||||
|
- source /etc/profile.d/bsenv.sh
|
||||||
|
- apt-get dist-upgrade
|
||||||
|
|
||||||
|
# install fdroidserver from git, with deps from Debian, until fdroidserver
|
||||||
|
# is stable enough to include all the things needed here
|
||||||
|
- apt-get install -t stretch-backports
|
||||||
|
fdroidserver
|
||||||
|
python3-asn1crypto
|
||||||
|
python3-ruamel.yaml
|
||||||
|
yamllint
|
||||||
|
- apt-get purge fdroidserver
|
||||||
|
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
# each `fdroid build --on-server` run expects sudo, then uninstalls it
|
||||||
|
- set -x
|
||||||
|
- apt-get install sudo
|
||||||
|
- fdroid fetchsrclibs --verbose
|
||||||
|
# this builds the latest version of the app from its source dir, using the build recipe in .fdroid.yml
|
||||||
|
- fdroid build --verbose --on-server --no-tarball
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
# PixelDroid
|
# PixelDroid
|
||||||

|

|
||||||
Free (as in freedom) Android client for Pixelfed, the federated image sharing platform.
|
Free (as in freedom) Android client for Pixelfed, the federated image sharing platform. Licensed under the GNU General Public License version 3 (or any later version)
|
||||||
|
|
||||||
[](https://gitlab.shinice.net/pixeldroid/PixelDroid/pipelines) [](https://codeclimate.com/github/H-PixelDroid/PixelDroid/maintainability) [](https://weblate.pixeldroid.org/engage/pixeldroid/?utm_source=widget)
|
[](https://gitlab.shinice.net/pixeldroid/PixelDroid/pipelines) [](https://codeclimate.com/github/H-PixelDroid/PixelDroid/maintainability) [](https://weblate.pixeldroid.org/engage/pixeldroid/?utm_source=widget)
|
||||||
|
|
||||||
<a href=https://apt.izzysoft.de/fdroid/index/apk/com.h.pixeldroid><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" width="170"></a>
|
|
||||||
|
<a href="https://f-droid.org/en/packages/org.pixeldroid.app/">
|
||||||
|
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" width="206">
|
||||||
|
</a>
|
||||||
|
|
||||||
## 🔧 Compiling the code yourself
|
## 🔧 Compiling the code yourself
|
||||||
If you want to try out PixelDroid on your own device, you can compile the source code yourself. To do that you can install [Android Studio](https://developer.android.com/studio/).
|
If you want to try out PixelDroid on your own device, you can compile the source code yourself. To do that you can install [Android Studio](https://developer.android.com/studio/).
|
||||||
|
BIN
Screenshots/featureGraphic.xcf
Normal file
BIN
Screenshots/featureGraphic.xcf
Normal file
Binary file not shown.
108
app/build.gradle
108
app/build.gradle
@ -7,6 +7,9 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'jacoco'
|
apply plugin: 'jacoco'
|
||||||
|
|
||||||
|
// Force latest version of Jacoco, initially done to resolve https://github.com/jacoco/jacoco/issues/1155
|
||||||
|
jacoco.toolVersion = "0.8.7"
|
||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
@ -21,11 +24,11 @@ android {
|
|||||||
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
|
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
|
||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.h.pixeldroid"
|
applicationId "org.pixeldroid.app"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 10
|
versionCode 3
|
||||||
versionName "1.0.alpha9"
|
versionName "1.0.beta3"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
@ -36,15 +39,15 @@ android {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/java'
|
main.java.srcDirs += 'src/main/java'
|
||||||
test.java.srcDirs += 'src/test/java'
|
test.java.srcDirs += 'src/test/java'
|
||||||
|
staging.res.srcDirs += 'src/debug/res'
|
||||||
androidTest.java.srcDirs += 'src/androidTest/java'
|
androidTest.java.srcDirs += 'src/androidTest/java'
|
||||||
}
|
}
|
||||||
testBuildType "staging"
|
testBuildType "staging"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
|
applicationIdSuffix '.debug'
|
||||||
|
versionNameSuffix "-debug"
|
||||||
}
|
}
|
||||||
staging {
|
staging {
|
||||||
initWith debug
|
initWith debug
|
||||||
@ -61,12 +64,12 @@ android {
|
|||||||
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
|
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConfigField "String", "USER_ID", System.getenv("USER_ID") ?: localProperties['USER_ID']
|
buildConfigField "String", "USER_ID", System.getenv("USER_ID") ?: localProperties['USER_ID'] ?: ""
|
||||||
buildConfigField "String", "INSTANCE_URI", System.getenv("INSTANCE_URI") ?: localProperties['INSTANCE_URI']
|
buildConfigField "String", "INSTANCE_URI", System.getenv("INSTANCE_URI") ?: localProperties['INSTANCE_URI'] ?: ""
|
||||||
buildConfigField "String", "ACCESS_TOKEN", System.getenv("ACCESS_TOKEN") ?: localProperties['ACCESS_TOKEN']
|
buildConfigField "String", "ACCESS_TOKEN", System.getenv("ACCESS_TOKEN") ?: localProperties['ACCESS_TOKEN'] ?: ""
|
||||||
buildConfigField "String", "REFRESH_TOKEN", System.getenv("REFRESH_TOKEN") ?: localProperties['REFRESH_TOKEN']
|
buildConfigField "String", "REFRESH_TOKEN", System.getenv("REFRESH_TOKEN") ?: localProperties['REFRESH_TOKEN'] ?: ""
|
||||||
buildConfigField "String", "CLIENT_ID", System.getenv("CLIENT_ID") ?: localProperties['CLIENT_ID']
|
buildConfigField "String", "CLIENT_ID", System.getenv("CLIENT_ID") ?: localProperties['CLIENT_ID'] ?: ""
|
||||||
buildConfigField "String", "CLIENT_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET']
|
buildConfigField "String", "CLIENT_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET'] ?: ""
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
@ -74,6 +77,14 @@ android {
|
|||||||
proguardFiles 'proguard-rules.pro'
|
proguardFiles 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Make a string with the application_id (available in xml etc)
|
||||||
|
*/
|
||||||
|
android.applicationVariants.all { variant ->
|
||||||
|
variant.resValue 'string', 'application_id', variant.applicationId
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
animationsDisabled true
|
animationsDisabled true
|
||||||
|
|
||||||
@ -92,37 +103,38 @@ dependencies {
|
|||||||
/**
|
/**
|
||||||
* AndroidX dependencies:
|
* AndroidX dependencies:
|
||||||
*/
|
*/
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.5.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
implementation "androidx.browser:browser:1.3.0"
|
implementation "androidx.browser:browser:1.3.0"
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
implementation 'androidx.paging:paging-runtime-ktx:3.0.0-beta02'
|
implementation 'androidx.paging:paging-runtime-ktx:3.0.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1'
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0"
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.0"
|
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
|
||||||
implementation "androidx.annotation:annotation:1.1.0"
|
implementation "androidx.annotation:annotation:1.2.0"
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation "androidx.activity:activity-ktx:1.2.1"
|
implementation "androidx.activity:activity-ktx:1.2.3"
|
||||||
|
implementation 'androidx.fragment:fragment-ktx:1.3.5'
|
||||||
|
|
||||||
// Use the most recent version of CameraX
|
// Use the most recent version of CameraX
|
||||||
def cameraX_version = '1.0.0-rc03'
|
def cameraX_version = '1.0.0'
|
||||||
implementation "androidx.camera:camera-core:${cameraX_version}"
|
implementation "androidx.camera:camera-core:${cameraX_version}"
|
||||||
implementation "androidx.camera:camera-camera2:${cameraX_version}"
|
implementation "androidx.camera:camera-camera2:${cameraX_version}"
|
||||||
// CameraX Lifecycle library
|
// CameraX Lifecycle library
|
||||||
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
|
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
|
||||||
|
|
||||||
// CameraX View class
|
// CameraX View class
|
||||||
implementation 'androidx.camera:camera-view:1.0.0-alpha22'
|
implementation 'androidx.camera:camera-view:1.0.0-alpha25'
|
||||||
|
|
||||||
def room_version = "2.3.0-beta03"
|
def room_version = "2.3.0"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
@ -136,17 +148,17 @@ dependencies {
|
|||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
|
|
||||||
//Dagger (dependency injection)
|
//Dagger (dependency injection)
|
||||||
implementation 'com.google.dagger:dagger-android:2.30.1'
|
implementation 'com.google.dagger:dagger-android:2.37'
|
||||||
implementation 'com.google.dagger:dagger-android-support:2.30.1'
|
implementation 'com.google.dagger:dagger-android-support:2.37'
|
||||||
// if you use the support libraries
|
// if you use the support libraries
|
||||||
kapt 'com.google.dagger:dagger-android-processor:2.30.1'
|
kapt 'com.google.dagger:dagger-android-processor:2.37'
|
||||||
kapt 'com.google.dagger:dagger-compiler:2.30.1'
|
kapt 'com.google.dagger:dagger-compiler:2.37'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
|
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.20'
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
implementation 'com.github.connyduck:sparkbutton:4.1.0'
|
implementation 'com.github.connyduck:sparkbutton:4.1.0'
|
||||||
|
|
||||||
@ -154,26 +166,26 @@ dependencies {
|
|||||||
implementation 'info.androidhive:imagefilters:1.0.7'
|
implementation 'info.androidhive:imagefilters:1.0.7'
|
||||||
implementation 'com.github.yalantis:ucrop:2.2.6-native'
|
implementation 'com.github.yalantis:ucrop:2.2.6-native'
|
||||||
|
|
||||||
implementation("com.github.bumptech.glide:glide:4.11.0") {
|
implementation('com.github.bumptech.glide:glide:4.12.0') {
|
||||||
exclude group: "com.android.support"
|
exclude group: "com.android.support"
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
|
implementation 'com.github.bumptech.glide:okhttp-integration:4.12.0'
|
||||||
implementation("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
implementation('com.github.bumptech.glide:recyclerview-integration:4.12.0') {
|
||||||
// Excludes the support library because it's already included by Glide.
|
// Excludes the support library because it's already included by Glide.
|
||||||
transitive = false
|
transitive = false
|
||||||
}
|
}
|
||||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
kapt 'com.github.bumptech.glide:compiler:4.12.0'
|
||||||
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
|
||||||
implementation "com.mikepenz:materialdrawer:8.1.8"
|
implementation 'com.mikepenz:materialdrawer:8.4.1'
|
||||||
// Add for NavController support
|
// Add for NavController support
|
||||||
implementation "com.mikepenz:materialdrawer-nav:8.1.5"
|
implementation 'com.mikepenz:materialdrawer-nav:8.4.0'
|
||||||
|
|
||||||
//iconics
|
//iconics
|
||||||
implementation "com.mikepenz:iconics-core:5.0.3"
|
implementation "com.mikepenz:iconics-core:5.2.8"
|
||||||
implementation "com.mikepenz:materialdrawer-iconics:8.1.8"
|
implementation 'com.mikepenz:materialdrawer-iconics:8.4.1'
|
||||||
implementation "com.mikepenz:iconics-views:5.0.3"
|
implementation "com.mikepenz:iconics-views:5.0.3"
|
||||||
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar'
|
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar'
|
||||||
|
|
||||||
@ -190,13 +202,15 @@ dependencies {
|
|||||||
|
|
||||||
// debugImplementation required vs testImplementation: https://issuetracker.google.com/issues/128612536
|
// debugImplementation required vs testImplementation: https://issuetracker.google.com/issues/128612536
|
||||||
//noinspection FragmentGradleConfiguration
|
//noinspection FragmentGradleConfiguration
|
||||||
stagingImplementation("androidx.fragment:fragment-testing:1.3.1") {
|
stagingImplementation("androidx.fragment:fragment-testing:1.3.5") {
|
||||||
exclude group:'androidx.test', module:'monitor'
|
exclude group:'androidx.test', module:'monitor'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//stagingImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
|
||||||
|
|
||||||
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.27.2'
|
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.27.2'
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation "androidx.room:room-testing:$room_version"
|
testImplementation "androidx.room:room-testing:$room_version"
|
||||||
|
|
||||||
|
|
||||||
@ -227,10 +241,10 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['connectedStagingAndroidTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
|
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
|
||||||
def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
|
def kotlinTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/staging", excludes: fileFilter)
|
||||||
def mainSrc = "$project.projectDir/src/main/java"
|
def mainSrc = "$project.projectDir/src/main/java"
|
||||||
getSourceDirectories().from(files([mainSrc]))
|
getSourceDirectories().from(files([mainSrc]))
|
||||||
getClassDirectories().from(files([kotlinDebugTree]))
|
getClassDirectories().from(files([kotlinTree]))
|
||||||
getExecutionData().from(fileTree(dir: project.buildDir, includes: [
|
getExecutionData().from(fileTree(dir: project.buildDir, includes: [
|
||||||
|
|
||||||
'outputs/code_coverage/stagingAndroidTest/connected/*coverage.ec',
|
'outputs/code_coverage/stagingAndroidTest/connected/*coverage.ec',
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
license: The Apache Software License, Version 2.0
|
license: The Apache Software License, Version 2.0
|
||||||
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
url: https://github.com/mikepenz/MaterialDrawer
|
url: https://github.com/mikepenz/MaterialDrawer
|
||||||
- artifact: org.jetbrains.kotlin:kotlin-android-extensions-runtime:+
|
- artifact: androidx.startup:startup-runtime:+
|
||||||
name: kotlin-android-extensions-runtime
|
name: startup-runtime
|
||||||
copyrightHolder: JetBrains s.r.o. and contributors
|
copyrightHolder: Google Inc.
|
||||||
license: The Apache License, Version 2.0
|
license: The Apache Software License, Version 2.0
|
||||||
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
url: https://kotlinlang.org/
|
url: https://developer.android.com/jetpack/androidx/releases/startup#1.0.0
|
||||||
- artifact: com.mikepenz:iconics-views:+
|
- artifact: com.mikepenz:iconics-views:+
|
||||||
name: iconics-views
|
name: iconics-views
|
||||||
copyrightHolder: Mike Penz and contributors
|
copyrightHolder: Mike Penz and contributors
|
||||||
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
@ -37,9 +37,9 @@
|
|||||||
# APP SPECIFIC OPTIONS
|
# APP SPECIFIC OPTIONS
|
||||||
|
|
||||||
# keep members of our model classes, they are used in json de/serialization
|
# keep members of our model classes, they are used in json de/serialization
|
||||||
-keepclassmembers class com.h.pixeldroid.utils.api.objects.* { *; }
|
-keepclassmembers class org.pixeldroid.app.utils.api.objects.* { *; }
|
||||||
|
|
||||||
-keep public enum com.h.pixeldroid.utils.api.objects.*$** {
|
-keep public enum org.pixeldroid.app.utils.api.objects.*$** {
|
||||||
**[] $VALUES;
|
**[] $VALUES;
|
||||||
public *;
|
public *;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -10,11 +10,11 @@ import androidx.test.core.app.ApplicationProvider
|
|||||||
import androidx.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers
|
import androidx.test.espresso.intent.matcher.IntentMatchers
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
import com.h.pixeldroid.postCreation.camera.CameraFragment
|
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import com.h.pixeldroid.testUtility.initDB
|
import org.pixeldroid.app.testUtility.initDB
|
||||||
import org.hamcrest.CoreMatchers
|
import org.hamcrest.CoreMatchers
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.junit.After
|
import org.junit.After
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
@ -12,8 +12,9 @@ import androidx.test.espresso.matcher.ViewMatchers.*
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
import androidx.test.uiautomator.UiDevice
|
import androidx.test.uiautomator.UiDevice
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.hamcrest.Matchers.allOf
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.testUtility.*
|
||||||
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@ -107,8 +108,10 @@ class DrawerMenuTest {
|
|||||||
// Start the screen of your activity.
|
// Start the screen of your activity.
|
||||||
onView(withText(R.string.menu_account)).perform(click())
|
onView(withText(R.string.menu_account)).perform(click())
|
||||||
// Check that profile activity was opened.
|
// Check that profile activity was opened.
|
||||||
|
waitForView(R.id.editButton)
|
||||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||||
val followersText = context.resources.getQuantityString(R.plurals.nb_followers, 2, 2)
|
val followersText = context.resources.getQuantityString(R.plurals.nb_followers, 2, 2)
|
||||||
|
waitForView(R.id.nbFollowingTextView, allOf(withId(R.id.nbFollowersTextView), withText(followersText)))
|
||||||
onView(withText(followersText)).perform(click())
|
onView(withText(followersText)).perform(click())
|
||||||
|
|
||||||
waitForView(R.id.account_entry_avatar)
|
waitForView(R.id.account_entry_avatar)
|
||||||
@ -120,8 +123,10 @@ class DrawerMenuTest {
|
|||||||
// Start the screen of your activity.
|
// Start the screen of your activity.
|
||||||
onView(withText(R.string.menu_account)).perform(click())
|
onView(withText(R.string.menu_account)).perform(click())
|
||||||
// Check that profile activity was opened.
|
// Check that profile activity was opened.
|
||||||
|
waitForView(R.id.editButton)
|
||||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||||
val followingText = context.resources.getQuantityString(R.plurals.nb_following, 3, 3)
|
val followingText = context.resources.getQuantityString(R.plurals.nb_following, 3, 3)
|
||||||
|
waitForView(R.id.nbFollowingTextView, allOf(withId(R.id.nbFollowingTextView), withText(followingText)))
|
||||||
onView(withText(followingText)).perform(click())
|
onView(withText(followingText)).perform(click())
|
||||||
|
|
||||||
waitForView(R.id.account_entry_avatar)
|
waitForView(R.id.account_entry_avatar)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -22,13 +22,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
|
import org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity
|
||||||
import com.h.pixeldroid.postCreation.photoEdit.ThumbnailAdapter
|
import org.pixeldroid.app.postCreation.photoEdit.ThumbnailAdapter
|
||||||
import com.h.pixeldroid.settings.AboutActivity
|
import org.pixeldroid.app.settings.AboutActivity
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import com.h.pixeldroid.testUtility.clickChildViewWithId
|
import org.pixeldroid.app.testUtility.clickChildViewWithId
|
||||||
import com.h.pixeldroid.testUtility.slowSwipeLeft
|
import org.pixeldroid.app.testUtility.slowSwipeLeft
|
||||||
import com.h.pixeldroid.testUtility.waitForView
|
import org.pixeldroid.app.testUtility.waitForView
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
@ -70,7 +70,9 @@ class EditPhotoTest {
|
|||||||
file.writeBitmap(image)
|
file.writeBitmap(image)
|
||||||
uri = file.toUri()
|
uri = file.toUri()
|
||||||
}
|
}
|
||||||
val intent = Intent(context, PhotoEditActivity::class.java).putExtra("picture_uri", uri)
|
val intent = Intent(context, PhotoEditActivity::class.java)
|
||||||
|
.putExtra(PhotoEditActivity.PICTURE_URI, uri)
|
||||||
|
.putExtra(PhotoEditActivity.PICTURE_POSITION, 0)
|
||||||
|
|
||||||
activityScenario = ActivityScenario.launch<PhotoEditActivity>(intent).onActivity{a -> activity = a}
|
activityScenario = ActivityScenario.launch<PhotoEditActivity>(intent).onActivity{a -> activity = a}
|
||||||
|
|
@ -1,10 +1,12 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.action.ViewActions.openLinkWithText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
|
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
|
||||||
@ -12,17 +14,21 @@ import androidx.test.espresso.matcher.ViewMatchers.*
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.hamcrest.CoreMatchers.*
|
||||||
import com.h.pixeldroid.posts.StatusViewHolder
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
import org.hamcrest.CoreMatchers.not
|
import org.pixeldroid.app.testUtility.*
|
||||||
|
import org.hamcrest.core.IsInstanceOf
|
||||||
|
import org.hamcrest.core.StringContains.containsString
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TestRule
|
||||||
import org.junit.rules.Timeout
|
import org.junit.rules.Timeout
|
||||||
|
import org.junit.runner.Description
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.model.Statement
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class HomeFeedTest {
|
class HomeFeedTest {
|
||||||
@ -31,8 +37,9 @@ class HomeFeedTest {
|
|||||||
private lateinit var db: AppDatabase
|
private lateinit var db: AppDatabase
|
||||||
private lateinit var context: Context
|
private lateinit var context: Context
|
||||||
|
|
||||||
@get:Rule
|
@Rule @JvmField
|
||||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
var repeatRule: RepeatRule = RepeatRule()
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun before(){
|
fun before(){
|
||||||
@ -47,6 +54,10 @@ class HomeFeedTest {
|
|||||||
)
|
)
|
||||||
db.close()
|
db.close()
|
||||||
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
||||||
|
|
||||||
|
waitForView(R.id.username)
|
||||||
|
onView(withId(R.id.list)).perform(scrollToPosition<StatusViewHolder>(0))
|
||||||
|
|
||||||
}
|
}
|
||||||
@After
|
@After
|
||||||
fun after() {
|
fun after() {
|
||||||
@ -54,6 +65,7 @@ class HomeFeedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@RepeatTest
|
||||||
fun clickingTabOnAlbumShowsNextPhoto() {
|
fun clickingTabOnAlbumShowsNextPhoto() {
|
||||||
//Wait for the feed to load
|
//Wait for the feed to load
|
||||||
waitForView(R.id.postPager)
|
waitForView(R.id.postPager)
|
||||||
@ -66,6 +78,40 @@ class HomeFeedTest {
|
|||||||
}
|
}
|
||||||
onView(first(withId(R.id.postPager))).check(matches(isDisplayed()))
|
onView(first(withId(R.id.postPager))).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RepeatTest
|
||||||
|
fun tabReClickScrollUp() {
|
||||||
|
//Wait for the feed to load
|
||||||
|
waitForView(R.id.postPager)
|
||||||
|
|
||||||
|
onView(withId(R.id.list)).perform(scrollToPosition<StatusViewHolder>(4))
|
||||||
|
|
||||||
|
onView(first(IsInstanceOf.instanceOf(TabLayout.TabView::class.java))).perform(ViewActions.click())
|
||||||
|
|
||||||
|
|
||||||
|
onView(first(withId(R.id.description))).check(matches(withText(containsString("@user2"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RepeatTest
|
||||||
|
fun hashtag() {
|
||||||
|
//Wait for the feed to load
|
||||||
|
waitForView(R.id.postPager)
|
||||||
|
|
||||||
|
onView(allOf(withClassName(endsWith("RecyclerView")), not(withId(R.id.material_drawer_recycler_view))))
|
||||||
|
.perform(
|
||||||
|
scrollToPosition<StatusViewHolder>(3)
|
||||||
|
)
|
||||||
|
|
||||||
|
onView(allOf(withText(containsString("randomNoise"))))
|
||||||
|
.perform(clickClickableSpan("#randomNoise"))
|
||||||
|
|
||||||
|
waitForView(R.id.action_bar, allOf(withText("#randomNoise"), not(withId(R.id.description))))
|
||||||
|
|
||||||
|
onView(withId(R.id.action_bar)).check(matches(isDisplayed()));
|
||||||
|
onView(allOf(withText("#randomNoise"), not(withId(R.id.description)))).check(matches(withParent(withId(R.id.action_bar))));
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun clickingReblogButtonWorks() {
|
fun clickingReblogButtonWorks() {
|
||||||
@ -127,6 +173,7 @@ class HomeFeedTest {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@RepeatTest
|
||||||
fun clickingUsernameOpensProfile() {
|
fun clickingUsernameOpensProfile() {
|
||||||
waitForView(R.id.username)
|
waitForView(R.id.username)
|
||||||
|
|
||||||
@ -137,6 +184,7 @@ class HomeFeedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@RepeatTest
|
||||||
fun clickingProfilePicOpensProfile() {
|
fun clickingProfilePicOpensProfile() {
|
||||||
waitForView(R.id.profilePic)
|
waitForView(R.id.profilePic)
|
||||||
|
|
||||||
@ -147,6 +195,7 @@ class HomeFeedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@RepeatTest
|
||||||
fun clickingMentionOpensProfile() {
|
fun clickingMentionOpensProfile() {
|
||||||
waitForView(R.id.description)
|
waitForView(R.id.description)
|
||||||
|
|
||||||
@ -156,13 +205,6 @@ class HomeFeedTest {
|
|||||||
onView(first(withId(R.id.username))).check(matches(isDisplayed()))
|
onView(first(withId(R.id.username))).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@Test
|
|
||||||
fun clickingHashTagsWorks() {
|
|
||||||
onView(withId(R.id.list)).perform(
|
|
||||||
actionOnItemAtPosition<StatusViewHolder>(1, clickChildViewWithId(R.id.description))
|
|
||||||
)
|
|
||||||
onView(withId(R.id.list)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -217,6 +259,7 @@ class HomeFeedTest {
|
|||||||
.check(matches(hasDescendant(withId(R.id.comment))))
|
.check(matches(hasDescendant(withId(R.id.comment))))
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
@RepeatTest
|
||||||
@Test
|
@Test
|
||||||
fun performClickOnSensitiveWarning() {
|
fun performClickOnSensitiveWarning() {
|
||||||
waitForView(R.id.username)
|
waitForView(R.id.username)
|
||||||
@ -232,11 +275,10 @@ class HomeFeedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@RepeatTest
|
||||||
fun performClickOnSensitiveWarningTabs() {
|
fun performClickOnSensitiveWarningTabs() {
|
||||||
waitForView(R.id.username)
|
waitForView(R.id.username)
|
||||||
|
|
||||||
onView(withId(R.id.list)).perform(scrollToPosition<StatusViewHolder>(0))
|
|
||||||
|
|
||||||
onView(first(withId(R.id.sensitiveWarning))).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
onView(first(withId(R.id.sensitiveWarning))).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
onView(withId(R.id.list))
|
onView(withId(R.id.list))
|
@ -1,17 +1,10 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.text.SpannableString
|
|
||||||
import android.text.style.ClickableSpan
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.espresso.Espresso
|
import androidx.test.espresso.Espresso
|
||||||
import androidx.test.espresso.NoMatchingViewException
|
|
||||||
import androidx.test.espresso.UiController
|
|
||||||
import androidx.test.espresso.ViewAction
|
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
import androidx.test.espresso.assertion.ViewAssertions
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
import androidx.test.espresso.contrib.DrawerActions
|
import androidx.test.espresso.contrib.DrawerActions
|
||||||
@ -23,17 +16,14 @@ import androidx.test.espresso.intent.matcher.IntentMatchers
|
|||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.posts.StatusViewHolder
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.settings.AboutActivity
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
import org.pixeldroid.app.testUtility.*
|
||||||
import com.h.pixeldroid.settings.AboutActivity
|
|
||||||
import com.h.pixeldroid.testUtility.*
|
|
||||||
import org.hamcrest.CoreMatchers
|
import org.hamcrest.CoreMatchers
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
@ -1,5 +1,5 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
/*
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
@ -9,33 +9,20 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
|
|||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import org.junit.*
|
||||||
import androidx.test.uiautomator.UiDevice
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import androidx.test.uiautomator.UiSelector
|
import org.pixeldroid.app.testUtility.initDB
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.testUtility.initDB
|
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.rules.Timeout
|
import org.junit.rules.Timeout
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.pixeldroid.app.testUtility.waitForView
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@Ignore("Ignore until we can get TestButler to work on CI")
|
||||||
class LoginActivityOfflineTest {
|
class LoginActivityOfflineTest {
|
||||||
|
|
||||||
private lateinit var context: Context
|
private lateinit var context: Context
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun switchAirplaneMode() {
|
|
||||||
val device = UiDevice.getInstance(getInstrumentation())
|
|
||||||
device.openQuickSettings()
|
|
||||||
device.findObject(UiSelector().textContains("Airplane")).click()
|
|
||||||
device.pressHome()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var db: AppDatabase
|
private lateinit var db: AppDatabase
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
@ -43,7 +30,8 @@ class LoginActivityOfflineTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun before() {
|
fun before() {
|
||||||
switchAirplaneMode()
|
//TestButler.setWifiState(false)
|
||||||
|
//TestButler.setGsmState(false)
|
||||||
context = ApplicationProvider.getApplicationContext<Context>()
|
context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
db = initDB(context)
|
db = initDB(context)
|
||||||
db.clearAllTables()
|
db.clearAllTables()
|
||||||
@ -52,19 +40,22 @@ class LoginActivityOfflineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun emptyDBandOfflineModeDisplayCorrectMessage() {
|
fun emptyDBandOfflineModeDisplayCorrectMessage() {
|
||||||
|
waitForView(R.id.login_activity_connection_required)
|
||||||
onView(withId(R.id.login_activity_connection_required)).check(matches(isDisplayed()))
|
onView(withId(R.id.login_activity_connection_required)).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun retryButtonReloadsLoginActivity() {
|
fun retryButtonReloadsLoginActivity() {
|
||||||
|
waitForView(R.id.login_activity_connection_required_button)
|
||||||
onView(withId(R.id.login_activity_connection_required_button)).perform(click())
|
onView(withId(R.id.login_activity_connection_required_button)).perform(click())
|
||||||
onView(withId(R.id.login_activity_connection_required)).check(matches(isDisplayed()))
|
onView(withId(R.id.login_activity_connection_required)).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun after() {
|
fun after() {
|
||||||
switchAirplaneMode()
|
//TestButler.setWifiState(true)
|
||||||
|
//TestButler.setGsmState(true)
|
||||||
db.close()
|
db.close()
|
||||||
clearData()
|
clearData()
|
||||||
}
|
}
|
||||||
}*/
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -13,22 +13,18 @@ import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
|||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
import androidx.test.espresso.action.ViewActions.replaceText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.hasErrorText
|
import androidx.test.espresso.matcher.ViewMatchers.hasErrorText
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.testUtility.initDB
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
|
||||||
import com.h.pixeldroid.testUtility.initDB
|
|
||||||
import com.h.pixeldroid.testUtility.testiTesto
|
|
||||||
import com.h.pixeldroid.testUtility.testiTestoInstance
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.Timeout
|
import org.junit.rules.Timeout
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.pixeldroid.app.testUtility.PACKAGE_ID
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class LoginActivityOnlineTest {
|
class LoginActivityOnlineTest {
|
||||||
@ -44,7 +40,7 @@ class LoginActivityOnlineTest {
|
|||||||
fun setup() {
|
fun setup() {
|
||||||
context = ApplicationProvider.getApplicationContext()
|
context = ApplicationProvider.getApplicationContext()
|
||||||
context = ApplicationProvider.getApplicationContext()
|
context = ApplicationProvider.getApplicationContext()
|
||||||
pref = context.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
|
pref = context.getSharedPreferences("${PACKAGE_ID}.pref", Context.MODE_PRIVATE)
|
||||||
pref.edit().clear().apply()
|
pref.edit().clear().apply()
|
||||||
db = initDB(context)
|
db = initDB(context)
|
||||||
db.clearAllTables()
|
db.clearAllTables()
|
||||||
@ -81,7 +77,7 @@ class LoginActivityOnlineTest {
|
|||||||
.putString("clientID", "iwndoiuqwnd")
|
.putString("clientID", "iwndoiuqwnd")
|
||||||
.putString("clientSecret", "wlifowed")
|
.putString("clientSecret", "wlifowed")
|
||||||
.apply()
|
.apply()
|
||||||
val uri = Uri.parse("oauth2redirect://com.h.pixeldroid?code=sdfdqsf")
|
val uri = Uri.parse("oauth2redirect://${PACKAGE_ID}?code=sdfdqsf")
|
||||||
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
||||||
ActivityScenario.launch<LoginActivity>(intent)
|
ActivityScenario.launch<LoginActivity>(intent)
|
||||||
onView(withId(R.id.editText)).check(matches(
|
onView(withId(R.id.editText)).check(matches(
|
||||||
@ -91,7 +87,7 @@ class LoginActivityOnlineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun incompleteIntentReturnInfoFailsTest() {
|
fun incompleteIntentReturnInfoFailsTest() {
|
||||||
val uri = Uri.parse("oauth2redirect://com.h.pixeldroid?code=")
|
val uri = Uri.parse("oauth2redirect://${PACKAGE_ID}?code=")
|
||||||
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
||||||
ActivityScenario.launch<LoginActivity>(intent)
|
ActivityScenario.launch<LoginActivity>(intent)
|
||||||
onView(withId(R.id.editText)).check(matches(
|
onView(withId(R.id.editText)).check(matches(
|
||||||
@ -117,7 +113,7 @@ class LoginActivityOnlineTest {
|
|||||||
.putString("clientID", testiTesto.clientId)
|
.putString("clientID", testiTesto.clientId)
|
||||||
.putString("clientSecret", testiTesto.clientSecret)
|
.putString("clientSecret", testiTesto.clientSecret)
|
||||||
.apply()
|
.apply()
|
||||||
val uri = Uri.parse("oauth2redirect://com.h.pixeldroid?code=$testiTesto.")
|
val uri = Uri.parse("oauth2redirect://org.pixeldroid.app?code=$testiTesto.")
|
||||||
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
val intent = Intent(ACTION_VIEW, uri, context, LoginActivity::class.java)
|
||||||
ActivityScenario.launch<LoginActivity>(intent)
|
ActivityScenario.launch<LoginActivity>(intent)
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.ACTION_VIEW
|
import android.content.Intent.ACTION_VIEW
|
||||||
@ -15,12 +15,9 @@ import androidx.test.espresso.intent.matcher.IntentMatchers.hasDataString
|
|||||||
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import org.pixeldroid.app.BuildConfig.INSTANCE_URI
|
||||||
import androidx.test.rule.ActivityTestRule
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import androidx.test.uiautomator.UiDevice
|
import org.pixeldroid.app.testUtility.waitForView
|
||||||
import com.h.pixeldroid.BuildConfig.INSTANCE_URI
|
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
|
||||||
import com.h.pixeldroid.testUtility.waitForView
|
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.CoreMatchers.containsString
|
import org.hamcrest.CoreMatchers.containsString
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -11,8 +11,8 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
|
|||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.pixeldroid.app.testUtility.*
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
@ -7,26 +7,21 @@ import android.content.Intent
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
|
||||||
import android.view.View.VISIBLE
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.action.ViewActions.typeText
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||||
import com.h.pixeldroid.postCreation.photoEdit.ThumbnailAdapter
|
import org.pixeldroid.app.settings.AboutActivity
|
||||||
import com.h.pixeldroid.settings.AboutActivity
|
import org.pixeldroid.app.testUtility.*
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.rules.Timeout
|
import org.junit.rules.Timeout
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@ -44,7 +39,8 @@ class PostCreationActivityTest {
|
|||||||
val globalTimeout: Timeout = Timeout.seconds(30)
|
val globalTimeout: Timeout = Timeout.seconds(30)
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val mRuntimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
val mRuntimePermissionRule: GrantPermissionRule =
|
||||||
|
GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
|
||||||
private fun File.writeBitmap(bitmap: Bitmap) {
|
private fun File.writeBitmap(bitmap: Bitmap) {
|
||||||
outputStream().use { out ->
|
outputStream().use { out ->
|
||||||
@ -112,32 +108,101 @@ class PostCreationActivityTest {
|
|||||||
// should send on main activity
|
// should send on main activity
|
||||||
onView(withId(R.id.retry_upload_button)).check(matches(not(isDisplayed())))
|
onView(withId(R.id.retry_upload_button)).check(matches(not(isDisplayed())))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the [org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity] is launched
|
||||||
|
* when the edit button is pressed
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun editImage() {
|
fun editImage() {
|
||||||
Thread.sleep(1000)
|
waitForView(R.id.postTextInputLayout)
|
||||||
|
|
||||||
onView(withId(R.id.image_grid)).perform(
|
onView(withId(R.id.editPhotoButton)).perform(click())
|
||||||
RecyclerViewActions.actionOnItemAtPosition<PostCreationActivity.PostCreationAdapter.ViewHolder>(
|
|
||||||
0,
|
|
||||||
CustomMatchers.clickChildViewWithId(R.id.galleryImage)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Thread.sleep(1000)
|
|
||||||
|
|
||||||
onView(withId(R.id.recycler_view))
|
waitForView(R.id.cropImageButton)
|
||||||
.perform(
|
|
||||||
RecyclerViewActions.actionOnItemAtPosition<ThumbnailAdapter.MyViewHolder>(
|
|
||||||
2,
|
|
||||||
CustomMatchers.clickChildViewWithId(R.id.thumbnail)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Thread.sleep(1000)
|
|
||||||
onView(withId(R.id.action_save)).perform(click())
|
|
||||||
Thread.sleep(1000)
|
|
||||||
onView(withId(R.id.carousel)).check(matches(isDisplayed()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch from carousel to grid and back
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun carouselSwitch() {
|
||||||
|
waitForView(R.id.postTextInputLayout)
|
||||||
|
|
||||||
|
onView(withId(R.id.switchToGridButton)).perform(click())
|
||||||
|
|
||||||
|
waitForView(R.id.galleryImage)
|
||||||
|
|
||||||
|
onView(withId(R.id.switchToCarouselButton)).perform(click())
|
||||||
|
|
||||||
|
waitForView(R.id.btn_previous)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete images and check if it worked
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun deleteImages() {
|
||||||
|
waitForView(R.id.postTextInputLayout)
|
||||||
|
|
||||||
|
onView(withId(R.id.removePhotoButton)).perform(click()).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.switchToGridButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.galleryImage)).check(doesNotExist())
|
||||||
|
onView(withId(R.id.addPhotoSquare)).check(matches(isDisplayed()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type media description and check it's saved
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun mediaDescription() {
|
||||||
|
waitForView(R.id.postTextInputLayout)
|
||||||
|
|
||||||
|
|
||||||
|
fun typeDescription(text: String) {
|
||||||
|
onView(withId(R.id.tv_caption)).perform(click())
|
||||||
|
|
||||||
|
waitForView(R.id.editTextMediaDescription)
|
||||||
|
|
||||||
|
onView(withId(R.id.editTextMediaDescription)).perform(typeText(text))
|
||||||
|
onView(withId(R.id.imageDescriptionButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.tv_caption)).check(matches(withText(text)))
|
||||||
|
}
|
||||||
|
|
||||||
|
val typedText1 = "Testi testo description"
|
||||||
|
typeDescription(typedText1)
|
||||||
|
|
||||||
|
onView(withId(R.id.btn_next)).perform(click())
|
||||||
|
|
||||||
|
val typedText2 = "Description 2"
|
||||||
|
typeDescription(typedText2)
|
||||||
|
|
||||||
|
onView(withId(R.id.btn_previous)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.tv_caption)).check(matches(withText(typedText1)))
|
||||||
|
|
||||||
|
onView(withId(R.id.btn_next)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.tv_caption)).check(matches(withText(typedText2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the [org.pixeldroid.app.postCreation.camera.CameraActivity] is launched
|
||||||
|
* when the add image button is pressed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun addImage() {
|
||||||
|
waitForView(R.id.postTextInputLayout)
|
||||||
|
|
||||||
|
onView(withId(R.id.addPhotoButton)).perform(click())
|
||||||
|
|
||||||
|
waitForView(R.id.camera_activity_fragment)
|
||||||
|
}
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun cancelEdit() {
|
fun cancelEdit() {
|
||||||
onView(withId(R.id.image_grid)).perform(
|
onView(withId(R.id.image_grid)).perform(
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -17,8 +17,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.rule.GrantPermissionRule
|
import androidx.test.rule.GrantPermissionRule
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.pixeldroid.app.testUtility.*
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -13,16 +13,14 @@ import androidx.test.espresso.matcher.ViewMatchers.*
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
import com.h.pixeldroid.BuildConfig.INSTANCE_URI
|
import org.pixeldroid.app.BuildConfig.INSTANCE_URI
|
||||||
import com.h.pixeldroid.posts.PostActivity
|
import org.pixeldroid.app.posts.PostActivity
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.api.objects.*
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.testUtility.clearData
|
||||||
import com.h.pixeldroid.utils.api.objects.*
|
import org.pixeldroid.app.testUtility.initDB
|
||||||
import com.h.pixeldroid.testUtility.clearData
|
import org.pixeldroid.app.testUtility.testiTesto
|
||||||
import com.h.pixeldroid.testUtility.initDB
|
import org.pixeldroid.app.testUtility.testiTestoInstance
|
||||||
import com.h.pixeldroid.testUtility.testiTesto
|
|
||||||
import com.h.pixeldroid.testUtility.testiTestoInstance
|
|
||||||
import org.hamcrest.CoreMatchers.anyOf
|
import org.hamcrest.CoreMatchers.anyOf
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.junit.*
|
import org.junit.*
|
@ -1,29 +1,24 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.espresso.Espresso
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
import androidx.test.espresso.action.ViewActions.swipeDown
|
import androidx.test.espresso.action.ViewActions.swipeDown
|
||||||
import androidx.test.espresso.assertion.ViewAssertions
|
import androidx.test.espresso.assertion.ViewAssertions
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
import org.pixeldroid.app.profile.ProfileActivity
|
||||||
import com.h.pixeldroid.profile.ProfileActivity
|
import org.pixeldroid.app.testUtility.*
|
||||||
import com.h.pixeldroid.testUtility.*
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
|
||||||
import okhttp3.internal.wait
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ProfileTest {
|
class ProfileTest {
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.testUtility
|
package org.pixeldroid.app.testUtility
|
||||||
|
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
@ -8,6 +8,7 @@ import android.widget.TextView
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.*
|
import androidx.test.espresso.*
|
||||||
|
import androidx.test.espresso.NoMatchingViewException
|
||||||
import androidx.test.espresso.action.*
|
import androidx.test.espresso.action.*
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.matcher.BoundedMatcher
|
import androidx.test.espresso.matcher.BoundedMatcher
|
||||||
@ -15,15 +16,43 @@ import androidx.test.espresso.matcher.ViewMatchers
|
|||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.espresso.util.HumanReadables
|
import androidx.test.espresso.util.HumanReadables
|
||||||
import androidx.test.espresso.util.TreeIterables
|
import androidx.test.espresso.util.TreeIterables
|
||||||
import com.h.pixeldroid.R
|
|
||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
|
import org.junit.rules.TestRule
|
||||||
|
import org.junit.runners.model.Statement
|
||||||
|
import org.pixeldroid.app.R
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS)
|
||||||
|
annotation class RepeatTest(val value: Int = 1)
|
||||||
|
|
||||||
|
class RepeatRule : TestRule {
|
||||||
|
|
||||||
|
private class RepeatStatement(private val statement: Statement, private val repeat: Int) : Statement() {
|
||||||
|
@Throws(Throwable::class)
|
||||||
|
override fun evaluate() {
|
||||||
|
for (i in 0 until repeat) {
|
||||||
|
statement.evaluate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun apply(statement: Statement, description: org.junit.runner.Description): Statement {
|
||||||
|
var result = statement
|
||||||
|
val repeat = description.getAnnotation(RepeatTest::class.java)
|
||||||
|
if (repeat != null) {
|
||||||
|
val times = repeat.value
|
||||||
|
result = RepeatStatement(statement, times)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun ViewInteraction.isDisplayed(): Boolean {
|
fun ViewInteraction.isDisplayed(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
check(matches(ViewMatchers.isDisplayed()))
|
check(matches(ViewMatchers.isDisplayed()))
|
||||||
@ -38,15 +67,15 @@ fun ViewInteraction.isDisplayed(): Boolean {
|
|||||||
* Doesn't work if the root changes (since it operates on the root!)
|
* Doesn't work if the root changes (since it operates on the root!)
|
||||||
* @param viewId The id of the view to wait for.
|
* @param viewId The id of the view to wait for.
|
||||||
*/
|
*/
|
||||||
fun waitForView(viewId: Int) {
|
fun waitForView(viewId: Int, viewMatcher: Matcher<View> = withId(viewId)) {
|
||||||
Espresso.onView(isRoot()).perform(waitForViewViewAction(viewId))
|
Espresso.onView(isRoot()).perform(waitForViewViewAction(viewId, viewMatcher))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This ViewAction tells espresso to wait till a certain view is found in the view hierarchy.
|
* This ViewAction tells espresso to wait till a certain view is found in the view hierarchy.
|
||||||
* @param viewId The id of the view to wait for.
|
* @param viewId The id of the view to wait for.
|
||||||
*/
|
*/
|
||||||
private fun waitForViewViewAction(viewId: Int): ViewAction {
|
private fun waitForViewViewAction(viewId: Int, viewMatcher: Matcher<View>): ViewAction {
|
||||||
// The maximum time which espresso will wait for the view to show up (in milliseconds)
|
// The maximum time which espresso will wait for the view to show up (in milliseconds)
|
||||||
val timeOut = 5000
|
val timeOut = 5000
|
||||||
return object : ViewAction {
|
return object : ViewAction {
|
||||||
@ -62,7 +91,6 @@ private fun waitForViewViewAction(viewId: Int): ViewAction {
|
|||||||
uiController.loopMainThreadUntilIdle()
|
uiController.loopMainThreadUntilIdle()
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
val endTime = startTime + timeOut
|
val endTime = startTime + timeOut
|
||||||
val viewMatcher = withId(viewId)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Iterate through all views on the screen and see if the view we are looking for is there already
|
// Iterate through all views on the screen and see if the view we are looking for is there already
|
||||||
@ -75,7 +103,7 @@ private fun waitForViewViewAction(viewId: Int): ViewAction {
|
|||||||
// Loops the main thread for a specified period of time.
|
// Loops the main thread for a specified period of time.
|
||||||
// Control may not return immediately, instead it'll return after the provided delay has passed and the queue is in an idle state again.
|
// Control may not return immediately, instead it'll return after the provided delay has passed and the queue is in an idle state again.
|
||||||
uiController.loopMainThreadForAtLeast(100)
|
uiController.loopMainThreadForAtLeast(100)
|
||||||
} while (System.currentTimeMillis() < endTime) // in case of a timeout we throw an exception -> test fails
|
} while (System.currentTimeMillis() < endTime) // in case of a timeout we throw an exception - test fails
|
||||||
throw PerformException.Builder()
|
throw PerformException.Builder()
|
||||||
.withCause(TimeoutException())
|
.withCause(TimeoutException())
|
||||||
.withActionDescription(this.description)
|
.withActionDescription(this.description)
|
||||||
@ -271,6 +299,54 @@ fun clickChildViewWithId(id: Int) = object : ViewAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clickClickableSpan(textToClick: CharSequence): ViewAction? {
|
||||||
|
return object : ViewAction {
|
||||||
|
override fun getConstraints(): Matcher<View> {
|
||||||
|
return Matchers.instanceOf(TextView::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDescription(): String {
|
||||||
|
return "clicking on a ClickableSpan"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun perform(uiController: UiController?, view: View) {
|
||||||
|
val textView = view as TextView
|
||||||
|
val spannableString = textView.text as SpannableString
|
||||||
|
if (spannableString.isEmpty()) {
|
||||||
|
// TextView is empty, nothing to do
|
||||||
|
throw NoMatchingViewException.Builder()
|
||||||
|
.includeViewHierarchy(true)
|
||||||
|
.withRootView(textView)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the links inside the TextView and check if we find textToClick
|
||||||
|
val spans = spannableString.getSpans(
|
||||||
|
0, spannableString.length,
|
||||||
|
ClickableSpan::class.java
|
||||||
|
)
|
||||||
|
if (spans.isNotEmpty()) {
|
||||||
|
var spanCandidate: ClickableSpan?
|
||||||
|
for (span in spans) {
|
||||||
|
spanCandidate = span
|
||||||
|
val start = spannableString.getSpanStart(spanCandidate)
|
||||||
|
val end = spannableString.getSpanEnd(spanCandidate)
|
||||||
|
val sequence = spannableString.subSequence(start, end)
|
||||||
|
if (textToClick.toString() == sequence.toString()) {
|
||||||
|
span.onClick(textView)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NoMatchingViewException.Builder()
|
||||||
|
.includeViewHierarchy(true)
|
||||||
|
.withRootView(textView)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction {
|
fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction {
|
||||||
|
|
||||||
override fun getConstraints() = null
|
override fun getConstraints() = null
|
@ -1,8 +1,8 @@
|
|||||||
package com.h.pixeldroid.testUtility
|
package org.pixeldroid.app.testUtility
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.ligi.tracedroid.TraceDroid
|
import org.ligi.tracedroid.TraceDroid
|
||||||
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
package org.pixeldroid.app.testUtility
|
||||||
|
/*
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.test.runner.AndroidJUnitRunner
|
||||||
|
import com.linkedin.android.testbutler.TestButler
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunner: AndroidJUnitRunner() {
|
||||||
|
override fun onStart() {
|
||||||
|
TestButler.setup(targetContext)
|
||||||
|
super.onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish(resultCode: Int, results: Bundle) {
|
||||||
|
TestButler.teardown(targetContext)
|
||||||
|
super.finish(resultCode, results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
@ -1,8 +1,12 @@
|
|||||||
package com.h.pixeldroid.testUtility
|
package org.pixeldroid.app.testUtility
|
||||||
|
|
||||||
|
import org.pixeldroid.app.BuildConfig.*
|
||||||
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
|
||||||
|
|
||||||
|
const val PACKAGE_ID = APPLICATION_ID
|
||||||
|
|
||||||
import com.h.pixeldroid.BuildConfig.*
|
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
|
||||||
|
|
||||||
val testiTestoInstance = InstanceDatabaseEntity(
|
val testiTestoInstance = InstanceDatabaseEntity(
|
||||||
uri = INSTANCE_URI,
|
uri = INSTANCE_URI,
|
38
app/src/debug/res/drawable-v24/ic_launcher_foreground.xml
Normal file
38
app/src/debug/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<group
|
||||||
|
android:name="foo"
|
||||||
|
android:scaleX="2"
|
||||||
|
android:scaleY="2"
|
||||||
|
android:translateY="0"
|
||||||
|
android:translateX="0">
|
||||||
|
<path
|
||||||
|
android:pathData="M23.0332,30.2725L27.5781,30.2725C31.8595,30.2725 35.3302,26.9088 35.3302,22.7596C35.3302,18.6103 31.8595,15.2467 27.5781,15.2467L21.0185,15.2467C18.5485,15.2467 16.5461,17.1872 16.5461,19.581L16.5461,36.451L23.0332,30.2725Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:strokeColor="#00000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
74
app/src/debug/res/drawable/ic_launcher_background.xml
Normal file
74
app/src/debug/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
android:height="108dp"
|
||||||
|
android:width="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
</vector>
|
5
app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
5
app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -1,7 +1,7 @@
|
|||||||
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.h.pixeldroid">
|
package="org.pixeldroid.app">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
@ -23,7 +23,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
<activity android:name="com.h.pixeldroid.postCreation.camera.CameraActivity" />
|
<activity android:name=".postCreation.camera.CameraActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.ReportActivity"
|
android:name=".posts.ReportActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
@ -33,11 +33,26 @@
|
|||||||
android:name=".postCreation.PostCreationActivity"
|
android:name=".postCreation.PostCreationActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".profile.FollowsActivity"
|
android:name=".profile.FollowsActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".posts.feeds.uncachedFeeds.hashtags.HashTagActivity"
|
||||||
|
android:screenOrientation="sensorPortrait"
|
||||||
|
tools:ignore="LockedOrientationActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.PostActivity"
|
android:name=".posts.PostActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
@ -66,7 +81,7 @@
|
|||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.default_searchable"
|
android:name="android.app.default_searchable"
|
||||||
android:value=".searchDiscover.SearchActivity" />
|
android:value="org.pixeldroid.app.searchDiscover.SearchActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".LoginActivity"
|
android:name=".LoginActivity"
|
||||||
@ -116,7 +131,7 @@
|
|||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.h.pixeldroid.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
6
app/src/main/assets/licenses.html
generated
6
app/src/main/assets/licenses.html
generated
@ -818,9 +818,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="library">
|
<div class="library">
|
||||||
<!-- https://opensource.org/licenses/Apache-2.0 -->
|
<!-- https://opensource.org/licenses/Apache-2.0 -->
|
||||||
<h1 class="title">kotlin-android-extensions-runtime</h1>
|
<h1 class="title">startup-runtime</h1>
|
||||||
<p class="notice">Copyright © JetBrains s.r.o. and contributors. All rights reserved.</p>
|
<p class="notice">Copyright © Google Inc. All rights reserved.</p>
|
||||||
<p><a href="https://kotlinlang.org/">https://kotlinlang.org/</a></p>
|
<p><a href="https://developer.android.com/jetpack/androidx/releases/startup#1.0.0">https://developer.android.com/jetpack/androidx/releases/startup#1.0.0</a></p>
|
||||||
<input type="checkbox"><label></label>
|
<input type="checkbox"><label></label>
|
||||||
<div class="license">
|
<div class="license">
|
||||||
<h2>
|
<h2>
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.paging.*
|
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ViewModel for the uncached feeds.
|
|
||||||
* The ViewModel works with the different [UncachedContentRepository]s to get the data.
|
|
||||||
*/
|
|
||||||
class FeedViewModel<T: FeedContent>(private val repository: UncachedContentRepository<T>) : ViewModel() {
|
|
||||||
|
|
||||||
private var currentResult: Flow<PagingData<T>>? = null
|
|
||||||
|
|
||||||
fun flow(): Flow<PagingData<T>> {
|
|
||||||
val lastResult = currentResult
|
|
||||||
if (lastResult != null) {
|
|
||||||
return lastResult
|
|
||||||
}
|
|
||||||
val newResult: Flow<PagingData<T>> = repository.getStream()
|
|
||||||
.cachedIn(viewModelScope)
|
|
||||||
currentResult = newResult
|
|
||||||
return newResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common interface for the different uncached feeds
|
|
||||||
*/
|
|
||||||
interface UncachedContentRepository<T: FeedContent>{
|
|
||||||
fun getStream(): Flow<PagingData<T>>
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.search
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.paging.ExperimentalPagingApi
|
|
||||||
import androidx.paging.PagingDataAdapter
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.h.pixeldroid.R
|
|
||||||
import com.h.pixeldroid.posts.StatusViewHolder
|
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.*
|
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
|
||||||
import com.h.pixeldroid.utils.displayDimensionsInPx
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment to show a list of [Status]es, as a result of a search.
|
|
||||||
*/
|
|
||||||
class SearchPostsFragment : UncachedFeedFragment<Status>() {
|
|
||||||
|
|
||||||
private lateinit var query: String
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
|
||||||
|
|
||||||
query = arguments?.getSerializable("searchFeed") as String
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExperimentalPagingApi
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
|
||||||
|
|
||||||
// get the view model
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
|
||||||
SearchContentRepository<Status>(
|
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
|
||||||
Results.SearchType.statuses,
|
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
|
||||||
query
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<Status>
|
|
||||||
|
|
||||||
launch()
|
|
||||||
initSearch()
|
|
||||||
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<Status, RecyclerView.ViewHolder>(
|
|
||||||
object : DiffUtil.ItemCallback<Status>() {
|
|
||||||
override fun areItemsTheSame(oldItem: Status, newItem: Status): Boolean {
|
|
||||||
return oldItem.id == newItem.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Status, newItem: Status): Boolean =
|
|
||||||
oldItem.id == newItem.id
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
|
||||||
return StatusViewHolder.create(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
|
||||||
return R.layout.post_fragment
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
||||||
val uiModel = getItem(position) as Status
|
|
||||||
uiModel.let {
|
|
||||||
(holder as StatusViewHolder).bind(it, apiHolder.setDomainToCurrentUser(db), db, lifecycleScope, displayDimensionsInPx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.h.pixeldroid.utils.api.objects
|
|
||||||
|
|
||||||
data class Error(
|
|
||||||
val error: String?
|
|
||||||
)
|
|
@ -1,18 +0,0 @@
|
|||||||
package com.h.pixeldroid.utils.api.objects
|
|
||||||
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
data class Relationship(
|
|
||||||
// Required atributes
|
|
||||||
val id: String,
|
|
||||||
val following: Boolean,
|
|
||||||
val requested: Boolean,
|
|
||||||
val endorsed: Boolean,
|
|
||||||
val followed_by: Boolean,
|
|
||||||
val muting: Boolean,
|
|
||||||
val muting_notifications: Boolean,
|
|
||||||
val showing_reblogs: Boolean,
|
|
||||||
val blocking: Boolean,
|
|
||||||
val domain_blocking: Boolean,
|
|
||||||
val blocked_by: Boolean
|
|
||||||
) : Serializable
|
|
@ -1,15 +0,0 @@
|
|||||||
package com.h.pixeldroid.utils.api.objects
|
|
||||||
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
data class Tag(
|
|
||||||
//Base attributes
|
|
||||||
val name: String,
|
|
||||||
val url: String,
|
|
||||||
//Optional attributes
|
|
||||||
val history: List<History>? = emptyList()) : Serializable, FeedContent {
|
|
||||||
//needed to be a FeedContent, this inheritance is a bit fickle. Do not use.
|
|
||||||
override val id: String
|
|
||||||
get() = "tag"
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -9,12 +9,12 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.h.pixeldroid.databinding.ActivityLoginBinding
|
import org.pixeldroid.app.databinding.ActivityLoginBinding
|
||||||
import com.h.pixeldroid.utils.*
|
import org.pixeldroid.app.utils.*
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.*
|
import org.pixeldroid.app.utils.api.objects.*
|
||||||
import com.h.pixeldroid.utils.db.addUser
|
import org.pixeldroid.app.utils.db.addUser
|
||||||
import com.h.pixeldroid.utils.db.storeInstance
|
import org.pixeldroid.app.utils.db.storeInstance
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -25,14 +25,13 @@ Overview of the flow of the login process: (boxes are requests done in parallel,
|
|||||||
since they do not depend on each other)
|
since they do not depend on each other)
|
||||||
|
|
||||||
_________________________________
|
_________________________________
|
||||||
|[PixelfedAPI.registerApplicationAsync]|
|
|[PixelfedAPI.registerApplication]|
|
||||||
|[PixelfedAPI.wellKnownNodeInfo] |
|
|[PixelfedAPI.wellKnownNodeInfo] |
|
||||||
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.nodeInfoSchema]
|
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅
|
||||||
+----> [promptOAuth]
|
+----> [PixelfedAPI.nodeInfoSchema] (and then [PixelfedAPI.instance] if needed)
|
||||||
+---->____________________________
|
+----> [promptOAuth]
|
||||||
|[PixelfedAPI.instance] |
|
+----> [PixelfedAPI.obtainToken]
|
||||||
|[PixelfedAPI.obtainToken] |
|
+----> [PixelfedAPI.verifyCredentials]
|
||||||
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.verifyCredentials]
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -311,7 +310,7 @@ class LoginActivity : BaseActivity() {
|
|||||||
clientId = clientId,
|
clientId = clientId,
|
||||||
clientSecret = clientSecret
|
clientSecret = clientSecret
|
||||||
)
|
)
|
||||||
apiHolder.setDomainToCurrentUser(db)
|
apiHolder.setToCurrentUser()
|
||||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
startActivity(intent)
|
startActivity(intent)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -14,22 +14,12 @@ import androidx.core.view.GravityCompat
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.h.pixeldroid.databinding.ActivityMainBinding
|
|
||||||
import com.h.pixeldroid.postCreation.camera.CameraFragment
|
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.notifications.NotificationsFragment
|
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment
|
|
||||||
import com.h.pixeldroid.profile.ProfileActivity
|
|
||||||
import com.h.pixeldroid.searchDiscover.SearchDiscoverFragment
|
|
||||||
import com.h.pixeldroid.settings.SettingsActivity
|
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
|
||||||
import com.h.pixeldroid.utils.db.addUser
|
|
||||||
import com.h.pixeldroid.utils.db.entities.HomeStatusDatabaseEntity
|
|
||||||
import com.h.pixeldroid.utils.db.entities.PublicFeedStatusDatabaseEntity
|
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
|
||||||
import com.h.pixeldroid.utils.hasInternet
|
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||||
import com.mikepenz.materialdrawer.iconics.iconicsIcon
|
import com.mikepenz.materialdrawer.iconics.iconicsIcon
|
||||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||||
@ -40,9 +30,25 @@ import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
|||||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||||
import org.ligi.tracedroid.sending.sendTraceDroidStackTracesIfExist
|
import org.ligi.tracedroid.sending.sendTraceDroidStackTracesIfExist
|
||||||
|
import org.pixeldroid.app.databinding.ActivityMainBinding
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||||
|
import org.pixeldroid.app.posts.NestedScrollableHost
|
||||||
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||||
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.notifications.NotificationsFragment
|
||||||
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment
|
||||||
|
import org.pixeldroid.app.profile.ProfileActivity
|
||||||
|
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||||
|
import org.pixeldroid.app.settings.SettingsActivity
|
||||||
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
import org.pixeldroid.app.utils.db.addUser
|
||||||
|
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
import org.pixeldroid.app.utils.hasInternet
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var header: AccountHeaderView
|
private lateinit var header: AccountHeaderView
|
||||||
@ -72,23 +78,22 @@ class MainActivity : BaseActivity() {
|
|||||||
sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this)
|
sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this)
|
||||||
|
|
||||||
setupDrawer()
|
setupDrawer()
|
||||||
|
|
||||||
val tabs: List<() -> Fragment> = listOf(
|
val tabs: List<() -> Fragment> = listOf(
|
||||||
{
|
{
|
||||||
PostFeedFragment<HomeStatusDatabaseEntity>()
|
PostFeedFragment<HomeStatusDatabaseEntity>()
|
||||||
.apply {
|
.apply {
|
||||||
arguments = Bundle().apply { putBoolean("home", true) }
|
arguments = Bundle().apply { putBoolean("home", true) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ SearchDiscoverFragment() },
|
{ SearchDiscoverFragment() },
|
||||||
{ CameraFragment() },
|
{ CameraFragment() },
|
||||||
{ NotificationsFragment() },
|
{ NotificationsFragment() },
|
||||||
{
|
{
|
||||||
PostFeedFragment<PublicFeedStatusDatabaseEntity>()
|
PostFeedFragment<PublicFeedStatusDatabaseEntity>()
|
||||||
.apply {
|
.apply {
|
||||||
arguments = Bundle().apply { putBoolean("home", false) }
|
arguments = Bundle().apply { putBoolean("home", false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
setupTabs(tabs)
|
setupTabs(tabs)
|
||||||
}
|
}
|
||||||
@ -177,7 +182,7 @@ class MainActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
val newActive = remainingUsers.first()
|
val newActive = remainingUsers.first()
|
||||||
db.userDao().activateUser(newActive.user_id)
|
db.userDao().activateUser(newActive.user_id)
|
||||||
apiHolder.setDomainToCurrentUser(db)
|
apiHolder.setToCurrentUser()
|
||||||
//relaunch the app
|
//relaunch the app
|
||||||
launchActivity(MainActivity(), firstTime = true)
|
launchActivity(MainActivity(), firstTime = true)
|
||||||
}
|
}
|
||||||
@ -185,16 +190,17 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
private fun getUpdatedAccount() {
|
private fun getUpdatedAccount() {
|
||||||
if (hasInternet(applicationContext)) {
|
if (hasInternet(applicationContext)) {
|
||||||
val domain = user?.instance_uri.orEmpty()
|
|
||||||
val accessToken = user?.accessToken.orEmpty()
|
|
||||||
val refreshToken = user?.refreshToken
|
|
||||||
val clientId = user?.clientId.orEmpty()
|
|
||||||
val clientSecret = user?.clientSecret.orEmpty()
|
|
||||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
try {
|
try {
|
||||||
val account = api.verifyCredentials("Bearer $accessToken")
|
val domain = user?.instance_uri.orEmpty()
|
||||||
|
val accessToken = user?.accessToken.orEmpty()
|
||||||
|
val refreshToken = user?.refreshToken
|
||||||
|
val clientId = user?.clientId.orEmpty()
|
||||||
|
val clientSecret = user?.clientSecret.orEmpty()
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
|
val account = api.verifyCredentials()
|
||||||
addUser(db, account, domain, accessToken = accessToken, refreshToken = refreshToken, clientId = clientId, clientSecret = clientSecret)
|
addUser(db, account, domain, accessToken = accessToken, refreshToken = refreshToken, clientId = clientId, clientSecret = clientSecret)
|
||||||
fillDrawerAccountInfo(account.id!!)
|
fillDrawerAccountInfo(account.id!!)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
@ -220,7 +226,7 @@ class MainActivity : BaseActivity() {
|
|||||||
|
|
||||||
db.userDao().deActivateActiveUsers()
|
db.userDao().deActivateActiveUsers()
|
||||||
db.userDao().activateUser(profile.identifier.toString())
|
db.userDao().activateUser(profile.identifier.toString())
|
||||||
apiHolder.setDomainToCurrentUser(db)
|
apiHolder.setToCurrentUser()
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -268,8 +274,22 @@ class MainActivity : BaseActivity() {
|
|||||||
header.setActiveProfile(account.toLong())
|
header.setActiveProfile(account.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use reflection to make it a bit harder to swipe between tabs
|
||||||
|
*/
|
||||||
|
private fun ViewPager2.reduceDragSensitivity() {
|
||||||
|
val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
|
||||||
|
recyclerViewField.isAccessible = true
|
||||||
|
val recyclerView = recyclerViewField.get(this) as RecyclerView
|
||||||
|
|
||||||
|
val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
|
||||||
|
touchSlopField.isAccessible = true
|
||||||
|
val touchSlop = touchSlopField.get(recyclerView) as Int
|
||||||
|
touchSlopField.set(recyclerView, touchSlop*NestedScrollableHost.touchSlopModifier)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupTabs(tab_array: List<() -> Fragment>){
|
private fun setupTabs(tab_array: List<() -> Fragment>){
|
||||||
|
binding.viewPager.reduceDragSensitivity()
|
||||||
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
|
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return tab_array[position]()
|
return tab_array[position]()
|
||||||
@ -279,6 +299,20 @@ class MainActivity : BaseActivity() {
|
|||||||
return tab_array.size
|
return tab_array.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||||
|
override fun onTabSelected(tab: TabLayout.Tab?){}
|
||||||
|
|
||||||
|
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||||
|
|
||||||
|
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||||
|
tab?.position?.let { position ->
|
||||||
|
val page =
|
||||||
|
//No clue why this works but it does. F to pay respects
|
||||||
|
supportFragmentManager.findFragmentByTag("f$position")
|
||||||
|
(page as? CachedFeedFragment<*>)?.onTabReClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
|
TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
|
||||||
tab.icon = ContextCompat.getDrawable(applicationContext,
|
tab.icon = ContextCompat.getDrawable(applicationContext,
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation
|
package org.pixeldroid.app.postCreation
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
@ -16,29 +16,30 @@ import android.view.View.INVISIBLE
|
|||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.h.pixeldroid.MainActivity
|
import org.pixeldroid.app.MainActivity
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
|
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||||
import com.h.pixeldroid.postCreation.camera.CameraActivity
|
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||||
import com.h.pixeldroid.postCreation.carousel.CarouselItem
|
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
||||||
import com.h.pixeldroid.postCreation.carousel.ImageCarousel
|
import org.pixeldroid.app.postCreation.carousel.ImageCarousel
|
||||||
import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
|
import org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -58,9 +59,6 @@ data class PhotoData(
|
|||||||
|
|
||||||
class PostCreationActivity : BaseActivity() {
|
class PostCreationActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var accessToken: String
|
|
||||||
private lateinit var pixelfedAPI: PixelfedAPI
|
|
||||||
|
|
||||||
private var user: UserDatabaseEntity? = null
|
private var user: UserDatabaseEntity? = null
|
||||||
private lateinit var instance: InstanceDatabaseEntity
|
private lateinit var instance: InstanceDatabaseEntity
|
||||||
|
|
||||||
@ -86,9 +84,6 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
// get image URIs
|
// get image URIs
|
||||||
intent.clipData?.let { addPossibleImages(it) }
|
intent.clipData?.let { addPossibleImages(it) }
|
||||||
|
|
||||||
accessToken = user?.accessToken.orEmpty()
|
|
||||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
|
||||||
|
|
||||||
val carousel: ImageCarousel = binding.carousel
|
val carousel: ImageCarousel = binding.carousel
|
||||||
carousel.addData(photoData.map { CarouselItem(it.imageUri) })
|
carousel.addData(photoData.map { CarouselItem(it.imageUri) })
|
||||||
carousel.layoutCarouselCallback = {
|
carousel.layoutCarouselCallback = {
|
||||||
@ -105,7 +100,7 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
addPhoto()
|
addPhoto()
|
||||||
}
|
}
|
||||||
carousel.updateDescriptionCallback = { position: Int, description: String ->
|
carousel.updateDescriptionCallback = { position: Int, description: String ->
|
||||||
photoData[position].imageDescription = description
|
photoData.getOrNull(position)?.imageDescription = description
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the description and send the post
|
// get the description and send the post
|
||||||
@ -319,7 +314,16 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
|
|
||||||
for (data: PhotoData in photoData) {
|
for (data: PhotoData in photoData) {
|
||||||
val imageUri = data.imageUri
|
val imageUri = data.imageUri
|
||||||
val imageInputStream = contentResolver.openInputStream(imageUri)!!
|
val imageInputStream = try {
|
||||||
|
contentResolver.openInputStream(imageUri)!!
|
||||||
|
} catch (e: FileNotFoundException){
|
||||||
|
AlertDialog.Builder(this).apply {
|
||||||
|
setMessage(getString(R.string.file_not_found).format(imageUri))
|
||||||
|
|
||||||
|
setNegativeButton(android.R.string.ok) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val imagePart = ProgressRequestBody(imageInputStream, data.size)
|
val imagePart = ProgressRequestBody(imageInputStream, data.size)
|
||||||
val requestBody = MultipartBody.Builder()
|
val requestBody = MultipartBody.Builder()
|
||||||
@ -339,8 +343,8 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
|
|
||||||
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
|
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
|
||||||
|
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
val inter = pixelfedAPI.mediaUpload("Bearer $accessToken", description, requestBody.parts[0])
|
val inter = api.mediaUpload(description, requestBody.parts[0])
|
||||||
|
|
||||||
postSub = inter
|
postSub = inter
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
@ -382,8 +386,9 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
enableButton(false)
|
enableButton(false)
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
try {
|
try {
|
||||||
pixelfedAPI.postStatus(
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
authorization = "Bearer $accessToken",
|
|
||||||
|
api.postStatus(
|
||||||
statusText = description,
|
statusText = description,
|
||||||
media_ids = photoData.mapNotNull { it.uploadId }.toList()
|
media_ids = photoData.mapNotNull { it.uploadId }.toList()
|
||||||
)
|
)
|
||||||
@ -418,18 +423,18 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editResultContract(position: Int) = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
|
private val editResultContract: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
|
||||||
result: ActivityResult? ->
|
result: ActivityResult? ->
|
||||||
if (result?.resultCode == Activity.RESULT_OK && result.data != null) {
|
if (result?.resultCode == Activity.RESULT_OK && result.data != null) {
|
||||||
photoData[position].apply {
|
val position: Int = result.data!!.getIntExtra(PhotoEditActivity.PICTURE_POSITION, 0)
|
||||||
imageUri = result.data!!.getStringExtra("result")!!.toUri()
|
photoData.getOrNull(position)?.apply {
|
||||||
|
imageUri = result.data!!.getStringExtra(PhotoEditActivity.PICTURE_URI)!!.toUri()
|
||||||
size = imageUri.getSize()
|
size = imageUri.getSize()
|
||||||
}
|
progress = null
|
||||||
|
uploadId = null
|
||||||
|
} ?: Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
binding.carousel.addData(photoData.map { CarouselItem(it.imageUri, it.imageDescription) })
|
binding.carousel.addData(photoData.map { CarouselItem(it.imageUri, it.imageDescription) })
|
||||||
|
|
||||||
photoData[position].progress = null
|
|
||||||
photoData[position].uploadId = null
|
|
||||||
} else if(result?.resultCode != Activity.RESULT_CANCELED){
|
} else if(result?.resultCode != Activity.RESULT_CANCELED){
|
||||||
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@ -437,8 +442,8 @@ class PostCreationActivity : BaseActivity() {
|
|||||||
|
|
||||||
private fun edit(position: Int) {
|
private fun edit(position: Int) {
|
||||||
val intent = Intent(this, PhotoEditActivity::class.java)
|
val intent = Intent(this, PhotoEditActivity::class.java)
|
||||||
.putExtra("picture_uri", photoData[position].imageUri)
|
.putExtra(PhotoEditActivity.PICTURE_URI, photoData[position].imageUri)
|
||||||
.putExtra("no upload", false)
|
.putExtra(PhotoEditActivity.PICTURE_POSITION, position)
|
||||||
editResultContract(position).launch(intent)
|
editResultContract.launch(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation
|
package org.pixeldroid.app.postCreation
|
||||||
|
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.subjects.PublishSubject
|
import io.reactivex.subjects.PublishSubject
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation
|
package org.pixeldroid.app.postCreation
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
@ -1,8 +1,8 @@
|
|||||||
package com.h.pixeldroid.postCreation.camera
|
package org.pixeldroid.app.postCreation.camera
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
|
|
||||||
class CameraActivity : BaseActivity() {
|
class CameraActivity : BaseActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -20,9 +20,4 @@ class CameraActivity : BaseActivity() {
|
|||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.camera
|
package org.pixeldroid.app.postCreation.camera
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
@ -31,8 +31,8 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.carousel
|
package org.pixeldroid.app.postCreation.carousel
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -9,7 +9,7 @@ import androidx.annotation.IdRes
|
|||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
|
|
||||||
|
|
||||||
class CarouselAdapter(
|
class CarouselAdapter(
|
||||||
@ -96,8 +96,10 @@ class CarouselAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateDescription(position: Int, description: String) {
|
fun updateDescription(position: Int, description: String) {
|
||||||
dataList[position] = dataList[position].copy(caption = description)
|
dataList.getOrNull(position)?.apply {
|
||||||
notifyItemChanged(position)
|
dataList[position] = copy(caption = description)
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addAll(dataList: List<CarouselItem>) {
|
fun addAll(dataList: List<CarouselItem>) {
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.carousel
|
package org.pixeldroid.app.postCreation.carousel
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.carousel
|
package org.pixeldroid.app.postCreation.carousel
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.carousel
|
package org.pixeldroid.app.postCreation.carousel
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
@ -15,8 +15,8 @@ import androidx.annotation.LayoutRes
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.*
|
import androidx.recyclerview.widget.*
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ImageCarouselBinding
|
import org.pixeldroid.app.databinding.ImageCarouselBinding
|
||||||
import me.relex.circleindicator.CircleIndicator2
|
import me.relex.circleindicator.CircleIndicator2
|
||||||
import org.jetbrains.annotations.NotNull
|
import org.jetbrains.annotations.NotNull
|
||||||
import org.jetbrains.annotations.Nullable
|
import org.jetbrains.annotations.Nullable
|
||||||
@ -44,7 +44,6 @@ class ImageCarousel(
|
|||||||
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
private lateinit var tvCaption: TextView
|
private lateinit var tvCaption: TextView
|
||||||
private lateinit var editTextMediaDescription: EditText
|
|
||||||
private var snapHelper: SnapHelper = PagerSnapHelper()
|
private var snapHelper: SnapHelper = PagerSnapHelper()
|
||||||
|
|
||||||
var indicator: CircleIndicator2? = null
|
var indicator: CircleIndicator2? = null
|
||||||
@ -292,9 +291,9 @@ class ImageCarousel(
|
|||||||
|
|
||||||
if(layoutCarousel){
|
if(layoutCarousel){
|
||||||
field = value
|
field = value
|
||||||
if(value) editTextMediaDescription.setText(currentDescription)
|
if(value) binding.editTextMediaDescription.setText(currentDescription)
|
||||||
else {
|
else {
|
||||||
val description = editTextMediaDescription.text.toString()
|
val description = binding.editTextMediaDescription.text.toString()
|
||||||
currentDescription = description
|
currentDescription = description
|
||||||
adapter?.updateDescription(currentPosition, description)
|
adapter?.updateDescription(currentPosition, description)
|
||||||
updateDescriptionCallback?.invoke(currentPosition, description)
|
updateDescriptionCallback?.invoke(currentPosition, description)
|
||||||
@ -339,7 +338,6 @@ class ImageCarousel(
|
|||||||
|
|
||||||
recyclerView = binding.recyclerView
|
recyclerView = binding.recyclerView
|
||||||
tvCaption = binding.tvCaption
|
tvCaption = binding.tvCaption
|
||||||
editTextMediaDescription = binding.editTextMediaDescription
|
|
||||||
|
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.carousel
|
package org.pixeldroid.app.postCreation.carousel
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -6,7 +6,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
|
|
||||||
class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
class EditImageFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.zomato.photofilters.FilterPack
|
import com.zomato.photofilters.FilterPack
|
||||||
import com.zomato.photofilters.imageprocessors.Filter
|
import com.zomato.photofilters.imageprocessors.Filter
|
||||||
import com.zomato.photofilters.utils.ThumbnailItem
|
import com.zomato.photofilters.utils.ThumbnailItem
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
@ -18,14 +18,11 @@ import android.widget.Toast
|
|||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.tabs.TabLayout
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.databinding.ActivityPhotoEditBinding
|
||||||
import com.h.pixeldroid.databinding.ActivityPhotoEditBinding
|
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||||
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
|
||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import com.zomato.photofilters.imageprocessors.Filter
|
import com.zomato.photofilters.imageprocessors.Filter
|
||||||
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
|
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
|
||||||
@ -65,6 +62,8 @@ class PhotoEditActivity : BaseActivity() {
|
|||||||
private lateinit var filterListFragment: FilterListFragment
|
private lateinit var filterListFragment: FilterListFragment
|
||||||
private lateinit var editImageFragment: EditImageFragment
|
private lateinit var editImageFragment: EditImageFragment
|
||||||
|
|
||||||
|
private var picturePosition: Int? = null
|
||||||
|
|
||||||
private var brightnessFinal = BRIGHTNESS_START
|
private var brightnessFinal = BRIGHTNESS_START
|
||||||
private var saturationFinal = SATURATION_START
|
private var saturationFinal = SATURATION_START
|
||||||
private var contrastFinal = CONTRAST_START
|
private var contrastFinal = CONTRAST_START
|
||||||
@ -74,6 +73,9 @@ class PhotoEditActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object{
|
companion object{
|
||||||
|
internal const val PICTURE_URI = "picture_uri"
|
||||||
|
internal const val PICTURE_POSITION = "picture_position"
|
||||||
|
|
||||||
private var executor: ExecutorService = newSingleThreadExecutor()
|
private var executor: ExecutorService = newSingleThreadExecutor()
|
||||||
private var future: Future<*>? = null
|
private var future: Future<*>? = null
|
||||||
|
|
||||||
@ -97,7 +99,8 @@ class PhotoEditActivity : BaseActivity() {
|
|||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setHomeButtonEnabled(true)
|
supportActionBar?.setHomeButtonEnabled(true)
|
||||||
|
|
||||||
initialUri = intent.getParcelableExtra("picture_uri")
|
initialUri = intent.getParcelableExtra(PICTURE_URI)
|
||||||
|
picturePosition = intent.getIntExtra(PICTURE_POSITION, 0)
|
||||||
imageUri = initialUri
|
imageUri = initialUri
|
||||||
|
|
||||||
// Crop button on-click listener
|
// Crop button on-click listener
|
||||||
@ -342,7 +345,8 @@ class PhotoEditActivity : BaseActivity() {
|
|||||||
private fun sendBackImage(file: String) {
|
private fun sendBackImage(file: String) {
|
||||||
val intent = Intent(this, PostCreationActivity::class.java)
|
val intent = Intent(this, PostCreationActivity::class.java)
|
||||||
.apply {
|
.apply {
|
||||||
putExtra("result", file)
|
putExtra(PICTURE_URI, file)
|
||||||
|
putExtra(PICTURE_POSITION, picturePosition)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.view.View
|
import android.view.View
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.postCreation.photoEdit
|
package org.pixeldroid.app.postCreation.photoEdit
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -7,8 +7,8 @@ import android.widget.ImageView
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ThumbnailListItemBinding
|
import org.pixeldroid.app.databinding.ThumbnailListItemBinding
|
||||||
import com.zomato.photofilters.utils.ThumbnailItem
|
import com.zomato.photofilters.utils.ThumbnailItem
|
||||||
|
|
||||||
class ThumbnailAdapter (private val context: Context,
|
class ThumbnailAdapter (private val context: Context,
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.text.TextPaint
|
import android.text.TextPaint
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -12,13 +12,13 @@ import android.view.View
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.text.toSpanned
|
import androidx.core.text.toSpanned
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.openAccountFromId
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
||||||
import com.h.pixeldroid.utils.api.objects.Mention
|
import org.pixeldroid.app.utils.api.objects.Mention
|
||||||
import kotlinx.coroutines.coroutineScope
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.openTag
|
||||||
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
@ -50,12 +50,11 @@ fun getDomain(urlString: String?): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun parseHTMLText(
|
fun parseHTMLText(
|
||||||
text : String,
|
text: String,
|
||||||
mentions: List<Mention>?,
|
mentions: List<Mention>?,
|
||||||
api : PixelfedAPI,
|
apiHolder: PixelfedAPIHolder,
|
||||||
context: Context,
|
context: Context,
|
||||||
credential: String,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
lifecycleScope: LifecycleCoroutineScope
|
|
||||||
) : Spanned {
|
) : Spanned {
|
||||||
//Convert text to spannable
|
//Convert text to spannable
|
||||||
val content = fromHtml(text)
|
val content = fromHtml(text)
|
||||||
@ -76,7 +75,7 @@ fun parseHTMLText(
|
|||||||
val tag = text.subSequence(1, text.length).toString()
|
val tag = text.subSequence(1, text.length).toString()
|
||||||
customSpan = object : ClickableSpanNoUnderline() {
|
customSpan = object : ClickableSpanNoUnderline() {
|
||||||
override fun onClick(widget: View) {
|
override fun onClick(widget: View) {
|
||||||
Toast.makeText(context, tag, Toast.LENGTH_SHORT).show()
|
openTag(context, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -108,7 +107,8 @@ fun parseHTMLText(
|
|||||||
Log.e("MENTION", "CLICKED")
|
Log.e("MENTION", "CLICKED")
|
||||||
//Retrieve the account for the given profile
|
//Retrieve the account for the given profile
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
openAccountFromId(accountId, api, context, credential)
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
openAccountFromId(accountId, api, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@ -51,7 +51,7 @@ class NestedScrollableHost : ConstraintLayout {
|
|||||||
return v as? ViewPager2
|
return v as? ViewPager2
|
||||||
}
|
}
|
||||||
|
|
||||||
var doubleTapCallback: ((Unit) -> Unit)? = null
|
var doubleTapCallback: ((Boolean) -> Unit)? = null
|
||||||
|
|
||||||
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
|
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ class NestedScrollableHost : ConstraintLayout {
|
|||||||
if (e.action == MotionEvent.ACTION_DOWN) {
|
if (e.action == MotionEvent.ACTION_DOWN) {
|
||||||
initialX = e.x
|
initialX = e.x
|
||||||
initialY = e.y
|
initialY = e.y
|
||||||
doubleTapCallback?.invoke(Unit)
|
doubleTapCallback?.invoke(true)
|
||||||
}
|
}
|
||||||
// Early return if child can't scroll in same direction as parent
|
// Early return if child can't scroll in same direction as parent
|
||||||
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
|
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
|
||||||
@ -94,10 +94,13 @@ class NestedScrollableHost : ConstraintLayout {
|
|||||||
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
|
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
|
||||||
|
|
||||||
// assuming ViewPager2 touch-slop is 2x touch-slop of child
|
// assuming ViewPager2 touch-slop is 2x touch-slop of child
|
||||||
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
|
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f/ touchSlopModifier else 1f
|
||||||
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
|
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f/touchSlopModifier
|
||||||
|
|
||||||
|
if(dx.absoluteValue * .5f > touchSlop || scaledDy > touchSlop) doubleTapCallback?.invoke(false)
|
||||||
|
|
||||||
if (scaledDx > touchSlop || scaledDy > touchSlop) {
|
if (scaledDx > touchSlop || scaledDy > touchSlop) {
|
||||||
|
|
||||||
if (isVpHorizontal == (scaledDy > scaledDx)) {
|
if (isVpHorizontal == (scaledDy > scaledDx)) {
|
||||||
// Gesture is perpendicular, allow all parents to intercept
|
// Gesture is perpendicular, allow all parents to intercept
|
||||||
parent.requestDisallowInterceptTouchEvent(false)
|
parent.requestDisallowInterceptTouchEvent(false)
|
||||||
@ -114,4 +117,8 @@ class NestedScrollableHost : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val touchSlopModifier = 2
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -9,23 +9,22 @@ import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityPostBinding
|
import org.pixeldroid.app.databinding.ActivityPostBinding
|
||||||
import com.h.pixeldroid.databinding.CommentBinding
|
import org.pixeldroid.app.databinding.CommentBinding
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Mention
|
import org.pixeldroid.app.utils.api.objects.Mention
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||||
import com.h.pixeldroid.utils.displayDimensionsInPx
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class PostActivity : BaseActivity() {
|
class PostActivity : BaseActivity() {
|
||||||
lateinit var domain : String
|
lateinit var domain : String
|
||||||
private lateinit var accessToken : String
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityPostBinding
|
private lateinit var binding: ActivityPostBinding
|
||||||
|
|
||||||
@ -45,17 +44,15 @@ class PostActivity : BaseActivity() {
|
|||||||
val user = db.userDao().getActiveUser()
|
val user = db.userDao().getActiveUser()
|
||||||
|
|
||||||
domain = user?.instance_uri.orEmpty()
|
domain = user?.instance_uri.orEmpty()
|
||||||
accessToken = user?.accessToken.orEmpty()
|
|
||||||
|
|
||||||
|
|
||||||
supportActionBar?.title = getString(R.string.post_title).format(status.account?.getDisplayName())
|
supportActionBar?.title = getString(R.string.post_title).format(status.account?.getDisplayName())
|
||||||
|
|
||||||
val holder = StatusViewHolder(binding.postFragmentSingle)
|
val holder = StatusViewHolder(binding.postFragmentSingle)
|
||||||
|
|
||||||
holder.bind(status, apiHolder.api!!, db, lifecycleScope, displayDimensionsInPx(), isActivity = true)
|
holder.bind(status, apiHolder, db, lifecycleScope, displayDimensionsInPx(), isActivity = true)
|
||||||
|
|
||||||
val credential = "Bearer $accessToken"
|
activateCommenter()
|
||||||
activateCommenter(credential)
|
|
||||||
|
|
||||||
if(viewComments || postComment){
|
if(viewComments || postComment){
|
||||||
//Scroll already down as much as possible (since comments are not loaded yet)
|
//Scroll already down as much as possible (since comments are not loaded yet)
|
||||||
@ -68,19 +65,14 @@ class PostActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// also retrieve comments if we're not posting the comment
|
// also retrieve comments if we're not posting the comment
|
||||||
if(!postComment) retrieveComments(apiHolder.api!!, credential)
|
if(!postComment) retrieveComments(apiHolder.api!!)
|
||||||
}
|
}
|
||||||
binding.postFragmentSingle.viewComments.setOnClickListener {
|
binding.postFragmentSingle.viewComments.setOnClickListener {
|
||||||
retrieveComments(apiHolder.api!!, credential)
|
retrieveComments(apiHolder.api!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
private fun activateCommenter() {
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun activateCommenter(credential: String) {
|
|
||||||
//Activate commenter
|
//Activate commenter
|
||||||
binding.submitComment.setOnClickListener {
|
binding.submitComment.setOnClickListener {
|
||||||
val textIn = binding.editComment.text
|
val textIn = binding.editComment.text
|
||||||
@ -94,15 +86,14 @@ class PostActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
//Post the comment
|
//Post the comment
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
apiHolder.api?.let { it1 -> postComment(it1, credential) }
|
apiHolder.api?.let { it1 -> postComment(it1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addComment(context: Context, commentContainer: LinearLayout,
|
private fun addComment(context: Context, commentContainer: LinearLayout,
|
||||||
commentUsername: String, commentContent: String, mentions: List<Mention>,
|
commentUsername: String, commentContent: String, mentions: List<Mention>) {
|
||||||
credential: String) {
|
|
||||||
|
|
||||||
|
|
||||||
val itemBinding = CommentBinding.inflate(
|
val itemBinding = CommentBinding.inflate(
|
||||||
@ -111,27 +102,30 @@ class PostActivity : BaseActivity() {
|
|||||||
|
|
||||||
itemBinding.user.text = commentUsername
|
itemBinding.user.text = commentUsername
|
||||||
itemBinding.commentText.text = parseHTMLText(
|
itemBinding.commentText.text = parseHTMLText(
|
||||||
commentContent,
|
commentContent,
|
||||||
mentions,
|
mentions,
|
||||||
apiHolder.api!!,
|
apiHolder,
|
||||||
context,
|
context,
|
||||||
credential,
|
lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun retrieveComments(api: PixelfedAPI, credential: String) {
|
private fun retrieveComments(api: PixelfedAPI) {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
status.id.let {
|
status.id.let {
|
||||||
try {
|
try {
|
||||||
val statuses = api.statusComments(it, credential).descendants
|
val statuses = api.statusComments(it).descendants
|
||||||
|
|
||||||
binding.commentContainer.removeAllViews()
|
binding.commentContainer.removeAllViews()
|
||||||
|
|
||||||
//Create the new views for each comment
|
//Create the new views for each comment
|
||||||
for (status in statuses) {
|
for (status in statuses) {
|
||||||
addComment(binding.root.context, binding.commentContainer, status.account!!.username!!,
|
addComment(
|
||||||
status.content!!, status.mentions.orEmpty(), credential
|
binding.root.context,
|
||||||
|
binding.commentContainer,
|
||||||
|
status.account!!.username!!,
|
||||||
|
status.content!!,
|
||||||
|
status.mentions.orEmpty()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
binding.commentContainer.visibility = View.VISIBLE
|
binding.commentContainer.visibility = View.VISIBLE
|
||||||
@ -149,19 +143,18 @@ class PostActivity : BaseActivity() {
|
|||||||
|
|
||||||
private suspend fun postComment(
|
private suspend fun postComment(
|
||||||
api: PixelfedAPI,
|
api: PixelfedAPI,
|
||||||
credential: String,
|
|
||||||
) {
|
) {
|
||||||
val textIn = binding.editComment.text
|
val textIn = binding.editComment.text
|
||||||
val nonNullText = textIn.toString()
|
val nonNullText = textIn.toString()
|
||||||
status.id.let {
|
status.id.let {
|
||||||
try {
|
try {
|
||||||
val response = api.postStatus(credential, nonNullText, it)
|
val response = api.postStatus(nonNullText, it)
|
||||||
binding.commentIn.visibility = View.GONE
|
binding.commentIn.visibility = View.GONE
|
||||||
|
|
||||||
//Add the comment to the comment section
|
//Add the comment to the comment section
|
||||||
addComment(
|
addComment(
|
||||||
binding.root.context, binding.commentContainer, response.account!!.username!!,
|
binding.root.context, binding.commentContainer, response.account!!.username!!,
|
||||||
response.content!!, response.mentions.orEmpty(), credential
|
response.content!!, response.mentions.orEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
@ -1,12 +1,12 @@
|
|||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityReportBinding
|
import org.pixeldroid.app.databinding.ActivityReportBinding
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -24,10 +24,6 @@ class ReportActivity : BaseActivity() {
|
|||||||
|
|
||||||
val status = intent.getSerializableExtra(Status.POST_TAG) as Status?
|
val status = intent.getSerializableExtra(Status.POST_TAG) as Status?
|
||||||
|
|
||||||
//get the currently active user
|
|
||||||
val user = db.userDao().getActiveUser()
|
|
||||||
|
|
||||||
|
|
||||||
binding.reportTargetTextview.text = getString(R.string.report_target).format(status?.account?.acct)
|
binding.reportTargetTextview.text = getString(R.string.report_target).format(status?.account?.acct)
|
||||||
|
|
||||||
|
|
||||||
@ -37,12 +33,15 @@ class ReportActivity : BaseActivity() {
|
|||||||
|
|
||||||
binding.textInputLayout.editText?.isEnabled = false
|
binding.textInputLayout.editText?.isEnabled = false
|
||||||
|
|
||||||
val accessToken = user?.accessToken.orEmpty()
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
try {
|
try {
|
||||||
api.report("Bearer $accessToken", status?.account?.id!!, listOf(status), binding.textInputLayout.editText?.text.toString())
|
api.report(
|
||||||
|
status?.account?.id!!,
|
||||||
|
listOf(status),
|
||||||
|
binding.textInputLayout.editText?.text.toString()
|
||||||
|
)
|
||||||
|
|
||||||
reportStatus(true)
|
reportStatus(true)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
@ -67,9 +66,4 @@ class ReportActivity : BaseActivity() {
|
|||||||
binding.reportProgressBar.visibility = View.GONE
|
binding.reportProgressBar.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package com.h.pixeldroid.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.drawable.AnimatedVectorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -14,21 +15,23 @@ import android.widget.*
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestBuilder
|
import com.bumptech.glide.RequestBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.AlbumImageViewBinding
|
import org.pixeldroid.app.databinding.AlbumImageViewBinding
|
||||||
import com.h.pixeldroid.databinding.PostFragmentBinding
|
import org.pixeldroid.app.databinding.PostFragmentBinding
|
||||||
import com.h.pixeldroid.utils.BlurHashDecoder
|
import org.pixeldroid.app.utils.BlurHashDecoder
|
||||||
import com.h.pixeldroid.utils.ImageConverter
|
import org.pixeldroid.app.utils.ImageConverter
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import com.karumi.dexter.Dexter
|
import com.karumi.dexter.Dexter
|
||||||
import com.karumi.dexter.listener.PermissionDeniedResponse
|
import com.karumi.dexter.listener.PermissionDeniedResponse
|
||||||
import com.karumi.dexter.listener.PermissionGrantedResponse
|
import com.karumi.dexter.listener.PermissionGrantedResponse
|
||||||
@ -46,7 +49,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
|
|
||||||
private var status: Status? = null
|
private var status: Status? = null
|
||||||
|
|
||||||
fun bind(status: Status?, pixelfedAPI: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope, displayDimensionsInPx: Pair<Int, Int>, isActivity: Boolean = false) {
|
fun bind(status: Status?, pixelfedAPI: PixelfedAPIHolder, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope, displayDimensionsInPx: Pair<Int, Int>, isActivity: Boolean = false) {
|
||||||
|
|
||||||
this.itemView.visibility = View.VISIBLE
|
this.itemView.visibility = View.VISIBLE
|
||||||
this.status = status
|
this.status = status
|
||||||
@ -177,21 +180,19 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setDescription(
|
private fun setDescription(
|
||||||
api: PixelfedAPI,
|
apiHolder: PixelfedAPIHolder,
|
||||||
credential: String,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
lifecycleScope: LifecycleCoroutineScope
|
|
||||||
) {
|
) {
|
||||||
binding.description.apply {
|
binding.description.apply {
|
||||||
if (status?.content.isNullOrBlank()) {
|
if (status?.content.isNullOrBlank()) {
|
||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
text = parseHTMLText(
|
text = parseHTMLText(
|
||||||
status?.content.orEmpty(),
|
status?.content.orEmpty(),
|
||||||
status?.mentions,
|
status?.mentions,
|
||||||
api,
|
apiHolder,
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
credential,
|
lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
movementMethod = LinkMovementMethod.getInstance()
|
movementMethod = LinkMovementMethod.getInstance()
|
||||||
}
|
}
|
||||||
@ -199,25 +200,20 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
//region buttons
|
//region buttons
|
||||||
private fun activateButtons(
|
private fun activateButtons(
|
||||||
api: PixelfedAPI,
|
apiHolder: PixelfedAPIHolder,
|
||||||
db: AppDatabase,
|
db: AppDatabase,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
isActivity: Boolean
|
isActivity: Boolean
|
||||||
){
|
){
|
||||||
val user = db.userDao().getActiveUser()!!
|
|
||||||
|
|
||||||
val credential = "Bearer ${user.accessToken}"
|
|
||||||
//Set the special HTML text
|
//Set the special HTML text
|
||||||
setDescription(api, credential, lifecycleScope)
|
setDescription(apiHolder, lifecycleScope)
|
||||||
|
|
||||||
//Activate onclickListeners
|
//Activate onclickListeners
|
||||||
activateLiker(
|
activateLiker(
|
||||||
api, credential, status?.favourited ?: false,
|
apiHolder, status?.favourited ?: false, lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
activateReblogger(
|
activateReblogger(
|
||||||
api, credential, status?.reblogged ?: false,
|
apiHolder, status?.reblogged ?: false, lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(isActivity){
|
if(isActivity){
|
||||||
@ -237,14 +233,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
|
|
||||||
showComments(lifecycleScope, isActivity)
|
showComments(lifecycleScope, isActivity)
|
||||||
|
|
||||||
activateMoreButton(api, db, lifecycleScope)
|
activateMoreButton(apiHolder, db, lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateReblogger(
|
private fun activateReblogger(
|
||||||
api: PixelfedAPI,
|
apiHolder: PixelfedAPIHolder,
|
||||||
credential: String,
|
isReblogged: Boolean,
|
||||||
isReblogged: Boolean,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
lifecycleScope: LifecycleCoroutineScope
|
|
||||||
) {
|
) {
|
||||||
binding.reblogger.apply {
|
binding.reblogger.apply {
|
||||||
//Set initial button state
|
//Set initial button state
|
||||||
@ -253,12 +248,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
//Activate the button
|
//Activate the button
|
||||||
setEventListener { _, buttonState ->
|
setEventListener { _, buttonState ->
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
if (buttonState) {
|
if (buttonState) {
|
||||||
// Button is active
|
// Button is active
|
||||||
undoReblogPost(api, credential)
|
undoReblogPost(api)
|
||||||
} else {
|
} else {
|
||||||
// Button is inactive
|
// Button is inactive
|
||||||
reblogPost(api, credential)
|
reblogPost(api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//show animation or not?
|
//show animation or not?
|
||||||
@ -267,15 +263,12 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun reblogPost(
|
private suspend fun reblogPost(api: PixelfedAPI) {
|
||||||
api: PixelfedAPI,
|
|
||||||
credential: String
|
|
||||||
) {
|
|
||||||
//Call the api function
|
//Call the api function
|
||||||
status?.id?.let {
|
status?.id?.let {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val resp = api.reblogStatus(credential, it)
|
val resp = api.reblogStatus(it)
|
||||||
|
|
||||||
//Update shown share count
|
//Update shown share count
|
||||||
binding.nshares.text = resp.getNShares(binding.root.context)
|
binding.nshares.text = resp.getNShares(binding.root.context)
|
||||||
@ -290,14 +283,11 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun undoReblogPost(
|
private suspend fun undoReblogPost(api: PixelfedAPI) {
|
||||||
api: PixelfedAPI,
|
|
||||||
credential: String,
|
|
||||||
) {
|
|
||||||
//Call the api function
|
//Call the api function
|
||||||
status?.id?.let {
|
status?.id?.let {
|
||||||
try {
|
try {
|
||||||
val resp = api.undoReblogStatus(credential, it)
|
val resp = api.undoReblogStatus(it)
|
||||||
|
|
||||||
//Update shown share count
|
//Update shown share count
|
||||||
binding.nshares.text = resp.getNShares(binding.root.context)
|
binding.nshares.text = resp.getNShares(binding.root.context)
|
||||||
@ -312,7 +302,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateMoreButton(api: PixelfedAPI, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
|
private fun activateMoreButton(apiHolder: PixelfedAPIHolder, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
|
||||||
binding.statusMore.setOnClickListener {
|
binding.statusMore.setOnClickListener {
|
||||||
PopupMenu(it.context, it).apply {
|
PopupMenu(it.context, it).apply {
|
||||||
setOnMenuItemClickListener { item ->
|
setOnMenuItemClickListener { item ->
|
||||||
@ -395,7 +385,8 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
db.homePostDao().delete(id, user.user_id, user.instance_uri)
|
db.homePostDao().delete(id, user.user_id, user.instance_uri)
|
||||||
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
|
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
|
||||||
try {
|
try {
|
||||||
api.deleteStatus("Bearer ${user.accessToken}", id)
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
api.deleteStatus(id)
|
||||||
binding.root.visibility = View.GONE
|
binding.root.visibility = View.GONE
|
||||||
} catch (exception: HttpException) {
|
} catch (exception: HttpException) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -439,10 +430,9 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun activateLiker(
|
private fun activateLiker(
|
||||||
api: PixelfedAPI,
|
apiHolder: PixelfedAPIHolder,
|
||||||
credential: String,
|
isLiked: Boolean,
|
||||||
isLiked: Boolean,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
lifecycleScope: LifecycleCoroutineScope
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.liker.apply {
|
binding.liker.apply {
|
||||||
@ -452,12 +442,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
//Activate the liker
|
//Activate the liker
|
||||||
setEventListener { _, buttonState ->
|
setEventListener { _, buttonState ->
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
if (buttonState) {
|
if (buttonState) {
|
||||||
// Button is active, unlike
|
// Button is active, unlike
|
||||||
unLikePostCall(api, credential)
|
unLikePostCall(api)
|
||||||
} else {
|
} else {
|
||||||
// Button is inactive, like
|
// Button is inactive, like
|
||||||
likePostCall(api, credential)
|
likePostCall(api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//show animation or not?
|
//show animation or not?
|
||||||
@ -468,20 +459,23 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
//Activate double tap liking
|
//Activate double tap liking
|
||||||
var clicked = false
|
var clicked = false
|
||||||
binding.postPagerHost.doubleTapCallback = {
|
binding.postPagerHost.doubleTapCallback = {
|
||||||
lifecycleScope.launchWhenCreated {
|
if(!it) clicked = false
|
||||||
|
else lifecycleScope.launchWhenCreated {
|
||||||
//Check that the post isn't hidden
|
//Check that the post isn't hidden
|
||||||
if(binding.sensitiveWarning.visibility == View.GONE) {
|
if(binding.sensitiveWarning.visibility == View.GONE) {
|
||||||
//Check for double click
|
//Check for double click
|
||||||
if(clicked) {
|
if(clicked) {
|
||||||
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
if (binding.liker.isChecked) {
|
if (binding.liker.isChecked) {
|
||||||
// Button is active, unlike
|
// Button is active, unlike
|
||||||
binding.liker.isChecked = false
|
binding.liker.isChecked = false
|
||||||
unLikePostCall(api, credential)
|
unLikePostCall(api)
|
||||||
} else {
|
} else {
|
||||||
// Button is inactive, like
|
// Button is inactive, like
|
||||||
binding.liker.playAnimation()
|
binding.liker.playAnimation()
|
||||||
binding.liker.isChecked = true
|
binding.liker.isChecked = true
|
||||||
likePostCall(api, credential)
|
binding.likeAnimation.animateView()
|
||||||
|
likePostCall(api)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clicked = true
|
clicked = true
|
||||||
@ -490,20 +484,27 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
binding.postPager.handler.postDelayed(fun() { clicked = false }, 500)
|
binding.postPager.handler.postDelayed(fun() { clicked = false }, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun ImageView.animateView() {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
when (val drawable = drawable) {
|
||||||
|
is AnimatedVectorDrawableCompat -> {
|
||||||
|
drawable.start()
|
||||||
|
}
|
||||||
|
is AnimatedVectorDrawable -> {
|
||||||
|
drawable.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun likePostCall(
|
private suspend fun likePostCall(api: PixelfedAPI) {
|
||||||
api: PixelfedAPI,
|
|
||||||
credential: String,
|
|
||||||
) {
|
|
||||||
//Call the api function
|
//Call the api function
|
||||||
status?.id?.let {
|
status?.id?.let {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val resp = api.likePost(credential, it)
|
val resp = api.likePost(it)
|
||||||
|
|
||||||
//Update shown like count and internal like toggle
|
//Update shown like count and internal like toggle
|
||||||
binding.nlikes.text = resp.getNLikes(binding.root.context)
|
binding.nlikes.text = resp.getNLikes(binding.root.context)
|
||||||
@ -518,15 +519,12 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun unLikePostCall(
|
private suspend fun unLikePostCall(api: PixelfedAPI) {
|
||||||
api: PixelfedAPI,
|
|
||||||
credential: String,
|
|
||||||
) {
|
|
||||||
//Call the api function
|
//Call the api function
|
||||||
status?.id?.let {
|
status?.id?.let {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val resp = api.unlikePost(credential, it)
|
val resp = api.unlikePost(it)
|
||||||
|
|
||||||
//Update shown like count and internal like toggle
|
//Update shown like count and internal like toggle
|
||||||
binding.nlikes.text = resp.getNLikes(binding.root.context)
|
binding.nlikes.text = resp.getNLikes(binding.root.context)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds
|
package org.pixeldroid.app.posts.feeds
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -12,14 +12,16 @@ import androidx.paging.LoadStateAdapter
|
|||||||
import androidx.paging.PagingDataAdapter
|
import androidx.paging.PagingDataAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.h.pixeldroid.R
|
import com.google.gson.Gson
|
||||||
import com.h.pixeldroid.databinding.ErrorLayoutBinding
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.LoadStateFooterViewItemBinding
|
import org.pixeldroid.app.databinding.ErrorLayoutBinding
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
import org.pixeldroid.app.databinding.LoadStateFooterViewItemBinding
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import retrofit2.HttpException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows or hides the error in the different FeedFragments
|
* Shows or hides the error in the different FeedFragments
|
||||||
@ -56,24 +58,17 @@ internal fun <T: Any> initAdapter(
|
|||||||
if(!progressBar.isVisible && swipeRefreshLayout.isRefreshing) {
|
if(!progressBar.isVisible && swipeRefreshLayout.isRefreshing) {
|
||||||
// Stop loading spinner when loading is done
|
// Stop loading spinner when loading is done
|
||||||
swipeRefreshLayout.isRefreshing = loadState.refresh is LoadState.Loading
|
swipeRefreshLayout.isRefreshing = loadState.refresh is LoadState.Loading
|
||||||
} else {
|
|
||||||
// ProgressBar should stop showing as soon as the source stops loading ("source"
|
|
||||||
// meaning the database, so don't wait on the network)
|
|
||||||
val sourceLoading = loadState.source.refresh is LoadState.Loading
|
|
||||||
if(!sourceLoading && recyclerView.size > 0){
|
|
||||||
recyclerView.isVisible = true
|
|
||||||
progressBar.isVisible = false
|
|
||||||
} else if(recyclerView.size == 0
|
|
||||||
&& loadState.append is LoadState.NotLoading
|
|
||||||
&& loadState.append.endOfPaginationReached){
|
|
||||||
progressBar.isVisible = false
|
|
||||||
showError(motionLayout = motionLayout, errorLayout = errorLayout,
|
|
||||||
errorText = errorLayout.root.context.getString(R.string.empty_feed))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProgressBar should stop showing as soon as the source stops loading ("source"
|
||||||
|
// meaning the database, so don't wait on the network)
|
||||||
|
val sourceLoading = loadState.source.refresh is LoadState.Loading
|
||||||
|
if (!sourceLoading && adapter.itemCount > 0) {
|
||||||
|
recyclerView.isVisible = true
|
||||||
|
progressBar.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
// Toast on any error, regardless of whether it came from RemoteMediator or PagingSource
|
// Show any error, regardless of whether it came from RemoteMediator or PagingSource
|
||||||
val errorState = loadState.source.append as? LoadState.Error
|
val errorState = loadState.source.append as? LoadState.Error
|
||||||
?: loadState.source.prepend as? LoadState.Error
|
?: loadState.source.prepend as? LoadState.Error
|
||||||
?: loadState.source.refresh as? LoadState.Error
|
?: loadState.source.refresh as? LoadState.Error
|
||||||
@ -81,21 +76,37 @@ internal fun <T: Any> initAdapter(
|
|||||||
?: loadState.prepend as? LoadState.Error
|
?: loadState.prepend as? LoadState.Error
|
||||||
?: loadState.refresh as? LoadState.Error
|
?: loadState.refresh as? LoadState.Error
|
||||||
errorState?.let {
|
errorState?.let {
|
||||||
showError(motionLayout = motionLayout, errorLayout = errorLayout, errorText = it.error.toString())
|
val error: String = (it.error as? HttpException)?.response()?.errorBody()?.string()?.ifEmpty { null }?.let { s ->
|
||||||
|
Gson().fromJson(s, org.pixeldroid.app.utils.api.objects.Error::class.java)?.error?.ifBlank { null }
|
||||||
|
} ?: it.error.localizedMessage.orEmpty()
|
||||||
|
showError(motionLayout = motionLayout, errorLayout = errorLayout, errorText = error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the state is not an error, hide the error layout, or show message that the feed is empty
|
||||||
if(errorState == null) {
|
if(errorState == null) {
|
||||||
showError(motionLayout = motionLayout, errorLayout = errorLayout, show = false, errorText = "")
|
if (adapter.itemCount == 0
|
||||||
|
&& loadState.append is LoadState.NotLoading
|
||||||
|
&& loadState.append.endOfPaginationReached
|
||||||
|
) {
|
||||||
|
progressBar.isVisible = false
|
||||||
|
showError(
|
||||||
|
motionLayout = motionLayout, errorLayout = errorLayout,
|
||||||
|
errorText = errorLayout.root.context.getString(R.string.empty_feed)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
showError(motionLayout = motionLayout, errorLayout = errorLayout, show = false, errorText = "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launch(
|
fun <T: FeedContent> launch(
|
||||||
job: Job?, lifecycleScope: LifecycleCoroutineScope, viewModel: FeedViewModel<FeedContent>,
|
job: Job?, lifecycleScope: LifecycleCoroutineScope, viewModel: FeedViewModel<T>,
|
||||||
pagingDataAdapter: PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>): Job {
|
pagingDataAdapter: PagingDataAdapter<T, RecyclerView.ViewHolder>): Job {
|
||||||
// Make sure we cancel the previous job before creating a new one
|
// Make sure we cancel the previous job before creating a new one
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
return lifecycleScope.launch {
|
return lifecycleScope.launch {
|
||||||
viewModel.flow().collectLatest {
|
viewModel.flow.collectLatest {
|
||||||
pagingDataAdapter.submitData(it)
|
pagingDataAdapter.submitData(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -8,21 +8,18 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
|
import androidx.paging.LoadState.*
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
||||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||||
import kotlinx.coroutines.flow.filter
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
import kotlinx.coroutines.launch
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.databinding.FragmentFeedBinding
|
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.limitedLengthSmoothScrollToPosition
|
||||||
import com.h.pixeldroid.utils.db.dao.feedContent.FeedContentDao
|
|
||||||
import com.h.pixeldroid.utils.BaseFragment
|
|
||||||
import com.h.pixeldroid.posts.feeds.initAdapter
|
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment representing a list of [FeedContentDatabase] items that are cached by the database.
|
* A fragment representing a list of [FeedContentDatabase] items that are cached by the database.
|
||||||
@ -43,7 +40,7 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
// Make sure we cancel the previous job before creating a new one
|
// Make sure we cancel the previous job before creating a new one
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
job = lifecycleScope.launchWhenStarted {
|
job = lifecycleScope.launchWhenStarted {
|
||||||
viewModel.flow().collectLatest {
|
viewModel.flow.collectLatest {
|
||||||
adapter.submitData(it)
|
adapter.submitData(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,12 +48,14 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
|
|
||||||
internal fun initSearch() {
|
internal fun initSearch() {
|
||||||
// Scroll to top when the list is refreshed from network.
|
// Scroll to top when the list is refreshed from network.
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launchWhenStarted {
|
||||||
adapter.loadStateFlow
|
adapter.loadStateFlow
|
||||||
// Only emit when REFRESH LoadState for RemoteMediator changes.
|
// Only emit when REFRESH LoadState for RemoteMediator changes.
|
||||||
.distinctUntilChangedBy { it.refresh }
|
.distinctUntilChangedBy {
|
||||||
|
it.refresh
|
||||||
|
}
|
||||||
// Only react to cases where Remote REFRESH completes i.e., NotLoading.
|
// Only react to cases where Remote REFRESH completes i.e., NotLoading.
|
||||||
.filter { it.refresh is LoadState.NotLoading }
|
.filter { it.refresh is NotLoading}
|
||||||
.collect { binding.list.scrollToPosition(0) }
|
.collect { binding.list.scrollToPosition(0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,17 +72,16 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
||||||
binding.list, binding.motionLayout, binding.errorLayout, adapter)
|
binding.list, binding.motionLayout, binding.errorLayout, adapter)
|
||||||
|
|
||||||
//binding.progressBar.visibility = View.GONE
|
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
//It shouldn't be necessary to also retry() in addition to refresh(),
|
|
||||||
//but if we don't do this, reloads after an error fail immediately...
|
|
||||||
// https://issuetracker.google.com/issues/173438474
|
|
||||||
adapter.retry()
|
|
||||||
adapter.refresh()
|
adapter.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onTabReClicked() {
|
||||||
|
binding.list.limitedLengthSmoothScrollToPosition(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,12 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds
|
||||||
|
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.dao.feedContent.FeedContentDao
|
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -14,31 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewModel for the cached feeds.
|
* ViewModel for the cached feeds.
|
||||||
* The ViewModel works with the [FeedContentRepository] to get the data.
|
* The ViewModel works with the [FeedContentRepository] to get the data.
|
||||||
*/
|
*/
|
||||||
class FeedViewModel<T: FeedContentDatabase>(private val repository: FeedContentRepository<T>) : ViewModel() {
|
class FeedViewModel<T: FeedContentDatabase>(repository: FeedContentRepository<T>) : ViewModel() {
|
||||||
|
|
||||||
private var currentResult: Flow<PagingData<T>>? = null
|
|
||||||
|
|
||||||
@ExperimentalPagingApi
|
@ExperimentalPagingApi
|
||||||
fun flow(): Flow<PagingData<T>> {
|
val flow: Flow<PagingData<T>> = repository.stream().cachedIn(viewModelScope)
|
||||||
val lastResult = currentResult
|
|
||||||
if (lastResult != null) {
|
|
||||||
return lastResult
|
|
||||||
}
|
|
||||||
val newResult: Flow<PagingData<T>> = repository.stream()
|
|
||||||
.cachedIn(viewModelScope)
|
|
||||||
currentResult = newResult
|
|
||||||
return newResult
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds.notifications
|
package org.pixeldroid.app.posts.feeds.cachedFeeds.notifications
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -18,21 +18,19 @@ import androidx.paging.PagingDataAdapter
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.FragmentNotificationsBinding
|
import org.pixeldroid.app.databinding.FragmentNotificationsBinding
|
||||||
import com.h.pixeldroid.posts.PostActivity
|
import org.pixeldroid.app.posts.PostActivity
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
||||||
import com.h.pixeldroid.posts.parseHTMLText
|
import org.pixeldroid.app.posts.parseHTMLText
|
||||||
import com.h.pixeldroid.posts.setTextViewFromISO8601
|
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
||||||
import com.h.pixeldroid.profile.ProfileActivity
|
import org.pixeldroid.app.profile.ProfileActivity
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
import com.h.pixeldroid.utils.api.objects.Notification
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +40,7 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
adapter = NotificationsAdapter(apiHolder, db)
|
adapter = NotificationsAdapter(apiHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalPagingApi
|
@ExperimentalPagingApi
|
||||||
@ -55,10 +53,10 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(
|
viewModel = ViewModelProvider(
|
||||||
this,
|
requireActivity(),
|
||||||
ViewModelFactory(db, db.notificationDao(), NotificationsRemoteMediator(apiHolder, db))
|
ViewModelFactory(db, db.notificationDao(), NotificationsRemoteMediator(apiHolder, db))
|
||||||
)
|
)
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<Notification>
|
.get("notifications", FeedViewModel::class.java) as FeedViewModel<Notification>
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
||||||
@ -174,9 +172,8 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
|
|
||||||
fun bind(
|
fun bind(
|
||||||
notification: Notification?,
|
notification: Notification?,
|
||||||
api: PixelfedAPI,
|
api: PixelfedAPIHolder,
|
||||||
accessToken: String,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
lifecycleScope: LifecycleCoroutineScope
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
this.notification = notification
|
this.notification = notification
|
||||||
@ -213,12 +210,11 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
//Convert HTML to clickable text
|
//Convert HTML to clickable text
|
||||||
postDescription.text =
|
postDescription.text =
|
||||||
parseHTMLText(
|
parseHTMLText(
|
||||||
notification?.status?.content ?: "",
|
notification?.status?.content ?: "",
|
||||||
notification?.status?.mentions,
|
notification?.status?.mentions,
|
||||||
api,
|
api,
|
||||||
itemView.context,
|
itemView.context,
|
||||||
"Bearer $accessToken",
|
lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
|
|
||||||
avatar.setOnClickListener {
|
avatar.setOnClickListener {
|
||||||
@ -240,7 +236,6 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
|
|
||||||
inner class NotificationsAdapter(
|
inner class NotificationsAdapter(
|
||||||
private val apiHolder: PixelfedAPIHolder,
|
private val apiHolder: PixelfedAPIHolder,
|
||||||
private val db: AppDatabase
|
|
||||||
) : PagingDataAdapter<Notification, RecyclerView.ViewHolder>(
|
) : PagingDataAdapter<Notification, RecyclerView.ViewHolder>(
|
||||||
object : DiffUtil.ItemCallback<Notification>() {
|
object : DiffUtil.ItemCallback<Notification>() {
|
||||||
override fun areItemsTheSame(
|
override fun areItemsTheSame(
|
||||||
@ -270,10 +265,9 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
val uiModel = getItem(position)
|
val uiModel = getItem(position)
|
||||||
uiModel.let {
|
uiModel.let {
|
||||||
(holder as NotificationViewHolder).bind(
|
(holder as NotificationViewHolder).bind(
|
||||||
it,
|
it,
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
apiHolder,
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
lifecycleScope
|
||||||
lifecycleScope
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds.notifications
|
package org.pixeldroid.app.posts.feeds.cachedFeeds.notifications
|
||||||
|
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import com.h.pixeldroid.utils.api.objects.Notification
|
import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
@ -58,13 +58,12 @@ class NotificationsRemoteMediator @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
val user = db.userDao().getActiveUser()
|
val user = db.userDao().getActiveUser()
|
||||||
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
||||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
val accessToken = user.accessToken
|
|
||||||
|
|
||||||
val apiResponse = api.notifications("Bearer $accessToken",
|
val apiResponse = api.notifications(
|
||||||
max_id = max_id,
|
max_id = max_id,
|
||||||
min_id = min_id,
|
min_id = min_id,
|
||||||
limit = state.config.pageSize.toString(),
|
limit = state.config.pageSize.toString(),
|
||||||
)
|
)
|
||||||
|
|
||||||
apiResponse.forEach{it.user_id = user.user_id; it.instance_uri = user.instance_uri}
|
apiResponse.forEach{it.user_id = user.user_id; it.instance_uri = user.instance_uri}
|
@ -1,10 +1,10 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds
|
||||||
|
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import com.h.pixeldroid.utils.db.entities.HomeStatusDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
@ -43,12 +43,12 @@ class HomeFeedRemoteMediator @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
val user = db.userDao().getActiveUser()
|
val user = db.userDao().getActiveUser()
|
||||||
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
||||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
val accessToken = user.accessToken
|
|
||||||
|
|
||||||
val apiResponse = api.timelineHome( "Bearer $accessToken",
|
val apiResponse = api.timelineHome(
|
||||||
max_id= max_id, min_id = min_id,
|
max_id= max_id,
|
||||||
limit = state.config.pageSize.toString())
|
min_id = min_id, limit = state.config.pageSize.toString()
|
||||||
|
)
|
||||||
|
|
||||||
val dbObjects = apiResponse.map{
|
val dbObjects = apiResponse.map{
|
||||||
HomeStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
HomeStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -11,15 +11,16 @@ import androidx.paging.PagingDataAdapter
|
|||||||
import androidx.paging.RemoteMediator
|
import androidx.paging.RemoteMediator
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.utils.db.dao.feedContent.FeedContentDao
|
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||||
import com.h.pixeldroid.posts.StatusViewHolder
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.displayDimensionsInPx
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,14 +33,17 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
|
|
||||||
private lateinit var mediator: RemoteMediator<Int, T>
|
private lateinit var mediator: RemoteMediator<Int, T>
|
||||||
private lateinit var dao: FeedContentDao<T>
|
private lateinit var dao: FeedContentDao<T>
|
||||||
|
private var home by Delegates.notNull<Boolean>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
||||||
|
|
||||||
|
home = requireArguments().get("home") as Boolean
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (requireArguments().get("home") as Boolean){
|
if (home){
|
||||||
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
||||||
dao = db.homePostDao() as FeedContentDao<T>
|
dao = db.homePostDao() as FeedContentDao<T>
|
||||||
}
|
}
|
||||||
@ -59,8 +63,8 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
|
|
||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(this, ViewModelFactory(db, dao, mediator))
|
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(db, dao, mediator))
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<T>
|
.get(if(home) "home" else "public", FeedViewModel::class.java) as FeedViewModel<T>
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
||||||
@ -70,12 +74,8 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
|
|
||||||
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<T, RecyclerView.ViewHolder>(
|
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<T, RecyclerView.ViewHolder>(
|
||||||
object : DiffUtil.ItemCallback<T>() {
|
object : DiffUtil.ItemCallback<T>() {
|
||||||
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
|
override fun areItemsTheSame (oldItem: T, newItem: T): Boolean = oldItem.id == newItem.id
|
||||||
return oldItem.id == newItem.id
|
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = oldItem.id == newItem.id
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean =
|
|
||||||
oldItem.id == newItem.id
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -88,9 +88,9 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val uiModel = getItem(position) as Status
|
val uiModel = getItem(position) as Status?
|
||||||
uiModel.let {
|
uiModel?.let {
|
||||||
(holder as StatusViewHolder).bind(it, apiHolder.setDomainToCurrentUser(db), db, lifecycleScope, displayDimensionsInPx)
|
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.h.pixeldroid.posts.feeds.cachedFeeds.postFeeds
|
package org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds
|
||||||
|
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.db.entities.PublicFeedStatusDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
@ -58,7 +58,7 @@ class PublicFeedRemoteMediator @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
val user = db.userDao().getActiveUser()
|
val user = db.userDao().getActiveUser()
|
||||||
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
||||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
val apiResponse = api.timelinePublic(
|
val apiResponse = api.timelinePublic(
|
||||||
max_id = max_id,
|
max_id = max_id,
|
@ -0,0 +1,22 @@
|
|||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.paging.*
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewModel for the uncached feeds.
|
||||||
|
* The ViewModel works with the different [UncachedContentRepository]s to get the data.
|
||||||
|
*/
|
||||||
|
class FeedViewModel<T: FeedContent>(repository: UncachedContentRepository<T>) : ViewModel() {
|
||||||
|
val flow: Flow<PagingData<T>> = repository.getStream().cachedIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for the different uncached feeds
|
||||||
|
*/
|
||||||
|
interface UncachedContentRepository<T: FeedContent>{
|
||||||
|
fun getStream(): Flow<PagingData<T>>
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -9,17 +9,17 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.paging.*
|
import androidx.paging.*
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.posts.feeds.launch
|
import org.pixeldroid.app.posts.feeds.launch
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
import com.h.pixeldroid.databinding.FragmentFeedBinding
|
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
||||||
import com.h.pixeldroid.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
import com.h.pixeldroid.posts.feeds.initAdapter
|
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,10 +37,7 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
|||||||
|
|
||||||
|
|
||||||
internal fun launch() {
|
internal fun launch() {
|
||||||
@Suppress("UNCHECKED_CAST")
|
job = launch(job, lifecycleScope, viewModel, adapter)
|
||||||
job = launch(job, lifecycleScope,
|
|
||||||
viewModel as FeedViewModel<FeedContent>,
|
|
||||||
adapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun initSearch() {
|
internal fun initSearch() {
|
||||||
@ -68,9 +65,6 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
|||||||
binding.motionLayout, binding.errorLayout, adapter)
|
binding.motionLayout, binding.errorLayout, adapter)
|
||||||
|
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
//It shouldn't be necessary to also retry() in addition to refresh(),
|
|
||||||
//but if we don't do this, reloads after an error fail immediately...
|
|
||||||
adapter.retry()
|
|
||||||
adapter.refresh()
|
adapter.refresh()
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
|||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags.HashTagContentRepository
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchContentRepository
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
||||||
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment to show a list of [Status]es, as a result of a search or a hashtag.
|
||||||
|
*/
|
||||||
|
class UncachedPostsFragment : UncachedFeedFragment<Status>() {
|
||||||
|
|
||||||
|
private var hashtagOrQuery: String? = null
|
||||||
|
private var search: Boolean = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
||||||
|
|
||||||
|
hashtagOrQuery = arguments?.getString(HASHTAG_TAG)
|
||||||
|
|
||||||
|
if(hashtagOrQuery == null){
|
||||||
|
search = true
|
||||||
|
hashtagOrQuery = arguments?.getString("searchFeed")!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalPagingApi
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
|
||||||
|
// get the view model
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
viewModel = if(search) {
|
||||||
|
ViewModelProvider(
|
||||||
|
requireActivity(), ViewModelFactory(
|
||||||
|
SearchContentRepository<Status>(
|
||||||
|
apiHolder.setToCurrentUser(),
|
||||||
|
Results.SearchType.statuses,
|
||||||
|
hashtagOrQuery!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get("searchPosts", FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||||
|
HashTagContentRepository(
|
||||||
|
apiHolder.setToCurrentUser(),
|
||||||
|
hashtagOrQuery!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get(HASHTAG_TAG, FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
|
}
|
||||||
|
|
||||||
|
launch()
|
||||||
|
initSearch()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<Status, RecyclerView.ViewHolder>(
|
||||||
|
object : DiffUtil.ItemCallback<Status>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Status, newItem: Status): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Status, newItem: Status): Boolean =
|
||||||
|
oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return StatusViewHolder.create(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int = R.layout.post_fragment
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
getItem(position)?.let {
|
||||||
|
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -13,14 +13,14 @@ import androidx.paging.PagingDataAdapter
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.AccountListEntryBinding
|
import org.pixeldroid.app.databinding.AccountListEntryBinding
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,16 +52,15 @@ class AccountListFragment : UncachedFeedFragment<Account>() {
|
|||||||
|
|
||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||||
FollowersContentRepository(
|
FollowersContentRepository(
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
apiHolder.setToCurrentUser(),
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
|
||||||
id,
|
id,
|
||||||
following
|
following
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<Account>
|
.get("accountList", FeedViewModel::class.java) as FeedViewModel<Account>
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
@ -1,12 +1,12 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists
|
||||||
|
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -14,7 +14,6 @@ import javax.inject.Inject
|
|||||||
class FollowersContentRepository @ExperimentalPagingApi
|
class FollowersContentRepository @ExperimentalPagingApi
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val accessToken: String,
|
|
||||||
private val accountId: String,
|
private val accountId: String,
|
||||||
private val following: Boolean,
|
private val following: Boolean,
|
||||||
): UncachedContentRepository<Account> {
|
): UncachedContentRepository<Account> {
|
||||||
@ -25,7 +24,7 @@ class FollowersContentRepository @ExperimentalPagingApi
|
|||||||
pageSize = NETWORK_PAGE_SIZE,
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
enablePlaceholders = false),
|
enablePlaceholders = false),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
FollowersPagingSource(api, accessToken, accountId, following)
|
FollowersPagingSource(api, accountId, following)
|
||||||
}
|
}
|
||||||
).flow
|
).flow
|
||||||
}
|
}
|
@ -1,16 +1,14 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.math.BigInteger
|
|
||||||
|
|
||||||
class FollowersPagingSource(
|
class FollowersPagingSource(
|
||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val accessToken: String,
|
|
||||||
private val accountId: String,
|
private val accountId: String,
|
||||||
private val following: Boolean
|
private val following: Boolean
|
||||||
) : PagingSource<String, Account>() {
|
) : PagingSource<String, Account>() {
|
||||||
@ -22,17 +20,19 @@ class FollowersPagingSource(
|
|||||||
// Laravel's paging mechanism, while Mastodon uses the Link header for pagination.
|
// Laravel's paging mechanism, while Mastodon uses the Link header for pagination.
|
||||||
// No need to know which is which, they should ignore the non-relevant argument
|
// No need to know which is which, they should ignore the non-relevant argument
|
||||||
if(following) {
|
if(following) {
|
||||||
api.followers(account_id = accountId,
|
api.followers(
|
||||||
authorization = "Bearer $accessToken",
|
account_id = accountId,
|
||||||
|
max_id = position,
|
||||||
limit = params.loadSize,
|
limit = params.loadSize,
|
||||||
page = position,
|
page = position
|
||||||
max_id = position)
|
)
|
||||||
} else {
|
} else {
|
||||||
api.following(account_id = accountId,
|
api.following(
|
||||||
authorization = "Bearer $accessToken",
|
account_id = accountId,
|
||||||
|
max_id = position,
|
||||||
limit = params.loadSize,
|
limit = params.loadSize,
|
||||||
page = position,
|
page = position
|
||||||
max_id = position)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val accounts = if(response.isSuccessful){
|
val accounts = if(response.isSuccessful){
|
||||||
@ -41,25 +41,22 @@ class FollowersPagingSource(
|
|||||||
throw HttpException(response)
|
throw HttpException(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextPosition: String = if(response.headers()["Link"] != null){
|
val nextPosition: String = response.headers()["Link"]
|
||||||
//Header is of the form:
|
// Header is of the form:
|
||||||
// Link: <https://mastodon.social/api/v1/accounts/1/followers?limit=2&max_id=7628164>; rel="next", <https://mastodon.social/api/v1/accounts/1/followers?limit=2&since_id=7628165>; rel="prev"
|
// Link: <https://mastodon.social/api/v1/accounts/1/followers?limit=2&max_id=7628164>; rel="next", <https://mastodon.social/api/v1/accounts/1/followers?limit=2&since_id=7628165>; rel="prev"
|
||||||
// So we want the first max_id value. In case there are arguments after
|
// So we want the first max_id value. In case there are arguments after
|
||||||
// the max_id in the URL, we make sure to stop at the first '?'
|
// the max_id in the URL, we make sure to stop at the first '?'
|
||||||
response.headers()["Link"]
|
?.substringAfter("max_id=", "")
|
||||||
.orEmpty()
|
?.substringBefore('?', "")
|
||||||
.substringAfter("max_id=", "")
|
?.substringBefore('>', "")
|
||||||
.substringBefore('?', "")
|
|
||||||
.substringBefore('>', "")
|
?: // No Link header, so we just increment the position value (Pixelfed case)
|
||||||
} else {
|
|
||||||
// No Link header, so we just increment the position value
|
|
||||||
(position?.toBigIntegerOrNull() ?: 1.toBigInteger()).inc().toString()
|
(position?.toBigIntegerOrNull() ?: 1.toBigInteger()).inc().toString()
|
||||||
}
|
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = accounts,
|
data = accounts,
|
||||||
prevKey = null,
|
prevKey = null,
|
||||||
nextKey = if (accounts.isEmpty()) null else nextPosition
|
nextKey = if (accounts.isEmpty() || nextPosition.isEmpty() || nextPosition == position) null else nextPosition
|
||||||
)
|
)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
LoadResult.Error(exception)
|
LoadResult.Error(exception)
|
||||||
@ -68,8 +65,5 @@ class FollowersPagingSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshKey(state: PagingState<String, Account>): String? =
|
override fun getRefreshKey(state: PagingState<String, Account>): String? = null
|
||||||
state.anchorPosition?.run {
|
|
||||||
state.closestItemToPosition(this)?.id
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||||
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
||||||
|
|
||||||
|
|
||||||
|
class HashTagActivity : BaseActivity() {
|
||||||
|
private var tagFragment = UncachedPostsFragment()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_followers)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
// Get hashtag tag
|
||||||
|
val tag = intent.getSerializableExtra(HASHTAG_TAG) as String?
|
||||||
|
|
||||||
|
startFragment(tag!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startFragment(tag : String) {
|
||||||
|
supportActionBar?.title = getString(R.string.hashtag_title).format(tag)
|
||||||
|
|
||||||
|
val arguments = Bundle()
|
||||||
|
arguments.putSerializable(HASHTAG_TAG, tag)
|
||||||
|
tagFragment.arguments = arguments
|
||||||
|
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.add(R.id.followsFragment, tagFragment).commit()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository class for viewing hashtags
|
||||||
|
*/
|
||||||
|
class HashTagContentRepository @ExperimentalPagingApi
|
||||||
|
@Inject constructor(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val hashtag: String,
|
||||||
|
): UncachedContentRepository<Status> {
|
||||||
|
override fun getStream(): Flow<PagingData<Status>> {
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
initialLoadSize = NETWORK_PAGE_SIZE,
|
||||||
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
|
enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = {
|
||||||
|
HashTagPagingSource(api, hashtag)
|
||||||
|
}
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val NETWORK_PAGE_SIZE = 20
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the PagingSource for hashtag feeds. Is used in [HashTagContentRepository]
|
||||||
|
*/
|
||||||
|
class HashTagPagingSource(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val query: String,
|
||||||
|
) : PagingSource<String, Status>() {
|
||||||
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
||||||
|
val position = params.key
|
||||||
|
return try {
|
||||||
|
val response = api.hashtag(
|
||||||
|
hashtag = query,
|
||||||
|
limit = params.loadSize,
|
||||||
|
max_id = position,
|
||||||
|
)
|
||||||
|
|
||||||
|
val nextKey = response.lastOrNull()?.id
|
||||||
|
|
||||||
|
LoadResult.Page(
|
||||||
|
data = response,
|
||||||
|
prevKey = null,
|
||||||
|
nextKey = if(nextKey == position) null else nextKey
|
||||||
|
)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME if implemented with [PagingState.anchorPosition], this breaks refreshes? How is this
|
||||||
|
* supposed to work?
|
||||||
|
*/
|
||||||
|
override fun getRefreshKey(state: PagingState<String, Status>): String? = null
|
||||||
|
}
|
@ -1,19 +1,18 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.profile
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.profile
|
||||||
|
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ProfileContentRepository @ExperimentalPagingApi
|
class ProfileContentRepository @ExperimentalPagingApi
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val accessToken: String,
|
|
||||||
private val accountId: String
|
private val accountId: String
|
||||||
) : UncachedContentRepository<Status> {
|
) : UncachedContentRepository<Status> {
|
||||||
override fun getStream(): Flow<PagingData<Status>> {
|
override fun getStream(): Flow<PagingData<Status>> {
|
||||||
@ -23,7 +22,7 @@ class ProfileContentRepository @ExperimentalPagingApi
|
|||||||
pageSize = NETWORK_PAGE_SIZE,
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
enablePlaceholders = false),
|
enablePlaceholders = false),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
ProfilePagingSource(api, accessToken, accountId)
|
ProfilePagingSource(api, accountId)
|
||||||
}
|
}
|
||||||
).flow
|
).flow
|
||||||
}
|
}
|
@ -1,30 +1,31 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.profile
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.profile
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProfilePagingSource(
|
class ProfilePagingSource(
|
||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val accessToken: String,
|
|
||||||
private val accountId: String
|
private val accountId: String
|
||||||
) : PagingSource<String, Status>() {
|
) : PagingSource<String, Status>() {
|
||||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
||||||
val position = params.key
|
val position = params.key
|
||||||
return try {
|
return try {
|
||||||
val posts = api.accountPosts("Bearer $accessToken",
|
val posts = api.accountPosts(
|
||||||
account_id = accountId,
|
account_id = accountId,
|
||||||
max_id = position,
|
max_id = position,
|
||||||
limit = params.loadSize
|
limit = params.loadSize
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val nextKey = posts.lastOrNull()?.id
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = posts,
|
data = posts,
|
||||||
prevKey = null,
|
prevKey = null,
|
||||||
nextKey = posts.lastOrNull()?.id
|
nextKey = if(nextKey == position) null else nextKey
|
||||||
)
|
)
|
||||||
} catch (exception: HttpException) {
|
} catch (exception: HttpException) {
|
||||||
LoadResult.Error(exception)
|
LoadResult.Error(exception)
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.search
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.search
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -6,10 +6,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.*
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.*
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists.AccountAdapter
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountAdapter
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment to show a list of [Account]s, as a result of a search.
|
* Fragment to show a list of [Account]s, as a result of a search.
|
||||||
@ -37,15 +37,14 @@ class SearchAccountFragment : UncachedFeedFragment<Account>() {
|
|||||||
|
|
||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||||
SearchContentRepository<Account>(
|
SearchContentRepository<Account>(
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
apiHolder.setToCurrentUser(),
|
||||||
Results.SearchType.accounts,
|
Results.SearchType.accounts,
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
|
||||||
query
|
query
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).get(FeedViewModel::class.java) as FeedViewModel<Account>
|
).get("searchAccounts", FeedViewModel::class.java) as FeedViewModel<Account>
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
@ -1,13 +1,13 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.search
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.search
|
||||||
|
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -15,14 +15,13 @@ import javax.inject.Inject
|
|||||||
* Repository class to perform searches
|
* Repository class to perform searches
|
||||||
*
|
*
|
||||||
* The type argument [T] and the [Results.SearchType][type] argument should always
|
* The type argument [T] and the [Results.SearchType][type] argument should always
|
||||||
* be in agreement, e.g. if [T] is a [com.h.pixeldroid.utils.api.objects.Account] then
|
* be in agreement, e.g. if [T] is a [org.pixeldroid.app.utils.api.objects.Account] then
|
||||||
* [type] should be [Results.SearchType.accounts].
|
* [type] should be [Results.SearchType.accounts].
|
||||||
*/
|
*/
|
||||||
class SearchContentRepository<T: FeedContent> @ExperimentalPagingApi
|
class SearchContentRepository<T: FeedContent> @ExperimentalPagingApi
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val type: Results.SearchType,
|
private val type: Results.SearchType,
|
||||||
private val accessToken: String,
|
|
||||||
private val query: String,
|
private val query: String,
|
||||||
): UncachedContentRepository<T> {
|
): UncachedContentRepository<T> {
|
||||||
override fun getStream(): Flow<PagingData<T>> {
|
override fun getStream(): Flow<PagingData<T>> {
|
||||||
@ -32,7 +31,7 @@ class SearchContentRepository<T: FeedContent> @ExperimentalPagingApi
|
|||||||
pageSize = NETWORK_PAGE_SIZE,
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
enablePlaceholders = false),
|
enablePlaceholders = false),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
SearchPagingSource<T>(api, query, type, accessToken)
|
SearchPagingSource<T>(api, query, type)
|
||||||
}
|
}
|
||||||
).flow
|
).flow
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.search
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.search
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -11,13 +11,14 @@ import androidx.paging.ExperimentalPagingApi
|
|||||||
import androidx.paging.PagingDataAdapter
|
import androidx.paging.PagingDataAdapter
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.FragmentTagsBinding
|
import org.pixeldroid.app.databinding.FragmentTagsBinding
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import com.h.pixeldroid.utils.api.objects.Tag
|
import org.pixeldroid.app.utils.api.objects.Tag
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.openTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment to show a list of [hashtag][Tag]s, as a result of a search.
|
* Fragment to show a list of [hashtag][Tag]s, as a result of a search.
|
||||||
@ -44,16 +45,15 @@ class SearchHashtagFragment : UncachedFeedFragment<Tag>() {
|
|||||||
|
|
||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||||
SearchContentRepository<Tag>(
|
SearchContentRepository<Tag>(
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
apiHolder.setToCurrentUser(),
|
||||||
Results.SearchType.hashtags,
|
Results.SearchType.hashtags,
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
|
||||||
query
|
query
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.get(FeedViewModel::class.java) as FeedViewModel<Tag>
|
.get("searchHashtag", FeedViewModel::class.java) as FeedViewModel<Tag>
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
||||||
@ -87,7 +87,7 @@ class HashTagAdapter : PagingDataAdapter<Tag, RecyclerView.ViewHolder>(
|
|||||||
companion object {
|
companion object {
|
||||||
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Tag>() {
|
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Tag>() {
|
||||||
override fun areItemsTheSame(oldItem: Tag, newItem: Tag): Boolean {
|
override fun areItemsTheSame(oldItem: Tag, newItem: Tag): Boolean {
|
||||||
return oldItem.id == newItem.id
|
return oldItem.name == newItem.name
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Tag, newItem: Tag): Boolean =
|
override fun areContentsTheSame(oldItem: Tag, newItem: Tag): Boolean =
|
||||||
@ -108,7 +108,9 @@ class HashTagViewHolder(binding: FragmentTagsBinding) : RecyclerView.ViewHolder(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
//TODO
|
tag?.apply {
|
||||||
|
openTag(itemView.context, this.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
package com.h.pixeldroid.posts.feeds.uncachedFeeds.search
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.search
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -15,16 +15,16 @@ class SearchPagingSource<T: FeedContent>(
|
|||||||
private val api: PixelfedAPI,
|
private val api: PixelfedAPI,
|
||||||
private val query: String,
|
private val query: String,
|
||||||
private val type: Results.SearchType,
|
private val type: Results.SearchType,
|
||||||
private val accessToken: String,
|
|
||||||
) : PagingSource<Int, T>() {
|
) : PagingSource<Int, T>() {
|
||||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
|
||||||
val position = params.key
|
val position = params.key
|
||||||
return try {
|
return try {
|
||||||
val response = api.search(authorization = "Bearer $accessToken",
|
val response = api.search(
|
||||||
offset = position?.toString(),
|
|
||||||
q = query,
|
|
||||||
type = type,
|
type = type,
|
||||||
limit = params.loadSize.toString())
|
q = query,
|
||||||
|
limit = params.loadSize.toString(),
|
||||||
|
offset = position?.toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@ -34,10 +34,12 @@ class SearchPagingSource<T: FeedContent>(
|
|||||||
Results.SearchType.statuses -> response.statuses
|
Results.SearchType.statuses -> response.statuses
|
||||||
} as List<T>
|
} as List<T>
|
||||||
|
|
||||||
|
val nextKey = if (repos.isEmpty()) null else (position ?: 0) + repos.size
|
||||||
|
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = repos,
|
data = repos,
|
||||||
prevKey = null,
|
prevKey = null,
|
||||||
nextKey = if (repos.isEmpty()) null else (position ?: 0) + repos.size
|
nextKey = if(nextKey == position) null else nextKey
|
||||||
)
|
)
|
||||||
} catch (exception: HttpException) {
|
} catch (exception: HttpException) {
|
||||||
LoadResult.Error(exception)
|
LoadResult.Error(exception)
|
||||||
@ -46,8 +48,9 @@ class SearchPagingSource<T: FeedContent>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRefreshKey(state: PagingState<Int, T>): Int? =
|
/**
|
||||||
state.anchorPosition?.run {
|
* FIXME if implemented with [PagingState.anchorPosition], this breaks refreshes? How is this
|
||||||
state.closestItemToPosition(this)?.id?.toIntOrNull()
|
* supposed to work?
|
||||||
}
|
*/
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, T>): Int? = null
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package com.h.pixeldroid.profile
|
package org.pixeldroid.app.profile
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
|
|
||||||
class FollowsActivity : BaseActivity() {
|
class FollowsActivity : BaseActivity() {
|
||||||
@ -31,11 +31,6 @@ class FollowsActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startFragment(id : String, displayName: String, followers : Boolean) {
|
private fun startFragment(id : String, displayName: String, followers : Boolean) {
|
||||||
supportActionBar?.title =
|
supportActionBar?.title =
|
||||||
if (followers) {
|
if (followers) {
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.profile
|
package org.pixeldroid.app.profile
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -7,6 +7,7 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -18,34 +19,31 @@ import androidx.recyclerview.widget.DiffUtil
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityProfileBinding
|
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
||||||
import com.h.pixeldroid.databinding.FragmentProfilePostsBinding
|
import org.pixeldroid.app.databinding.FragmentProfilePostsBinding
|
||||||
import com.h.pixeldroid.posts.PostActivity
|
import org.pixeldroid.app.posts.PostActivity
|
||||||
import com.h.pixeldroid.posts.feeds.initAdapter
|
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||||
import com.h.pixeldroid.posts.feeds.launch
|
import org.pixeldroid.app.posts.feeds.launch
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
|
||||||
import com.h.pixeldroid.posts.parseHTMLText
|
import org.pixeldroid.app.posts.parseHTMLText
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.ImageConverter
|
import org.pixeldroid.app.utils.ImageConverter
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
import com.h.pixeldroid.utils.openUrl
|
import org.pixeldroid.app.utils.openUrl
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProfileActivity : BaseActivity() {
|
class ProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var pixelfedAPI : PixelfedAPI
|
|
||||||
private lateinit var accessToken : String
|
|
||||||
private lateinit var domain : String
|
private lateinit var domain : String
|
||||||
private lateinit var accountId : String
|
private lateinit var accountId : String
|
||||||
private lateinit var binding: ActivityProfileBinding
|
private lateinit var binding: ActivityProfileBinding
|
||||||
@ -66,8 +64,6 @@ class ProfileActivity : BaseActivity() {
|
|||||||
user = db.userDao().getActiveUser()
|
user = db.userDao().getActiveUser()
|
||||||
|
|
||||||
domain = user?.instance_uri.orEmpty()
|
domain = user?.instance_uri.orEmpty()
|
||||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
|
||||||
accessToken = user?.accessToken.orEmpty()
|
|
||||||
|
|
||||||
// Set profile according to given account
|
// Set profile according to given account
|
||||||
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
||||||
@ -77,9 +73,8 @@ class ProfileActivity : BaseActivity() {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(this, ProfileViewModelFactory(
|
viewModel = ViewModelProvider(this, ProfileViewModelFactory(
|
||||||
ProfileContentRepository(
|
ProfileContentRepository(
|
||||||
apiHolder.setDomainToCurrentUser(db),
|
apiHolder.setToCurrentUser(),
|
||||||
db.userDao().getActiveUser()!!.accessToken,
|
accountId
|
||||||
accountId
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).get(FeedViewModel::class.java) as FeedViewModel<Status>
|
).get(FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
@ -96,9 +91,7 @@ class ProfileActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContent(account)
|
setContent(account)
|
||||||
@Suppress("UNCHECKED_CAST")
|
job = launch(job, lifecycleScope, viewModel, profileAdapter)
|
||||||
job = launch(job, lifecycleScope, viewModel as FeedViewModel<FeedContent>,
|
|
||||||
profileAdapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,18 +107,14 @@ class ProfileActivity : BaseActivity() {
|
|||||||
binding.profileRefreshLayout.isRefreshing = false
|
binding.profileRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setContent(account: Account?) {
|
private fun setContent(account: Account?) {
|
||||||
if(account != null) {
|
if(account != null) {
|
||||||
setViews(account)
|
setViews(account)
|
||||||
} else {
|
} else {
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
val myAccount: Account = try {
|
val myAccount: Account = try {
|
||||||
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
api.verifyCredentials()
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
Log.e("ProfileActivity:", exception.toString())
|
Log.e("ProfileActivity:", exception.toString())
|
||||||
return@launchWhenResumed showError()
|
return@launchWhenResumed showError()
|
||||||
@ -162,9 +151,9 @@ class ProfileActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
binding.descriptionTextView.text = parseHTMLText(
|
binding.descriptionTextView.text = parseHTMLText(
|
||||||
account.note ?: "", emptyList(), pixelfedAPI,
|
account.note ?: "", emptyList(), apiHolder,
|
||||||
applicationContext, "Bearer $accessToken",
|
applicationContext,
|
||||||
lifecycleScope
|
lifecycleScope
|
||||||
)
|
)
|
||||||
|
|
||||||
val displayName = account.getDisplayName()
|
val displayName = account.getDisplayName()
|
||||||
@ -235,13 +224,14 @@ class ProfileActivity : BaseActivity() {
|
|||||||
// Get relationship between the two users (credential and this) and set followButton accordingly
|
// Get relationship between the two users (credential and this) and set followButton accordingly
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val relationship = pixelfedAPI.checkRelationships(
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
"Bearer $accessToken", listOf(account.id.orEmpty())
|
val relationship = api.checkRelationships(
|
||||||
|
listOf(account.id.orEmpty())
|
||||||
).firstOrNull()
|
).firstOrNull()
|
||||||
|
|
||||||
if(relationship != null){
|
if(relationship != null){
|
||||||
if (relationship.following) {
|
if (relationship.following == true || relationship.requested == true) {
|
||||||
setOnClickUnfollow(account)
|
setOnClickUnfollow(account, relationship.requested == true)
|
||||||
} else {
|
} else {
|
||||||
setOnClickFollow(account)
|
setOnClickFollow(account)
|
||||||
}
|
}
|
||||||
@ -268,8 +258,10 @@ class ProfileActivity : BaseActivity() {
|
|||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
try {
|
try {
|
||||||
pixelfedAPI.follow(account.id.orEmpty(), "Bearer $accessToken")
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
setOnClickUnfollow(account)
|
val rel = api.follow(account.id.orEmpty())
|
||||||
|
if(rel.following == true) setOnClickUnfollow(account, rel.requested == true)
|
||||||
|
else setOnClickFollow(account)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
Log.e("FOLLOW ERROR", exception.toString())
|
Log.e("FOLLOW ERROR", exception.toString())
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -287,29 +279,46 @@ class ProfileActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setOnClickUnfollow(account: Account) {
|
private fun setOnClickUnfollow(account: Account, requested: Boolean) {
|
||||||
binding.followButton.apply {
|
binding.followButton.apply {
|
||||||
setText(R.string.unfollow)
|
if(account.locked == true && requested) {
|
||||||
|
setText(R.string.follow_requested)
|
||||||
|
} else setText(R.string.unfollow)
|
||||||
|
|
||||||
setOnClickListener {
|
|
||||||
|
fun unfollow() {
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
try {
|
try {
|
||||||
pixelfedAPI.unfollow(account.id.orEmpty(), "Bearer $accessToken")
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
setOnClickFollow(account)
|
val rel = api.unfollow(account.id.orEmpty())
|
||||||
|
if(rel.following == false && rel.requested == false) setOnClickFollow(account)
|
||||||
|
else setOnClickUnfollow(account, rel.requested == true)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
Log.e("FOLLOW ERROR", exception.toString())
|
Log.e("FOLLOW ERROR", exception.toString())
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext, getString(R.string.unfollow_error),
|
applicationContext, getString(R.string.unfollow_error),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
} catch (exception: HttpException) {
|
} catch (exception: HttpException) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
applicationContext, getString(R.string.unfollow_error),
|
applicationContext, getString(R.string.unfollow_error),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
if(account.locked == true && requested){
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setMessage(R.string.dialog_message_cancel_follow_request)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
unfollow()
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel){_, _ -> }
|
||||||
|
.show()
|
||||||
|
} else unfollow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.profile
|
package org.pixeldroid.app.profile
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -7,11 +7,11 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.posts.PostActivity
|
import org.pixeldroid.app.posts.PostActivity
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromDrawable
|
import org.pixeldroid.app.utils.ImageConverter.Companion.setSquareImageFromDrawable
|
||||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
|
import org.pixeldroid.app.utils.ImageConverter.Companion.setSquareImageFromURL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [RecyclerView.Adapter] that can display a list of [Status]s
|
* [RecyclerView.Adapter] that can display a list of [Status]s
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.searchDiscover
|
package org.pixeldroid.app.searchDiscover
|
||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -8,12 +8,12 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
|
|||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
||||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchPostsFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
||||||
import com.h.pixeldroid.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
class SearchActivity : BaseActivity() {
|
class SearchActivity : BaseActivity() {
|
||||||
|
|
||||||
@ -47,14 +47,9 @@ class SearchActivity : BaseActivity() {
|
|||||||
setupTabs(tabs, searchType)
|
setupTabs(tabs, searchType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createSearchTabs(query: String): Array<Fragment>{
|
private fun createSearchTabs(query: String): Array<Fragment>{
|
||||||
|
|
||||||
val searchFeedFragment = SearchPostsFragment()
|
val searchFeedFragment = UncachedPostsFragment()
|
||||||
val searchAccountListFragment =
|
val searchAccountListFragment =
|
||||||
SearchAccountFragment()
|
SearchAccountFragment()
|
||||||
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.searchDiscover
|
package org.pixeldroid.app.searchDiscover
|
||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -11,21 +11,15 @@ import androidx.annotation.StringRes
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.FragmentSearchBinding
|
import org.pixeldroid.app.databinding.FragmentSearchBinding
|
||||||
import com.h.pixeldroid.profile.ProfilePostViewHolder
|
import org.pixeldroid.app.profile.ProfilePostViewHolder
|
||||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import com.h.pixeldroid.posts.PostActivity
|
import org.pixeldroid.app.posts.PostActivity
|
||||||
import com.h.pixeldroid.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
import com.h.pixeldroid.utils.ImageConverter
|
import org.pixeldroid.app.utils.ImageConverter
|
||||||
import com.h.pixeldroid.utils.bindingLifecycleAware
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
import com.mikepenz.iconics.IconicsColor
|
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
|
||||||
import com.mikepenz.iconics.utils.color
|
|
||||||
import com.mikepenz.iconics.utils.paddingDp
|
|
||||||
import com.mikepenz.iconics.utils.sizeDp
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -37,7 +31,6 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
private lateinit var api: PixelfedAPI
|
private lateinit var api: PixelfedAPI
|
||||||
private lateinit var recycler : RecyclerView
|
private lateinit var recycler : RecyclerView
|
||||||
private lateinit var adapter : DiscoverRecyclerViewAdapter
|
private lateinit var adapter : DiscoverRecyclerViewAdapter
|
||||||
private lateinit var accessToken: String
|
|
||||||
|
|
||||||
var binding: FragmentSearchBinding by bindingLifecycleAware()
|
var binding: FragmentSearchBinding by bindingLifecycleAware()
|
||||||
|
|
||||||
@ -66,9 +59,7 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
accessToken = db.userDao().getActiveUser()?.accessToken.orEmpty()
|
|
||||||
|
|
||||||
getDiscover()
|
getDiscover()
|
||||||
|
|
||||||
@ -93,8 +84,9 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
private fun getDiscover() {
|
private fun getDiscover() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
try {
|
try {
|
||||||
val discoverPosts = api.discover("Bearer $accessToken")
|
val discoverPosts = api.discover()
|
||||||
adapter.addPosts(discoverPosts.posts)
|
adapter.addPosts(discoverPosts.posts)
|
||||||
|
binding.discoverNoInfiniteLoad.visibility = View.VISIBLE
|
||||||
showError(show = false)
|
showError(show = false)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: IOException) {
|
||||||
showError()
|
showError()
|
||||||
@ -108,7 +100,7 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
* [RecyclerView.Adapter] that can display a list of [Status]s' thumbnails for the discover view
|
* [RecyclerView.Adapter] that can display a list of [Status]s' thumbnails for the discover view
|
||||||
*/
|
*/
|
||||||
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
|
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
|
||||||
private val posts: ArrayList<Status> = ArrayList()
|
private val posts: ArrayList<Status?> = ArrayList()
|
||||||
|
|
||||||
fun addPosts(newPosts : List<Status>) {
|
fun addPosts(newPosts : List<Status>) {
|
||||||
posts.clear()
|
posts.clear()
|
||||||
@ -124,12 +116,12 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
||||||
val post = posts[position]
|
val post = posts[position]
|
||||||
if(post.media_attachments?.size ?: 0 > 1) {
|
if(post?.media_attachments?.size ?: 0 > 1) {
|
||||||
holder.albumIcon.visibility = View.VISIBLE
|
holder.albumIcon.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
holder.albumIcon.visibility = View.GONE
|
holder.albumIcon.visibility = View.GONE
|
||||||
}
|
}
|
||||||
ImageConverter.setSquareImageFromURL(holder.postView, post.media_attachments?.firstOrNull()?.preview_url, holder.postPreview, post.media_attachments?.firstOrNull()?.blurhash)
|
ImageConverter.setSquareImageFromURL(holder.postView, post?.media_attachments?.firstOrNull()?.preview_url, holder.postPreview, post?.media_attachments?.firstOrNull()?.blurhash)
|
||||||
holder.postPreview.setOnClickListener {
|
holder.postPreview.setOnClickListener {
|
||||||
val intent = Intent(holder.postView.context, PostActivity::class.java)
|
val intent = Intent(holder.postView.context, PostActivity::class.java)
|
||||||
intent.putExtra(Status.POST_TAG, post)
|
intent.putExtra(Status.POST_TAG, post)
|
@ -1,11 +1,11 @@
|
|||||||
package com.h.pixeldroid.settings
|
package org.pixeldroid.app.settings
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.h.pixeldroid.BuildConfig
|
import org.pixeldroid.app.BuildConfig
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityAboutBinding
|
import org.pixeldroid.app.databinding.ActivityAboutBinding
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
class AboutActivity : BaseActivity() {
|
class AboutActivity : BaseActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
@ -1,9 +1,9 @@
|
|||||||
package com.h.pixeldroid.settings
|
package org.pixeldroid.app.settings
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.databinding.ActivityLicensesBinding
|
import org.pixeldroid.app.databinding.ActivityLicensesBinding
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
class LicenseActivity : BaseActivity() {
|
class LicenseActivity : BaseActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
@ -1,14 +1,14 @@
|
|||||||
package com.h.pixeldroid.settings
|
package org.pixeldroid.app.settings
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.h.pixeldroid.MainActivity
|
import org.pixeldroid.app.MainActivity
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
import com.h.pixeldroid.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import com.h.pixeldroid.utils.setThemeFromPreferences
|
import org.pixeldroid.app.utils.setThemeFromPreferences
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private var restartMainOnExit = false
|
private var restartMainOnExit = false
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@ -7,8 +7,8 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -28,6 +28,11 @@ open class BaseActivity : AppCompatActivity() {
|
|||||||
super.attachBaseContext(updateBaseContextLocale(base))
|
super.attachBaseContext(updateBaseContextLocale(base))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateBaseContextLocale(context: Context): Context {
|
private fun updateBaseContextLocale(context: Context): Context {
|
||||||
val language = PreferenceManager.getDefaultSharedPreferences(context).getString("language", "default") ?: "default"
|
val language = PreferenceManager.getDefaultSharedPreferences(context).getString("language", "default") ?: "default"
|
||||||
if(language == "default"){
|
if(language == "default"){
|
@ -1,9 +1,9 @@
|
|||||||
package com.h.pixeldroid.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.h.pixeldroid.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blurhash implementation from blurhash project:
|
* Blurhash implementation from blurhash project:
|
||||||
@ -11,7 +11,6 @@ import android.content.res.Resources
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
@ -1,4 +1,4 @@
|
|||||||
package com.h.pixeldroid.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.h.pixeldroid.R
|
import org.pixeldroid.app.R
|
||||||
|
|
||||||
class ImageConverter {
|
class ImageConverter {
|
||||||
companion object {
|
companion object {
|
@ -1,11 +1,10 @@
|
|||||||
package com.h.pixeldroid.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.h.pixeldroid.utils.di.*
|
import org.pixeldroid.app.utils.di.*
|
||||||
import com.mikepenz.iconics.Iconics
|
import com.mikepenz.iconics.Iconics
|
||||||
import org.ligi.tracedroid.TraceDroid
|
import org.ligi.tracedroid.TraceDroid
|
||||||
import org.ligi.tracedroid.sending.sendTraceDroidStackTracesIfExist
|
|
||||||
|
|
||||||
|
|
||||||
class PixelDroidApplication: Application() {
|
class PixelDroidApplication: Application() {
|
||||||
@ -27,8 +26,6 @@ class PixelDroidApplication: Application() {
|
|||||||
.aPIModule(APIModule())
|
.aPIModule(APIModule())
|
||||||
.build()
|
.build()
|
||||||
mApplicationComponent.inject(this)
|
mApplicationComponent.inject(this)
|
||||||
|
|
||||||
Iconics.init(applicationContext)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAppComponent(): ApplicationComponent {
|
fun getAppComponent(): ApplicationComponent {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user