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
|
||||
.idea
|
||||
app/release
|
||||
app/debug
|
||||
app/lint
|
||||
lint
|
||||
|
@ -7,6 +7,14 @@ variables:
|
||||
TARGET: "default"
|
||||
|
||||
|
||||
before_script:
|
||||
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .gradle/wrapper
|
||||
- .gradle/caches
|
||||
|
||||
# Basic android and gradle stuff
|
||||
# Check linting
|
||||
lintDebug:
|
||||
@ -52,6 +60,62 @@ emulatorTest:
|
||||
- cat app/build/reports/jacoco/jacocoTestReport/html/index.html | grep -o 'Total[^%]*%'
|
||||
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ./app/build/reports/jacoco/jacocoTestReport/
|
||||
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 project logo](pixeldroid_logo.png)
|
||||
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)
|
||||
|
||||
[![Build Status](https://gitlab.shinice.net/pixeldroid/PixelDroid/badges/master/pipeline.svg)](https://gitlab.shinice.net/pixeldroid/PixelDroid/pipelines) [![Maintainability](https://api.codeclimate.com/v1/badges/a4f1747dc60b96eb74df/maintainability)](https://codeclimate.com/github/H-PixelDroid/PixelDroid/maintainability) [![Translation status](https://weblate.pixeldroid.org/widgets/pixeldroid/-/pixeldroid/svg-badge.svg)](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
|
||||
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.
110
app/build.gradle
110
app/build.gradle
@ -7,6 +7,9 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
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 {
|
||||
|
||||
@ -21,11 +24,11 @@ android {
|
||||
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId "com.h.pixeldroid"
|
||||
applicationId "org.pixeldroid.app"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 30
|
||||
versionCode 10
|
||||
versionName "1.0.alpha9"
|
||||
versionCode 3
|
||||
versionName "1.0.beta3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
@ -36,15 +39,15 @@ android {
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/java'
|
||||
test.java.srcDirs += 'src/test/java'
|
||||
staging.res.srcDirs += 'src/debug/res'
|
||||
androidTest.java.srcDirs += 'src/androidTest/java'
|
||||
}
|
||||
testBuildType "staging"
|
||||
|
||||
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix "-debug"
|
||||
}
|
||||
staging {
|
||||
initWith debug
|
||||
@ -61,12 +64,12 @@ android {
|
||||
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
|
||||
}
|
||||
|
||||
buildConfigField "String", "USER_ID", System.getenv("USER_ID") ?: localProperties['USER_ID']
|
||||
buildConfigField "String", "INSTANCE_URI", System.getenv("INSTANCE_URI") ?: localProperties['INSTANCE_URI']
|
||||
buildConfigField "String", "ACCESS_TOKEN", System.getenv("ACCESS_TOKEN") ?: localProperties['ACCESS_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_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET']
|
||||
buildConfigField "String", "USER_ID", System.getenv("USER_ID") ?: localProperties['USER_ID'] ?: ""
|
||||
buildConfigField "String", "INSTANCE_URI", System.getenv("INSTANCE_URI") ?: localProperties['INSTANCE_URI'] ?: ""
|
||||
buildConfigField "String", "ACCESS_TOKEN", System.getenv("ACCESS_TOKEN") ?: localProperties['ACCESS_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_SECRET", System.getenv("CLIENT_SECRET") ?: localProperties['CLIENT_SECRET'] ?: ""
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
@ -74,6 +77,14 @@ android {
|
||||
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 {
|
||||
animationsDisabled true
|
||||
|
||||
@ -92,37 +103,38 @@ dependencies {
|
||||
/**
|
||||
* AndroidX dependencies:
|
||||
*/
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||
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.navigation:navigation-fragment-ktx:2.3.4'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
|
||||
implementation 'androidx.paging:paging-runtime-ktx:3.0.0-beta02'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.0"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||
implementation 'androidx.paging:paging-runtime-ktx:3.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
|
||||
implementation "androidx.annotation:annotation:1.2.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
|
||||
def cameraX_version = '1.0.0-rc03'
|
||||
def cameraX_version = '1.0.0'
|
||||
implementation "androidx.camera:camera-core:${cameraX_version}"
|
||||
implementation "androidx.camera:camera-camera2:${cameraX_version}"
|
||||
// CameraX Lifecycle library
|
||||
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
|
||||
|
||||
// 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"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
@ -136,17 +148,17 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
|
||||
//Dagger (dependency injection)
|
||||
implementation 'com.google.dagger:dagger-android:2.30.1'
|
||||
implementation 'com.google.dagger:dagger-android-support:2.30.1'
|
||||
implementation 'com.google.dagger:dagger-android:2.37'
|
||||
implementation 'com.google.dagger:dagger-android-support:2.37'
|
||||
// if you use the support libraries
|
||||
kapt 'com.google.dagger:dagger-android-processor:2.30.1'
|
||||
kapt 'com.google.dagger:dagger-compiler:2.30.1'
|
||||
kapt 'com.google.dagger:dagger-android-processor:2.37'
|
||||
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:converter-gson: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 'com.github.connyduck:sparkbutton:4.1.0'
|
||||
|
||||
@ -154,26 +166,26 @@ dependencies {
|
||||
implementation 'info.androidhive:imagefilters:1.0.7'
|
||||
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"
|
||||
}
|
||||
|
||||
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
|
||||
implementation("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||
implementation 'com.github.bumptech.glide:okhttp-integration:4.12.0'
|
||||
implementation('com.github.bumptech.glide:recyclerview-integration:4.12.0') {
|
||||
// Excludes the support library because it's already included by Glide.
|
||||
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 "com.mikepenz:materialdrawer:8.1.8"
|
||||
implementation 'com.mikepenz:materialdrawer:8.4.1'
|
||||
// Add for NavController support
|
||||
implementation "com.mikepenz:materialdrawer-nav:8.1.5"
|
||||
implementation 'com.mikepenz:materialdrawer-nav:8.4.0'
|
||||
|
||||
//iconics
|
||||
implementation "com.mikepenz:iconics-core:5.0.3"
|
||||
implementation "com.mikepenz:materialdrawer-iconics:8.1.8"
|
||||
implementation "com.mikepenz:iconics-core:5.2.8"
|
||||
implementation 'com.mikepenz:materialdrawer-iconics:8.4.1'
|
||||
implementation "com.mikepenz:iconics-views:5.0.3"
|
||||
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
|
||||
//noinspection FragmentGradleConfiguration
|
||||
stagingImplementation("androidx.fragment:fragment-testing:1.3.1") {
|
||||
stagingImplementation("androidx.fragment:fragment-testing:1.3.5") {
|
||||
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.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"
|
||||
|
||||
|
||||
@ -227,10 +241,10 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['connectedStagingAndroidTe
|
||||
}
|
||||
|
||||
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"
|
||||
getSourceDirectories().from(files([mainSrc]))
|
||||
getClassDirectories().from(files([kotlinDebugTree]))
|
||||
getClassDirectories().from(files([kotlinTree]))
|
||||
getExecutionData().from(fileTree(dir: project.buildDir, includes: [
|
||||
|
||||
'outputs/code_coverage/stagingAndroidTest/connected/*coverage.ec',
|
||||
@ -238,4 +252,4 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['connectedStagingAndroidTe
|
||||
'jacoco/testStagingUnitTest.exec'
|
||||
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,12 @@
|
||||
license: The Apache Software License, Version 2.0
|
||||
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
url: https://github.com/mikepenz/MaterialDrawer
|
||||
- artifact: org.jetbrains.kotlin:kotlin-android-extensions-runtime:+
|
||||
name: kotlin-android-extensions-runtime
|
||||
copyrightHolder: JetBrains s.r.o. and contributors
|
||||
license: The Apache License, Version 2.0
|
||||
- artifact: androidx.startup:startup-runtime:+
|
||||
name: startup-runtime
|
||||
copyrightHolder: Google Inc.
|
||||
license: The Apache Software License, Version 2.0
|
||||
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:+
|
||||
name: iconics-views
|
||||
copyrightHolder: Mike Penz and contributors
|
||||
@ -793,4 +793,4 @@
|
||||
copyrightHolder: Google Inc
|
||||
license: The Apache Software License, Version 2.0
|
||||
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
url: https://developer.android.com/jetpack/androidx/releases/tracing#1.0.0
|
||||
url: https://developer.android.com/jetpack/androidx/releases/tracing#1.0.0
|
||||
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
@ -37,9 +37,9 @@
|
||||
# APP SPECIFIC OPTIONS
|
||||
|
||||
# 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;
|
||||
public *;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.Manifest
|
||||
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.matcher.IntentMatchers
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
import com.h.pixeldroid.postCreation.camera.CameraFragment
|
||||
import com.h.pixeldroid.testUtility.clearData
|
||||
import com.h.pixeldroid.testUtility.initDB
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.initDB
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.Matcher
|
||||
import org.junit.After
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
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.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import org.hamcrest.Matchers.allOf
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@ -107,8 +108,10 @@ class DrawerMenuTest {
|
||||
// Start the screen of your activity.
|
||||
onView(withText(R.string.menu_account)).perform(click())
|
||||
// Check that profile activity was opened.
|
||||
waitForView(R.id.editButton)
|
||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||
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())
|
||||
|
||||
waitForView(R.id.account_entry_avatar)
|
||||
@ -120,8 +123,10 @@ class DrawerMenuTest {
|
||||
// Start the screen of your activity.
|
||||
onView(withText(R.string.menu_account)).perform(click())
|
||||
// Check that profile activity was opened.
|
||||
waitForView(R.id.editButton)
|
||||
onView(withId(R.id.editButton)).check(matches(isDisplayed()))
|
||||
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())
|
||||
|
||||
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.Intent
|
||||
@ -22,13 +22,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
|
||||
import com.h.pixeldroid.postCreation.photoEdit.ThumbnailAdapter
|
||||
import com.h.pixeldroid.settings.AboutActivity
|
||||
import com.h.pixeldroid.testUtility.clearData
|
||||
import com.h.pixeldroid.testUtility.clickChildViewWithId
|
||||
import com.h.pixeldroid.testUtility.slowSwipeLeft
|
||||
import com.h.pixeldroid.testUtility.waitForView
|
||||
import org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity
|
||||
import org.pixeldroid.app.postCreation.photoEdit.ThumbnailAdapter
|
||||
import org.pixeldroid.app.settings.AboutActivity
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.clickChildViewWithId
|
||||
import org.pixeldroid.app.testUtility.slowSwipeLeft
|
||||
import org.pixeldroid.app.testUtility.waitForView
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.junit.*
|
||||
import org.junit.Assert.assertTrue
|
||||
@ -70,7 +70,9 @@ class EditPhotoTest {
|
||||
file.writeBitmap(image)
|
||||
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}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
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.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||
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.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.posts.StatusViewHolder
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.posts.StatusViewHolder
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.hamcrest.core.IsInstanceOf
|
||||
import org.hamcrest.core.StringContains.containsString
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class HomeFeedTest {
|
||||
@ -31,8 +37,9 @@ class HomeFeedTest {
|
||||
private lateinit var db: AppDatabase
|
||||
private lateinit var context: Context
|
||||
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
@Rule @JvmField
|
||||
var repeatRule: RepeatRule = RepeatRule()
|
||||
|
||||
|
||||
@Before
|
||||
fun before(){
|
||||
@ -47,6 +54,10 @@ class HomeFeedTest {
|
||||
)
|
||||
db.close()
|
||||
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
waitForView(R.id.username)
|
||||
onView(withId(R.id.list)).perform(scrollToPosition<StatusViewHolder>(0))
|
||||
|
||||
}
|
||||
@After
|
||||
fun after() {
|
||||
@ -54,6 +65,7 @@ class HomeFeedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RepeatTest
|
||||
fun clickingTabOnAlbumShowsNextPhoto() {
|
||||
//Wait for the feed to load
|
||||
waitForView(R.id.postPager)
|
||||
@ -66,6 +78,40 @@ class HomeFeedTest {
|
||||
}
|
||||
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
|
||||
fun clickingReblogButtonWorks() {
|
||||
@ -127,6 +173,7 @@ class HomeFeedTest {
|
||||
}*/
|
||||
|
||||
@Test
|
||||
@RepeatTest
|
||||
fun clickingUsernameOpensProfile() {
|
||||
waitForView(R.id.username)
|
||||
|
||||
@ -137,6 +184,7 @@ class HomeFeedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RepeatTest
|
||||
fun clickingProfilePicOpensProfile() {
|
||||
waitForView(R.id.profilePic)
|
||||
|
||||
@ -147,6 +195,7 @@ class HomeFeedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RepeatTest
|
||||
fun clickingMentionOpensProfile() {
|
||||
waitForView(R.id.description)
|
||||
|
||||
@ -156,13 +205,6 @@ class HomeFeedTest {
|
||||
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
|
||||
@ -217,6 +259,7 @@ class HomeFeedTest {
|
||||
.check(matches(hasDescendant(withId(R.id.comment))))
|
||||
}*/
|
||||
|
||||
@RepeatTest
|
||||
@Test
|
||||
fun performClickOnSensitiveWarning() {
|
||||
waitForView(R.id.username)
|
||||
@ -232,11 +275,10 @@ class HomeFeedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RepeatTest
|
||||
fun performClickOnSensitiveWarningTabs() {
|
||||
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(withId(R.id.list))
|
@ -1,17 +1,10 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
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.ApplicationProvider
|
||||
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.assertion.ViewAssertions
|
||||
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.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
import com.h.pixeldroid.posts.StatusViewHolder
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||
import com.h.pixeldroid.settings.AboutActivity
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.posts.StatusViewHolder
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||
import org.pixeldroid.app.settings.AboutActivity
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
@ -1,5 +1,5 @@
|
||||
package com.h.pixeldroid
|
||||
/*
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
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.withId
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import androidx.test.uiautomator.UiSelector
|
||||
import com.h.pixeldroid.testUtility.clearData
|
||||
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.*
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.initDB
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
import org.pixeldroid.app.testUtility.waitForView
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@Ignore("Ignore until we can get TestButler to work on CI")
|
||||
class LoginActivityOfflineTest {
|
||||
|
||||
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
|
||||
|
||||
@get:Rule
|
||||
@ -43,7 +30,8 @@ class LoginActivityOfflineTest {
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
switchAirplaneMode()
|
||||
//TestButler.setWifiState(false)
|
||||
//TestButler.setGsmState(false)
|
||||
context = ApplicationProvider.getApplicationContext<Context>()
|
||||
db = initDB(context)
|
||||
db.clearAllTables()
|
||||
@ -52,19 +40,22 @@ class LoginActivityOfflineTest {
|
||||
|
||||
@Test
|
||||
fun emptyDBandOfflineModeDisplayCorrectMessage() {
|
||||
waitForView(R.id.login_activity_connection_required)
|
||||
onView(withId(R.id.login_activity_connection_required)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
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)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
switchAirplaneMode()
|
||||
//TestButler.setWifiState(true)
|
||||
//TestButler.setGsmState(true)
|
||||
db.close()
|
||||
clearData()
|
||||
}
|
||||
}*/
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
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.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasErrorText
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
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.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.initDB
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
import org.pixeldroid.app.testUtility.PACKAGE_ID
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LoginActivityOnlineTest {
|
||||
@ -44,7 +40,7 @@ class LoginActivityOnlineTest {
|
||||
fun setup() {
|
||||
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()
|
||||
db = initDB(context)
|
||||
db.clearAllTables()
|
||||
@ -81,7 +77,7 @@ class LoginActivityOnlineTest {
|
||||
.putString("clientID", "iwndoiuqwnd")
|
||||
.putString("clientSecret", "wlifowed")
|
||||
.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)
|
||||
ActivityScenario.launch<LoginActivity>(intent)
|
||||
onView(withId(R.id.editText)).check(matches(
|
||||
@ -91,7 +87,7 @@ class LoginActivityOnlineTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
ActivityScenario.launch<LoginActivity>(intent)
|
||||
onView(withId(R.id.editText)).check(matches(
|
||||
@ -117,7 +113,7 @@ class LoginActivityOnlineTest {
|
||||
.putString("clientID", testiTesto.clientId)
|
||||
.putString("clientSecret", testiTesto.clientSecret)
|
||||
.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)
|
||||
ActivityScenario.launch<LoginActivity>(intent)
|
||||
Thread.sleep(1000)
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Intent
|
||||
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.ViewMatchers.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import com.h.pixeldroid.BuildConfig.INSTANCE_URI
|
||||
import com.h.pixeldroid.testUtility.clearData
|
||||
import com.h.pixeldroid.testUtility.waitForView
|
||||
import org.pixeldroid.app.BuildConfig.INSTANCE_URI
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.waitForView
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.hamcrest.CoreMatchers.containsString
|
||||
import org.hamcrest.Matcher
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
|
||||
import android.content.Context
|
||||
@ -11,8 +11,8 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
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.Before
|
||||
import org.junit.Test
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ClipData
|
||||
@ -7,26 +7,21 @@ import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
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.contrib.RecyclerViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
||||
import com.h.pixeldroid.postCreation.photoEdit.ThumbnailAdapter
|
||||
import com.h.pixeldroid.settings.AboutActivity
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||
import org.pixeldroid.app.settings.AboutActivity
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.junit.*
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
@ -44,7 +39,8 @@ class PostCreationActivityTest {
|
||||
val globalTimeout: Timeout = Timeout.seconds(30)
|
||||
|
||||
@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) {
|
||||
outputStream().use { out ->
|
||||
@ -112,32 +108,101 @@ class PostCreationActivityTest {
|
||||
// should send on main activity
|
||||
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
|
||||
fun editImage() {
|
||||
Thread.sleep(1000)
|
||||
waitForView(R.id.postTextInputLayout)
|
||||
|
||||
onView(withId(R.id.image_grid)).perform(
|
||||
RecyclerViewActions.actionOnItemAtPosition<PostCreationActivity.PostCreationAdapter.ViewHolder>(
|
||||
0,
|
||||
CustomMatchers.clickChildViewWithId(R.id.galleryImage)
|
||||
)
|
||||
)
|
||||
Thread.sleep(1000)
|
||||
onView(withId(R.id.editPhotoButton)).perform(click())
|
||||
|
||||
onView(withId(R.id.recycler_view))
|
||||
.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()))
|
||||
waitForView(R.id.cropImageButton)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
fun cancelEdit() {
|
||||
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.Intent
|
||||
@ -17,8 +17,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.hamcrest.Matcher
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -13,16 +13,14 @@ import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.h.pixeldroid.BuildConfig.INSTANCE_URI
|
||||
import com.h.pixeldroid.posts.PostActivity
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
import com.h.pixeldroid.utils.api.objects.*
|
||||
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.pixeldroid.app.BuildConfig.INSTANCE_URI
|
||||
import org.pixeldroid.app.posts.PostActivity
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.api.objects.*
|
||||
import org.pixeldroid.app.testUtility.clearData
|
||||
import org.pixeldroid.app.testUtility.initDB
|
||||
import org.pixeldroid.app.testUtility.testiTesto
|
||||
import org.pixeldroid.app.testUtility.testiTestoInstance
|
||||
import org.hamcrest.CoreMatchers.anyOf
|
||||
import org.hamcrest.Matcher
|
||||
import org.junit.*
|
@ -1,29 +1,24 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.action.ViewActions.swipeDown
|
||||
import androidx.test.espresso.assertion.ViewAssertions
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
||||
import com.h.pixeldroid.profile.ProfileActivity
|
||||
import com.h.pixeldroid.testUtility.*
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import okhttp3.internal.wait
|
||||
import org.pixeldroid.app.profile.ProfileActivity
|
||||
import org.pixeldroid.app.testUtility.*
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.Serializable
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ProfileTest {
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.testUtility
|
||||
package org.pixeldroid.app.testUtility
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ClickableSpan
|
||||
@ -8,6 +8,7 @@ import android.widget.TextView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.espresso.*
|
||||
import androidx.test.espresso.NoMatchingViewException
|
||||
import androidx.test.espresso.action.*
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
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.util.HumanReadables
|
||||
import androidx.test.espresso.util.TreeIterables
|
||||
import com.h.pixeldroid.R
|
||||
import org.hamcrest.BaseMatcher
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
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
|
||||
|
||||
|
||||
@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 {
|
||||
return try {
|
||||
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!)
|
||||
* @param viewId The id of the view to wait for.
|
||||
*/
|
||||
fun waitForView(viewId: Int) {
|
||||
Espresso.onView(isRoot()).perform(waitForViewViewAction(viewId))
|
||||
fun waitForView(viewId: Int, viewMatcher: Matcher<View> = withId(viewId)) {
|
||||
Espresso.onView(isRoot()).perform(waitForViewViewAction(viewId, viewMatcher))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
val timeOut = 5000
|
||||
return object : ViewAction {
|
||||
@ -62,7 +91,6 @@ private fun waitForViewViewAction(viewId: Int): ViewAction {
|
||||
uiController.loopMainThreadUntilIdle()
|
||||
val startTime = System.currentTimeMillis()
|
||||
val endTime = startTime + timeOut
|
||||
val viewMatcher = withId(viewId)
|
||||
|
||||
do {
|
||||
// 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.
|
||||
// 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)
|
||||
} 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()
|
||||
.withCause(TimeoutException())
|
||||
.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 {
|
||||
|
||||
override fun getConstraints() = null
|
@ -1,8 +1,8 @@
|
||||
package com.h.pixeldroid.testUtility
|
||||
package org.pixeldroid.app.testUtility
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
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(
|
||||
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"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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.ACCESS_NETWORK_STATE" />
|
||||
@ -23,7 +23,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:replace="android:allowBackup">
|
||||
<activity android:name="com.h.pixeldroid.postCreation.camera.CameraActivity" />
|
||||
<activity android:name=".postCreation.camera.CameraActivity" />
|
||||
<activity
|
||||
android:name=".posts.ReportActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
@ -33,11 +33,26 @@
|
||||
android:name=".postCreation.PostCreationActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
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
|
||||
android:name=".profile.FollowsActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
tools:ignore="LockedOrientationActivity" />
|
||||
<activity
|
||||
android:name=".posts.feeds.uncachedFeeds.hashtags.HashTagActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
tools:ignore="LockedOrientationActivity" />
|
||||
<activity
|
||||
android:name=".posts.PostActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
@ -66,7 +81,7 @@
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".searchDiscover.SearchActivity" />
|
||||
android:value="org.pixeldroid.app.searchDiscover.SearchActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
@ -116,7 +131,7 @@
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.h.pixeldroid.fileprovider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
|
6
app/src/main/assets/licenses.html
generated
6
app/src/main/assets/licenses.html
generated
@ -818,9 +818,9 @@
|
||||
</div>
|
||||
<div class="library">
|
||||
<!-- https://opensource.org/licenses/Apache-2.0 -->
|
||||
<h1 class="title">kotlin-android-extensions-runtime</h1>
|
||||
<p class="notice">Copyright © JetBrains s.r.o. and contributors. All rights reserved.</p>
|
||||
<p><a href="https://kotlinlang.org/">https://kotlinlang.org/</a></p>
|
||||
<h1 class="title">startup-runtime</h1>
|
||||
<p class="notice">Copyright © Google Inc. All rights reserved.</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>
|
||||
<div class="license">
|
||||
<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.content.Context
|
||||
@ -9,12 +9,12 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.h.pixeldroid.databinding.ActivityLoginBinding
|
||||
import com.h.pixeldroid.utils.*
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.*
|
||||
import com.h.pixeldroid.utils.db.addUser
|
||||
import com.h.pixeldroid.utils.db.storeInstance
|
||||
import org.pixeldroid.app.databinding.ActivityLoginBinding
|
||||
import org.pixeldroid.app.utils.*
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.*
|
||||
import org.pixeldroid.app.utils.db.addUser
|
||||
import org.pixeldroid.app.utils.db.storeInstance
|
||||
import kotlinx.coroutines.*
|
||||
import retrofit2.HttpException
|
||||
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)
|
||||
|
||||
_________________________________
|
||||
|[PixelfedAPI.registerApplicationAsync]|
|
||||
|[PixelfedAPI.registerApplication]|
|
||||
|[PixelfedAPI.wellKnownNodeInfo] |
|
||||
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.nodeInfoSchema]
|
||||
+----> [promptOAuth]
|
||||
+---->____________________________
|
||||
|[PixelfedAPI.instance] |
|
||||
|[PixelfedAPI.obtainToken] |
|
||||
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.verifyCredentials]
|
||||
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅
|
||||
+----> [PixelfedAPI.nodeInfoSchema] (and then [PixelfedAPI.instance] if needed)
|
||||
+----> [promptOAuth]
|
||||
+----> [PixelfedAPI.obtainToken]
|
||||
+----> [PixelfedAPI.verifyCredentials]
|
||||
|
||||
*/
|
||||
|
||||
@ -311,7 +310,7 @@ class LoginActivity : BaseActivity() {
|
||||
clientId = clientId,
|
||||
clientSecret = clientSecret
|
||||
)
|
||||
apiHolder.setDomainToCurrentUser(db)
|
||||
apiHolder.setToCurrentUser()
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid
|
||||
package org.pixeldroid.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -14,22 +14,12 @@ import androidx.core.view.GravityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
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.materialdrawer.iconics.iconicsIcon
|
||||
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.widget.AccountHeaderView
|
||||
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 java.io.IOException
|
||||
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
private lateinit var header: AccountHeaderView
|
||||
@ -72,23 +78,22 @@ class MainActivity : BaseActivity() {
|
||||
sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this)
|
||||
|
||||
setupDrawer()
|
||||
|
||||
val tabs: List<() -> Fragment> = listOf(
|
||||
{
|
||||
PostFeedFragment<HomeStatusDatabaseEntity>()
|
||||
.apply {
|
||||
arguments = Bundle().apply { putBoolean("home", true) }
|
||||
}
|
||||
},
|
||||
{ SearchDiscoverFragment() },
|
||||
{ CameraFragment() },
|
||||
{ NotificationsFragment() },
|
||||
{
|
||||
PostFeedFragment<PublicFeedStatusDatabaseEntity>()
|
||||
.apply {
|
||||
arguments = Bundle().apply { putBoolean("home", false) }
|
||||
}
|
||||
}
|
||||
{
|
||||
PostFeedFragment<HomeStatusDatabaseEntity>()
|
||||
.apply {
|
||||
arguments = Bundle().apply { putBoolean("home", true) }
|
||||
}
|
||||
},
|
||||
{ SearchDiscoverFragment() },
|
||||
{ CameraFragment() },
|
||||
{ NotificationsFragment() },
|
||||
{
|
||||
PostFeedFragment<PublicFeedStatusDatabaseEntity>()
|
||||
.apply {
|
||||
arguments = Bundle().apply { putBoolean("home", false) }
|
||||
}
|
||||
}
|
||||
)
|
||||
setupTabs(tabs)
|
||||
}
|
||||
@ -177,7 +182,7 @@ class MainActivity : BaseActivity() {
|
||||
} else {
|
||||
val newActive = remainingUsers.first()
|
||||
db.userDao().activateUser(newActive.user_id)
|
||||
apiHolder.setDomainToCurrentUser(db)
|
||||
apiHolder.setToCurrentUser()
|
||||
//relaunch the app
|
||||
launchActivity(MainActivity(), firstTime = true)
|
||||
}
|
||||
@ -185,16 +190,17 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
private fun getUpdatedAccount() {
|
||||
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 {
|
||||
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)
|
||||
fillDrawerAccountInfo(account.id!!)
|
||||
} catch (exception: IOException) {
|
||||
@ -220,7 +226,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
db.userDao().deActivateActiveUsers()
|
||||
db.userDao().activateUser(profile.identifier.toString())
|
||||
apiHolder.setDomainToCurrentUser(db)
|
||||
apiHolder.setToCurrentUser()
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
@ -268,8 +274,22 @@ class MainActivity : BaseActivity() {
|
||||
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>){
|
||||
binding.viewPager.reduceDragSensitivity()
|
||||
binding.viewPager.adapter = object : FragmentStateAdapter(this) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return tab_array[position]()
|
||||
@ -279,6 +299,20 @@ class MainActivity : BaseActivity() {
|
||||
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 ->
|
||||
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.AlertDialog
|
||||
@ -16,29 +16,30 @@ import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.h.pixeldroid.MainActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
|
||||
import com.h.pixeldroid.postCreation.camera.CameraActivity
|
||||
import com.h.pixeldroid.postCreation.carousel.CarouselItem
|
||||
import com.h.pixeldroid.postCreation.carousel.ImageCarousel
|
||||
import com.h.pixeldroid.postCreation.photoEdit.PhotoEditActivity
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
||||
import com.h.pixeldroid.utils.db.entities.InstanceDatabaseEntity
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
import org.pixeldroid.app.MainActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
||||
import org.pixeldroid.app.postCreation.carousel.ImageCarousel
|
||||
import org.pixeldroid.app.postCreation.photoEdit.PhotoEditActivity
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.HttpException
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
@ -58,9 +59,6 @@ data class PhotoData(
|
||||
|
||||
class PostCreationActivity : BaseActivity() {
|
||||
|
||||
private lateinit var accessToken: String
|
||||
private lateinit var pixelfedAPI: PixelfedAPI
|
||||
|
||||
private var user: UserDatabaseEntity? = null
|
||||
private lateinit var instance: InstanceDatabaseEntity
|
||||
|
||||
@ -86,9 +84,6 @@ class PostCreationActivity : BaseActivity() {
|
||||
// get image URIs
|
||||
intent.clipData?.let { addPossibleImages(it) }
|
||||
|
||||
accessToken = user?.accessToken.orEmpty()
|
||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
|
||||
val carousel: ImageCarousel = binding.carousel
|
||||
carousel.addData(photoData.map { CarouselItem(it.imageUri) })
|
||||
carousel.layoutCarouselCallback = {
|
||||
@ -105,7 +100,7 @@ class PostCreationActivity : BaseActivity() {
|
||||
addPhoto()
|
||||
}
|
||||
carousel.updateDescriptionCallback = { position: Int, description: String ->
|
||||
photoData[position].imageDescription = description
|
||||
photoData.getOrNull(position)?.imageDescription = description
|
||||
}
|
||||
|
||||
// get the description and send the post
|
||||
@ -319,7 +314,16 @@ class PostCreationActivity : BaseActivity() {
|
||||
|
||||
for (data: PhotoData in photoData) {
|
||||
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 requestBody = MultipartBody.Builder()
|
||||
@ -339,8 +343,8 @@ class PostCreationActivity : BaseActivity() {
|
||||
|
||||
val description = data.imageDescription?.let { MultipartBody.Part.createFormData("description", it) }
|
||||
|
||||
|
||||
val inter = pixelfedAPI.mediaUpload("Bearer $accessToken", description, requestBody.parts[0])
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val inter = api.mediaUpload(description, requestBody.parts[0])
|
||||
|
||||
postSub = inter
|
||||
.subscribeOn(Schedulers.io())
|
||||
@ -382,8 +386,9 @@ class PostCreationActivity : BaseActivity() {
|
||||
enableButton(false)
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
pixelfedAPI.postStatus(
|
||||
authorization = "Bearer $accessToken",
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
api.postStatus(
|
||||
statusText = description,
|
||||
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? ->
|
||||
if (result?.resultCode == Activity.RESULT_OK && result.data != null) {
|
||||
photoData[position].apply {
|
||||
imageUri = result.data!!.getStringExtra("result")!!.toUri()
|
||||
val position: Int = result.data!!.getIntExtra(PhotoEditActivity.PICTURE_POSITION, 0)
|
||||
photoData.getOrNull(position)?.apply {
|
||||
imageUri = result.data!!.getStringExtra(PhotoEditActivity.PICTURE_URI)!!.toUri()
|
||||
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) })
|
||||
|
||||
photoData[position].progress = null
|
||||
photoData[position].uploadId = null
|
||||
} else if(result?.resultCode != Activity.RESULT_CANCELED){
|
||||
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@ -437,8 +442,8 @@ class PostCreationActivity : BaseActivity() {
|
||||
|
||||
private fun edit(position: Int) {
|
||||
val intent = Intent(this, PhotoEditActivity::class.java)
|
||||
.putExtra("picture_uri", photoData[position].imageUri)
|
||||
.putExtra("no upload", false)
|
||||
editResultContract(position).launch(intent)
|
||||
.putExtra(PhotoEditActivity.PICTURE_URI, photoData[position].imageUri)
|
||||
.putExtra(PhotoEditActivity.PICTURE_POSITION, position)
|
||||
editResultContract.launch(intent)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.postCreation
|
||||
package org.pixeldroid.app.postCreation
|
||||
|
||||
import io.reactivex.Observable
|
||||
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.util.AttributeSet
|
@ -1,8 +1,8 @@
|
||||
package com.h.pixeldroid.postCreation.camera
|
||||
package org.pixeldroid.app.postCreation.camera
|
||||
|
||||
import android.os.Bundle
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.R
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.R
|
||||
|
||||
class CameraActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -20,9 +20,4 @@ class CameraActivity : BaseActivity() {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.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.app.Activity
|
||||
@ -31,8 +31,8 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
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.view.LayoutInflater
|
||||
@ -9,7 +9,7 @@ import androidx.annotation.IdRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.h.pixeldroid.R
|
||||
import org.pixeldroid.app.R
|
||||
|
||||
|
||||
class CarouselAdapter(
|
||||
@ -96,8 +96,10 @@ class CarouselAdapter(
|
||||
}
|
||||
|
||||
fun updateDescription(position: Int, description: String) {
|
||||
dataList[position] = dataList[position].copy(caption = description)
|
||||
notifyItemChanged(position)
|
||||
dataList.getOrNull(position)?.apply {
|
||||
dataList[position] = copy(caption = description)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.postCreation.carousel
|
||||
package org.pixeldroid.app.postCreation.carousel
|
||||
|
||||
import android.content.Context
|
||||
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.graphics.Color
|
||||
@ -15,8 +15,8 @@ import androidx.annotation.LayoutRes
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.*
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ImageCarouselBinding
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ImageCarouselBinding
|
||||
import me.relex.circleindicator.CircleIndicator2
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
@ -44,7 +44,6 @@ class ImageCarousel(
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var tvCaption: TextView
|
||||
private lateinit var editTextMediaDescription: EditText
|
||||
private var snapHelper: SnapHelper = PagerSnapHelper()
|
||||
|
||||
var indicator: CircleIndicator2? = null
|
||||
@ -292,9 +291,9 @@ class ImageCarousel(
|
||||
|
||||
if(layoutCarousel){
|
||||
field = value
|
||||
if(value) editTextMediaDescription.setText(currentDescription)
|
||||
if(value) binding.editTextMediaDescription.setText(currentDescription)
|
||||
else {
|
||||
val description = editTextMediaDescription.text.toString()
|
||||
val description = binding.editTextMediaDescription.text.toString()
|
||||
currentDescription = description
|
||||
adapter?.updateDescription(currentPosition, description)
|
||||
updateDescriptionCallback?.invoke(currentPosition, description)
|
||||
@ -339,7 +338,6 @@ class ImageCarousel(
|
||||
|
||||
recyclerView = binding.recyclerView
|
||||
tvCaption = binding.tvCaption
|
||||
editTextMediaDescription = binding.editTextMediaDescription
|
||||
|
||||
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.util.DisplayMetrics
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.postCreation.photoEdit
|
||||
package org.pixeldroid.app.postCreation.photoEdit
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -6,7 +6,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.SeekBar
|
||||
import com.h.pixeldroid.R
|
||||
import org.pixeldroid.app.R
|
||||
|
||||
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.FragmentManager
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.postCreation.photoEdit
|
||||
package org.pixeldroid.app.postCreation.photoEdit
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import org.pixeldroid.app.R
|
||||
import com.zomato.photofilters.FilterPack
|
||||
import com.zomato.photofilters.imageprocessors.Filter
|
||||
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.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.AlertDialog
|
||||
@ -18,14 +18,11 @@ import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityPhotoEditBinding
|
||||
import com.h.pixeldroid.databinding.ActivityPostCreationBinding
|
||||
import com.h.pixeldroid.postCreation.PostCreationActivity
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPhotoEditBinding
|
||||
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import com.yalantis.ucrop.UCrop
|
||||
import com.zomato.photofilters.imageprocessors.Filter
|
||||
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
|
||||
@ -65,6 +62,8 @@ class PhotoEditActivity : BaseActivity() {
|
||||
private lateinit var filterListFragment: FilterListFragment
|
||||
private lateinit var editImageFragment: EditImageFragment
|
||||
|
||||
private var picturePosition: Int? = null
|
||||
|
||||
private var brightnessFinal = BRIGHTNESS_START
|
||||
private var saturationFinal = SATURATION_START
|
||||
private var contrastFinal = CONTRAST_START
|
||||
@ -74,6 +73,9 @@ class PhotoEditActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
companion object{
|
||||
internal const val PICTURE_URI = "picture_uri"
|
||||
internal const val PICTURE_POSITION = "picture_position"
|
||||
|
||||
private var executor: ExecutorService = newSingleThreadExecutor()
|
||||
private var future: Future<*>? = null
|
||||
|
||||
@ -97,7 +99,8 @@ class PhotoEditActivity : BaseActivity() {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setHomeButtonEnabled(true)
|
||||
|
||||
initialUri = intent.getParcelableExtra("picture_uri")
|
||||
initialUri = intent.getParcelableExtra(PICTURE_URI)
|
||||
picturePosition = intent.getIntExtra(PICTURE_POSITION, 0)
|
||||
imageUri = initialUri
|
||||
|
||||
// Crop button on-click listener
|
||||
@ -342,7 +345,8 @@ class PhotoEditActivity : BaseActivity() {
|
||||
private fun sendBackImage(file: String) {
|
||||
val intent = Intent(this, PostCreationActivity::class.java)
|
||||
.apply {
|
||||
putExtra("result", file)
|
||||
putExtra(PICTURE_URI, file)
|
||||
putExtra(PICTURE_POSITION, picturePosition)
|
||||
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.view.View
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.postCreation.photoEdit
|
||||
package org.pixeldroid.app.postCreation.photoEdit
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
@ -7,8 +7,8 @@ import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ThumbnailListItemBinding
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ThumbnailListItemBinding
|
||||
import com.zomato.photofilters.utils.ThumbnailItem
|
||||
|
||||
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.style.ClickableSpan
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.posts
|
||||
package org.pixeldroid.app.posts
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
@ -12,13 +12,13 @@ import android.view.View
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.toSpanned
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.openAccountFromId
|
||||
import com.h.pixeldroid.utils.api.objects.Mention
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
||||
import org.pixeldroid.app.utils.api.objects.Mention
|
||||
import org.pixeldroid.app.utils.api.objects.Tag.Companion.openTag
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.text.ParseException
|
||||
@ -50,12 +50,11 @@ fun getDomain(urlString: String?): String {
|
||||
}
|
||||
|
||||
fun parseHTMLText(
|
||||
text : String,
|
||||
mentions: List<Mention>?,
|
||||
api : PixelfedAPI,
|
||||
context: Context,
|
||||
credential: String,
|
||||
lifecycleScope: LifecycleCoroutineScope
|
||||
text: String,
|
||||
mentions: List<Mention>?,
|
||||
apiHolder: PixelfedAPIHolder,
|
||||
context: Context,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) : Spanned {
|
||||
//Convert text to spannable
|
||||
val content = fromHtml(text)
|
||||
@ -76,7 +75,7 @@ fun parseHTMLText(
|
||||
val tag = text.subSequence(1, text.length).toString()
|
||||
customSpan = object : ClickableSpanNoUnderline() {
|
||||
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")
|
||||
//Retrieve the account for the given profile
|
||||
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.
|
||||
*/
|
||||
|
||||
package com.h.pixeldroid.posts
|
||||
package org.pixeldroid.app.posts
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
@ -51,7 +51,7 @@ class NestedScrollableHost : ConstraintLayout {
|
||||
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
|
||||
|
||||
@ -79,7 +79,7 @@ class NestedScrollableHost : ConstraintLayout {
|
||||
if (e.action == MotionEvent.ACTION_DOWN) {
|
||||
initialX = e.x
|
||||
initialY = e.y
|
||||
doubleTapCallback?.invoke(Unit)
|
||||
doubleTapCallback?.invoke(true)
|
||||
}
|
||||
// Early return if child can't scroll in same direction as parent
|
||||
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
|
||||
@ -94,10 +94,13 @@ class NestedScrollableHost : ConstraintLayout {
|
||||
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
|
||||
|
||||
// assuming ViewPager2 touch-slop is 2x touch-slop of child
|
||||
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
|
||||
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
|
||||
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f/ touchSlopModifier else 1f
|
||||
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 (isVpHorizontal == (scaledDy > scaledDx)) {
|
||||
// Gesture is perpendicular, allow all parents to intercept
|
||||
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.os.Bundle
|
||||
@ -9,23 +9,22 @@ import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityPostBinding
|
||||
import com.h.pixeldroid.databinding.CommentBinding
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Mention
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||
import com.h.pixeldroid.utils.displayDimensionsInPx
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityPostBinding
|
||||
import org.pixeldroid.app.databinding.CommentBinding
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Mention
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class PostActivity : BaseActivity() {
|
||||
lateinit var domain : String
|
||||
private lateinit var accessToken : String
|
||||
|
||||
private lateinit var binding: ActivityPostBinding
|
||||
|
||||
@ -45,17 +44,15 @@ class PostActivity : BaseActivity() {
|
||||
val user = db.userDao().getActiveUser()
|
||||
|
||||
domain = user?.instance_uri.orEmpty()
|
||||
accessToken = user?.accessToken.orEmpty()
|
||||
|
||||
|
||||
supportActionBar?.title = getString(R.string.post_title).format(status.account?.getDisplayName())
|
||||
|
||||
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(credential)
|
||||
activateCommenter()
|
||||
|
||||
if(viewComments || postComment){
|
||||
//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
|
||||
if(!postComment) retrieveComments(apiHolder.api!!, credential)
|
||||
if(!postComment) retrieveComments(apiHolder.api!!)
|
||||
}
|
||||
binding.postFragmentSingle.viewComments.setOnClickListener {
|
||||
retrieveComments(apiHolder.api!!, credential)
|
||||
retrieveComments(apiHolder.api!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun activateCommenter(credential: String) {
|
||||
private fun activateCommenter() {
|
||||
//Activate commenter
|
||||
binding.submitComment.setOnClickListener {
|
||||
val textIn = binding.editComment.text
|
||||
@ -94,15 +86,14 @@ class PostActivity : BaseActivity() {
|
||||
} else {
|
||||
//Post the comment
|
||||
lifecycleScope.launchWhenCreated {
|
||||
apiHolder.api?.let { it1 -> postComment(it1, credential) }
|
||||
apiHolder.api?.let { it1 -> postComment(it1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addComment(context: Context, commentContainer: LinearLayout,
|
||||
commentUsername: String, commentContent: String, mentions: List<Mention>,
|
||||
credential: String) {
|
||||
commentUsername: String, commentContent: String, mentions: List<Mention>) {
|
||||
|
||||
|
||||
val itemBinding = CommentBinding.inflate(
|
||||
@ -111,27 +102,30 @@ class PostActivity : BaseActivity() {
|
||||
|
||||
itemBinding.user.text = commentUsername
|
||||
itemBinding.commentText.text = parseHTMLText(
|
||||
commentContent,
|
||||
mentions,
|
||||
apiHolder.api!!,
|
||||
context,
|
||||
credential,
|
||||
lifecycleScope
|
||||
commentContent,
|
||||
mentions,
|
||||
apiHolder,
|
||||
context,
|
||||
lifecycleScope
|
||||
)
|
||||
}
|
||||
|
||||
private fun retrieveComments(api: PixelfedAPI, credential: String) {
|
||||
private fun retrieveComments(api: PixelfedAPI) {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
status.id.let {
|
||||
try {
|
||||
val statuses = api.statusComments(it, credential).descendants
|
||||
val statuses = api.statusComments(it).descendants
|
||||
|
||||
binding.commentContainer.removeAllViews()
|
||||
|
||||
//Create the new views for each comment
|
||||
for (status in statuses) {
|
||||
addComment(binding.root.context, binding.commentContainer, status.account!!.username!!,
|
||||
status.content!!, status.mentions.orEmpty(), credential
|
||||
addComment(
|
||||
binding.root.context,
|
||||
binding.commentContainer,
|
||||
status.account!!.username!!,
|
||||
status.content!!,
|
||||
status.mentions.orEmpty()
|
||||
)
|
||||
}
|
||||
binding.commentContainer.visibility = View.VISIBLE
|
||||
@ -149,19 +143,18 @@ class PostActivity : BaseActivity() {
|
||||
|
||||
private suspend fun postComment(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
) {
|
||||
val textIn = binding.editComment.text
|
||||
val nonNullText = textIn.toString()
|
||||
status.id.let {
|
||||
try {
|
||||
val response = api.postStatus(credential, nonNullText, it)
|
||||
val response = api.postStatus(nonNullText, it)
|
||||
binding.commentIn.visibility = View.GONE
|
||||
|
||||
//Add the comment to the comment section
|
||||
addComment(
|
||||
binding.root.context, binding.commentContainer, response.account!!.username!!,
|
||||
response.content!!, response.mentions.orEmpty(), credential
|
||||
response.content!!, response.mentions.orEmpty()
|
||||
)
|
||||
|
||||
Toast.makeText(
|
@ -1,12 +1,12 @@
|
||||
package com.h.pixeldroid.posts
|
||||
package org.pixeldroid.app.posts
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityReportBinding
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityReportBinding
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
@ -24,10 +24,6 @@ class ReportActivity : BaseActivity() {
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@ -37,12 +33,15 @@ class ReportActivity : BaseActivity() {
|
||||
|
||||
binding.textInputLayout.editText?.isEnabled = false
|
||||
|
||||
val accessToken = user?.accessToken.orEmpty()
|
||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
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)
|
||||
} catch (exception: IOException) {
|
||||
@ -67,9 +66,4 @@ class ReportActivity : BaseActivity() {
|
||||
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.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.AnimatedVectorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.Log
|
||||
@ -14,21 +15,23 @@ import android.widget.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.AlbumImageViewBinding
|
||||
import com.h.pixeldroid.databinding.PostFragmentBinding
|
||||
import com.h.pixeldroid.utils.BlurHashDecoder
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.AlbumImageViewBinding
|
||||
import org.pixeldroid.app.databinding.PostFragmentBinding
|
||||
import org.pixeldroid.app.utils.BlurHashDecoder
|
||||
import org.pixeldroid.app.utils.ImageConverter
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_COMMENT_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.POST_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import com.karumi.dexter.Dexter
|
||||
import com.karumi.dexter.listener.PermissionDeniedResponse
|
||||
import com.karumi.dexter.listener.PermissionGrantedResponse
|
||||
@ -46,7 +49,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
|
||||
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.status = status
|
||||
@ -177,21 +180,19 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
|
||||
private fun setDescription(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
lifecycleScope: LifecycleCoroutineScope
|
||||
apiHolder: PixelfedAPIHolder,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) {
|
||||
binding.description.apply {
|
||||
if (status?.content.isNullOrBlank()) {
|
||||
visibility = View.GONE
|
||||
} else {
|
||||
text = parseHTMLText(
|
||||
status?.content.orEmpty(),
|
||||
status?.mentions,
|
||||
api,
|
||||
binding.root.context,
|
||||
credential,
|
||||
lifecycleScope
|
||||
status?.content.orEmpty(),
|
||||
status?.mentions,
|
||||
apiHolder,
|
||||
binding.root.context,
|
||||
lifecycleScope
|
||||
)
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
@ -199,25 +200,20 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
//region buttons
|
||||
private fun activateButtons(
|
||||
api: PixelfedAPI,
|
||||
apiHolder: PixelfedAPIHolder,
|
||||
db: AppDatabase,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
isActivity: Boolean
|
||||
){
|
||||
val user = db.userDao().getActiveUser()!!
|
||||
|
||||
val credential = "Bearer ${user.accessToken}"
|
||||
//Set the special HTML text
|
||||
setDescription(api, credential, lifecycleScope)
|
||||
setDescription(apiHolder, lifecycleScope)
|
||||
|
||||
//Activate onclickListeners
|
||||
activateLiker(
|
||||
api, credential, status?.favourited ?: false,
|
||||
lifecycleScope
|
||||
apiHolder, status?.favourited ?: false, lifecycleScope
|
||||
)
|
||||
activateReblogger(
|
||||
api, credential, status?.reblogged ?: false,
|
||||
lifecycleScope
|
||||
apiHolder, status?.reblogged ?: false, lifecycleScope
|
||||
)
|
||||
|
||||
if(isActivity){
|
||||
@ -237,14 +233,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
|
||||
showComments(lifecycleScope, isActivity)
|
||||
|
||||
activateMoreButton(api, db, lifecycleScope)
|
||||
activateMoreButton(apiHolder, db, lifecycleScope)
|
||||
}
|
||||
|
||||
private fun activateReblogger(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
isReblogged: Boolean,
|
||||
lifecycleScope: LifecycleCoroutineScope
|
||||
apiHolder: PixelfedAPIHolder,
|
||||
isReblogged: Boolean,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) {
|
||||
binding.reblogger.apply {
|
||||
//Set initial button state
|
||||
@ -253,12 +248,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
//Activate the button
|
||||
setEventListener { _, buttonState ->
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
if (buttonState) {
|
||||
// Button is active
|
||||
undoReblogPost(api, credential)
|
||||
undoReblogPost(api)
|
||||
} else {
|
||||
// Button is inactive
|
||||
reblogPost(api, credential)
|
||||
reblogPost(api)
|
||||
}
|
||||
}
|
||||
//show animation or not?
|
||||
@ -267,15 +263,12 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun reblogPost(
|
||||
api: PixelfedAPI,
|
||||
credential: String
|
||||
) {
|
||||
private suspend fun reblogPost(api: PixelfedAPI) {
|
||||
//Call the api function
|
||||
status?.id?.let {
|
||||
|
||||
try {
|
||||
val resp = api.reblogStatus(credential, it)
|
||||
val resp = api.reblogStatus(it)
|
||||
|
||||
//Update shown share count
|
||||
binding.nshares.text = resp.getNShares(binding.root.context)
|
||||
@ -290,14 +283,11 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun undoReblogPost(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
) {
|
||||
private suspend fun undoReblogPost(api: PixelfedAPI) {
|
||||
//Call the api function
|
||||
status?.id?.let {
|
||||
try {
|
||||
val resp = api.undoReblogStatus(credential, it)
|
||||
val resp = api.undoReblogStatus(it)
|
||||
|
||||
//Update shown share count
|
||||
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 {
|
||||
PopupMenu(it.context, it).apply {
|
||||
setOnMenuItemClickListener { item ->
|
||||
@ -395,7 +385,8 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
db.homePostDao().delete(id, user.user_id, user.instance_uri)
|
||||
db.publicPostDao().delete(id, user.user_id, user.instance_uri)
|
||||
try {
|
||||
api.deleteStatus("Bearer ${user.accessToken}", id)
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
api.deleteStatus(id)
|
||||
binding.root.visibility = View.GONE
|
||||
} catch (exception: HttpException) {
|
||||
Toast.makeText(
|
||||
@ -439,10 +430,9 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
|
||||
private fun activateLiker(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
isLiked: Boolean,
|
||||
lifecycleScope: LifecycleCoroutineScope
|
||||
apiHolder: PixelfedAPIHolder,
|
||||
isLiked: Boolean,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) {
|
||||
|
||||
binding.liker.apply {
|
||||
@ -452,12 +442,13 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
//Activate the liker
|
||||
setEventListener { _, buttonState ->
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
if (buttonState) {
|
||||
// Button is active, unlike
|
||||
unLikePostCall(api, credential)
|
||||
unLikePostCall(api)
|
||||
} else {
|
||||
// Button is inactive, like
|
||||
likePostCall(api, credential)
|
||||
likePostCall(api)
|
||||
}
|
||||
}
|
||||
//show animation or not?
|
||||
@ -468,20 +459,23 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
//Activate double tap liking
|
||||
var clicked = false
|
||||
binding.postPagerHost.doubleTapCallback = {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
if(!it) clicked = false
|
||||
else lifecycleScope.launchWhenCreated {
|
||||
//Check that the post isn't hidden
|
||||
if(binding.sensitiveWarning.visibility == View.GONE) {
|
||||
//Check for double click
|
||||
if(clicked) {
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
if (binding.liker.isChecked) {
|
||||
// Button is active, unlike
|
||||
binding.liker.isChecked = false
|
||||
unLikePostCall(api, credential)
|
||||
unLikePostCall(api)
|
||||
} else {
|
||||
// Button is inactive, like
|
||||
binding.liker.playAnimation()
|
||||
binding.liker.isChecked = true
|
||||
likePostCall(api, credential)
|
||||
binding.likeAnimation.animateView()
|
||||
likePostCall(api)
|
||||
}
|
||||
} else {
|
||||
clicked = true
|
||||
@ -490,20 +484,27 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
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(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
) {
|
||||
private suspend fun likePostCall(api: PixelfedAPI) {
|
||||
//Call the api function
|
||||
status?.id?.let {
|
||||
|
||||
try {
|
||||
val resp = api.likePost(credential, it)
|
||||
val resp = api.likePost(it)
|
||||
|
||||
//Update shown like count and internal like toggle
|
||||
binding.nlikes.text = resp.getNLikes(binding.root.context)
|
||||
@ -518,15 +519,12 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unLikePostCall(
|
||||
api: PixelfedAPI,
|
||||
credential: String,
|
||||
) {
|
||||
private suspend fun unLikePostCall(api: PixelfedAPI) {
|
||||
//Call the api function
|
||||
status?.id?.let {
|
||||
|
||||
try {
|
||||
val resp = api.unlikePost(credential, it)
|
||||
val resp = api.unlikePost(it)
|
||||
|
||||
//Update shown like count and internal like toggle
|
||||
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.ViewGroup
|
||||
@ -12,14 +12,16 @@ import androidx.paging.LoadStateAdapter
|
||||
import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ErrorLayoutBinding
|
||||
import com.h.pixeldroid.databinding.LoadStateFooterViewItemBinding
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||
import com.google.gson.Gson
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ErrorLayoutBinding
|
||||
import org.pixeldroid.app.databinding.LoadStateFooterViewItemBinding
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
|
||||
/**
|
||||
* Shows or hides the error in the different FeedFragments
|
||||
@ -56,24 +58,17 @@ internal fun <T: Any> initAdapter(
|
||||
if(!progressBar.isVisible && swipeRefreshLayout.isRefreshing) {
|
||||
// Stop loading spinner when loading is done
|
||||
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
|
||||
?: loadState.source.prepend as? LoadState.Error
|
||||
?: loadState.source.refresh as? LoadState.Error
|
||||
@ -81,21 +76,37 @@ internal fun <T: Any> initAdapter(
|
||||
?: loadState.prepend as? LoadState.Error
|
||||
?: loadState.refresh as? LoadState.Error
|
||||
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) {
|
||||
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(
|
||||
job: Job?, lifecycleScope: LifecycleCoroutineScope, viewModel: FeedViewModel<FeedContent>,
|
||||
pagingDataAdapter: PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>): Job {
|
||||
fun <T: FeedContent> launch(
|
||||
job: Job?, lifecycleScope: LifecycleCoroutineScope, viewModel: FeedViewModel<T>,
|
||||
pagingDataAdapter: PagingDataAdapter<T, RecyclerView.ViewHolder>): Job {
|
||||
// Make sure we cancel the previous job before creating a new one
|
||||
job?.cancel()
|
||||
return lifecycleScope.launch {
|
||||
viewModel.flow().collectLatest {
|
||||
viewModel.flow.collectLatest {
|
||||
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.view.LayoutInflater
|
||||
@ -8,21 +8,18 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.*
|
||||
import androidx.paging.LoadState.*
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
import com.h.pixeldroid.databinding.FragmentFeedBinding
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
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
|
||||
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
||||
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||
import org.pixeldroid.app.utils.BaseFragment
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||
import org.pixeldroid.app.utils.limitedLengthSmoothScrollToPosition
|
||||
|
||||
/**
|
||||
* 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
|
||||
job?.cancel()
|
||||
job = lifecycleScope.launchWhenStarted {
|
||||
viewModel.flow().collectLatest {
|
||||
viewModel.flow.collectLatest {
|
||||
adapter.submitData(it)
|
||||
}
|
||||
}
|
||||
@ -51,12 +48,14 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
||||
|
||||
internal fun initSearch() {
|
||||
// Scroll to top when the list is refreshed from network.
|
||||
lifecycleScope.launch {
|
||||
lifecycleScope.launchWhenStarted {
|
||||
adapter.loadStateFlow
|
||||
// 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.
|
||||
.filter { it.refresh is LoadState.NotLoading }
|
||||
.filter { it.refresh is NotLoading}
|
||||
.collect { binding.list.scrollToPosition(0) }
|
||||
}
|
||||
}
|
||||
@ -73,17 +72,16 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
||||
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
||||
binding.list, binding.motionLayout, binding.errorLayout, adapter)
|
||||
|
||||
//binding.progressBar.visibility = View.GONE
|
||||
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()
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
fun onTabReClicked() {
|
||||
binding.list.limitedLengthSmoothScrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.h.pixeldroid.posts.feeds.cachedFeeds
|
||||
package org.pixeldroid.app.posts.feeds.cachedFeeds
|
||||
|
||||
import androidx.paging.*
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.db.dao.feedContent.FeedContentDao
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
@ -14,31 +14,20 @@
|
||||
* 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.viewModelScope
|
||||
import androidx.paging.*
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* ViewModel for the cached feeds.
|
||||
* The ViewModel works with the [FeedContentRepository] to get the data.
|
||||
*/
|
||||
class FeedViewModel<T: FeedContentDatabase>(private val repository: FeedContentRepository<T>) : ViewModel() {
|
||||
|
||||
private var currentResult: Flow<PagingData<T>>? = null
|
||||
class FeedViewModel<T: FeedContentDatabase>(repository: FeedContentRepository<T>) : ViewModel() {
|
||||
|
||||
@ExperimentalPagingApi
|
||||
fun flow(): Flow<PagingData<T>> {
|
||||
val lastResult = currentResult
|
||||
if (lastResult != null) {
|
||||
return lastResult
|
||||
}
|
||||
val newResult: Flow<PagingData<T>> = repository.stream()
|
||||
.cachedIn(viewModelScope)
|
||||
currentResult = newResult
|
||||
return newResult
|
||||
}
|
||||
val flow: Flow<PagingData<T>> = repository.stream().cachedIn(viewModelScope)
|
||||
}
|
@ -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.Intent
|
||||
@ -18,21 +18,19 @@ import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.FragmentNotificationsBinding
|
||||
import com.h.pixeldroid.posts.PostActivity
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory
|
||||
import com.h.pixeldroid.posts.parseHTMLText
|
||||
import com.h.pixeldroid.posts.setTextViewFromISO8601
|
||||
import com.h.pixeldroid.profile.ProfileActivity
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.Notification
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.FragmentNotificationsBinding
|
||||
import org.pixeldroid.app.posts.PostActivity
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
||||
import org.pixeldroid.app.posts.parseHTMLText
|
||||
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
||||
import org.pixeldroid.app.profile.ProfileActivity
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.Notification
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
|
||||
|
||||
/**
|
||||
@ -42,7 +40,7 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
adapter = NotificationsAdapter(apiHolder, db)
|
||||
adapter = NotificationsAdapter(apiHolder)
|
||||
}
|
||||
|
||||
@ExperimentalPagingApi
|
||||
@ -55,10 +53,10 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
// get the view model
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
requireActivity(),
|
||||
ViewModelFactory(db, db.notificationDao(), NotificationsRemoteMediator(apiHolder, db))
|
||||
)
|
||||
.get(FeedViewModel::class.java) as FeedViewModel<Notification>
|
||||
.get("notifications", FeedViewModel::class.java) as FeedViewModel<Notification>
|
||||
|
||||
launch()
|
||||
initSearch()
|
||||
@ -174,9 +172,8 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
|
||||
fun bind(
|
||||
notification: Notification?,
|
||||
api: PixelfedAPI,
|
||||
accessToken: String,
|
||||
lifecycleScope: LifecycleCoroutineScope
|
||||
api: PixelfedAPIHolder,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) {
|
||||
|
||||
this.notification = notification
|
||||
@ -213,12 +210,11 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
//Convert HTML to clickable text
|
||||
postDescription.text =
|
||||
parseHTMLText(
|
||||
notification?.status?.content ?: "",
|
||||
notification?.status?.mentions,
|
||||
api,
|
||||
itemView.context,
|
||||
"Bearer $accessToken",
|
||||
lifecycleScope
|
||||
notification?.status?.content ?: "",
|
||||
notification?.status?.mentions,
|
||||
api,
|
||||
itemView.context,
|
||||
lifecycleScope
|
||||
)
|
||||
|
||||
avatar.setOnClickListener {
|
||||
@ -240,7 +236,6 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
|
||||
inner class NotificationsAdapter(
|
||||
private val apiHolder: PixelfedAPIHolder,
|
||||
private val db: AppDatabase
|
||||
) : PagingDataAdapter<Notification, RecyclerView.ViewHolder>(
|
||||
object : DiffUtil.ItemCallback<Notification>() {
|
||||
override fun areItemsTheSame(
|
||||
@ -270,10 +265,9 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
||||
val uiModel = getItem(position)
|
||||
uiModel.let {
|
||||
(holder as NotificationViewHolder).bind(
|
||||
it,
|
||||
apiHolder.setDomainToCurrentUser(db),
|
||||
db.userDao().getActiveUser()!!.accessToken,
|
||||
lifecycleScope
|
||||
it,
|
||||
apiHolder,
|
||||
lifecycleScope
|
||||
)
|
||||
}
|
||||
}
|
@ -14,13 +14,13 @@
|
||||
* 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.room.withTransaction
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import com.h.pixeldroid.utils.api.objects.Notification
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.api.objects.Notification
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.lang.NullPointerException
|
||||
@ -58,13 +58,12 @@ class NotificationsRemoteMediator @Inject constructor(
|
||||
try {
|
||||
val user = db.userDao().getActiveUser()
|
||||
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
val accessToken = user.accessToken
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
val apiResponse = api.notifications("Bearer $accessToken",
|
||||
max_id = max_id,
|
||||
min_id = min_id,
|
||||
limit = state.config.pageSize.toString(),
|
||||
val apiResponse = api.notifications(
|
||||
max_id = max_id,
|
||||
min_id = min_id,
|
||||
limit = state.config.pageSize.toString(),
|
||||
)
|
||||
|
||||
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.room.withTransaction
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import com.h.pixeldroid.utils.db.entities.HomeStatusDatabaseEntity
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.lang.NullPointerException
|
||||
@ -43,12 +43,12 @@ class HomeFeedRemoteMediator @Inject constructor(
|
||||
try {
|
||||
val user = db.userDao().getActiveUser()
|
||||
?: return MediatorResult.Error(NullPointerException("No active user exists"))
|
||||
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
val accessToken = user.accessToken
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
val apiResponse = api.timelineHome( "Bearer $accessToken",
|
||||
max_id= max_id, min_id = min_id,
|
||||
limit = state.config.pageSize.toString())
|
||||
val apiResponse = api.timelineHome(
|
||||
max_id= max_id,
|
||||
min_id = min_id, limit = state.config.pageSize.toString()
|
||||
)
|
||||
|
||||
val dbObjects = apiResponse.map{
|
||||
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.view.LayoutInflater
|
||||
@ -11,15 +11,16 @@ import androidx.paging.PagingDataAdapter
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.utils.db.dao.feedContent.FeedContentDao
|
||||
import com.h.pixeldroid.posts.StatusViewHolder
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||
import com.h.pixeldroid.posts.feeds.cachedFeeds.ViewModelFactory
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContentDatabase
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.displayDimensionsInPx
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||
import org.pixeldroid.app.posts.StatusViewHolder
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
|
||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
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 dao: FeedContentDao<T>
|
||||
private var home by Delegates.notNull<Boolean>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
||||
|
||||
home = requireArguments().get("home") as Boolean
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (requireArguments().get("home") as Boolean){
|
||||
if (home){
|
||||
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
||||
dao = db.homePostDao() as FeedContentDao<T>
|
||||
}
|
||||
@ -59,8 +63,8 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
||||
|
||||
// get the view model
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(this, ViewModelFactory(db, dao, mediator))
|
||||
.get(FeedViewModel::class.java) as FeedViewModel<T>
|
||||
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(db, dao, mediator))
|
||||
.get(if(home) "home" else "public", FeedViewModel::class.java) as FeedViewModel<T>
|
||||
|
||||
launch()
|
||||
initSearch()
|
||||
@ -70,12 +74,8 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
||||
|
||||
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<T, RecyclerView.ViewHolder>(
|
||||
object : DiffUtil.ItemCallback<T>() {
|
||||
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean =
|
||||
oldItem.id == newItem.id
|
||||
override fun areItemsTheSame (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) {
|
||||
val uiModel = getItem(position) as Status
|
||||
uiModel.let {
|
||||
(holder as StatusViewHolder).bind(it, apiHolder.setDomainToCurrentUser(db), db, lifecycleScope, displayDimensionsInPx)
|
||||
val uiModel = getItem(position) as Status?
|
||||
uiModel?.let {
|
||||
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
|
||||
}
|
||||
}
|
||||
}
|
@ -14,13 +14,13 @@
|
||||
* 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.room.withTransaction
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.db.entities.PublicFeedStatusDatabaseEntity
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.lang.NullPointerException
|
||||
@ -58,7 +58,7 @@ class PublicFeedRemoteMediator @Inject constructor(
|
||||
try {
|
||||
val user = db.userDao().getActiveUser()
|
||||
?: 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(
|
||||
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.view.LayoutInflater
|
||||
@ -9,17 +9,17 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.*
|
||||
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.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
import com.h.pixeldroid.databinding.FragmentFeedBinding
|
||||
import com.h.pixeldroid.utils.BaseFragment
|
||||
import com.h.pixeldroid.posts.feeds.initAdapter
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
||||
import org.pixeldroid.app.utils.BaseFragment
|
||||
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||
|
||||
|
||||
/**
|
||||
@ -37,10 +37,7 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
||||
|
||||
|
||||
internal fun launch() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
job = launch(job, lifecycleScope,
|
||||
viewModel as FeedViewModel<FeedContent>,
|
||||
adapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
||||
job = launch(job, lifecycleScope, viewModel, adapter)
|
||||
}
|
||||
|
||||
internal fun initSearch() {
|
||||
@ -68,9 +65,6 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
||||
binding.motionLayout, binding.errorLayout, adapter)
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -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.os.Bundle
|
||||
@ -13,14 +13,14 @@ import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.AccountListEntryBinding
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.AccountListEntryBinding
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||
|
||||
|
||||
/**
|
||||
@ -52,16 +52,15 @@ class AccountListFragment : UncachedFeedFragment<Account>() {
|
||||
|
||||
// get the view model
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
||||
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||
FollowersContentRepository(
|
||||
apiHolder.setDomainToCurrentUser(db),
|
||||
db.userDao().getActiveUser()!!.accessToken,
|
||||
apiHolder.setToCurrentUser(),
|
||||
id,
|
||||
following
|
||||
)
|
||||
)
|
||||
)
|
||||
.get(FeedViewModel::class.java) as FeedViewModel<Account>
|
||||
.get("accountList", FeedViewModel::class.java) as FeedViewModel<Account>
|
||||
|
||||
launch()
|
||||
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.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -14,7 +14,6 @@ import javax.inject.Inject
|
||||
class FollowersContentRepository @ExperimentalPagingApi
|
||||
@Inject constructor(
|
||||
private val api: PixelfedAPI,
|
||||
private val accessToken: String,
|
||||
private val accountId: String,
|
||||
private val following: Boolean,
|
||||
): UncachedContentRepository<Account> {
|
||||
@ -25,7 +24,7 @@ class FollowersContentRepository @ExperimentalPagingApi
|
||||
pageSize = NETWORK_PAGE_SIZE,
|
||||
enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
FollowersPagingSource(api, accessToken, accountId, following)
|
||||
FollowersPagingSource(api, accountId, following)
|
||||
}
|
||||
).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.PagingState
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
|
||||
class FollowersPagingSource(
|
||||
private val api: PixelfedAPI,
|
||||
private val accessToken: String,
|
||||
private val accountId: String,
|
||||
private val following: Boolean
|
||||
) : PagingSource<String, Account>() {
|
||||
@ -22,17 +20,19 @@ class FollowersPagingSource(
|
||||
// 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
|
||||
if(following) {
|
||||
api.followers(account_id = accountId,
|
||||
authorization = "Bearer $accessToken",
|
||||
api.followers(
|
||||
account_id = accountId,
|
||||
max_id = position,
|
||||
limit = params.loadSize,
|
||||
page = position,
|
||||
max_id = position)
|
||||
page = position
|
||||
)
|
||||
} else {
|
||||
api.following(account_id = accountId,
|
||||
authorization = "Bearer $accessToken",
|
||||
api.following(
|
||||
account_id = accountId,
|
||||
max_id = position,
|
||||
limit = params.loadSize,
|
||||
page = position,
|
||||
max_id = position)
|
||||
page = position
|
||||
)
|
||||
}
|
||||
|
||||
val accounts = if(response.isSuccessful){
|
||||
@ -41,25 +41,22 @@ class FollowersPagingSource(
|
||||
throw HttpException(response)
|
||||
}
|
||||
|
||||
val nextPosition: String = if(response.headers()["Link"] != null){
|
||||
//Header is of the form:
|
||||
val nextPosition: String = response.headers()["Link"]
|
||||
// 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"
|
||||
// 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 '?'
|
||||
response.headers()["Link"]
|
||||
.orEmpty()
|
||||
.substringAfter("max_id=", "")
|
||||
.substringBefore('?', "")
|
||||
.substringBefore('>', "")
|
||||
} else {
|
||||
// No Link header, so we just increment the position value
|
||||
?.substringAfter("max_id=", "")
|
||||
?.substringBefore('?', "")
|
||||
?.substringBefore('>', "")
|
||||
|
||||
?: // No Link header, so we just increment the position value (Pixelfed case)
|
||||
(position?.toBigIntegerOrNull() ?: 1.toBigInteger()).inc().toString()
|
||||
}
|
||||
|
||||
LoadResult.Page(
|
||||
data = accounts,
|
||||
prevKey = null,
|
||||
nextKey = if (accounts.isEmpty()) null else nextPosition
|
||||
nextKey = if (accounts.isEmpty() || nextPosition.isEmpty() || nextPosition == position) null else nextPosition
|
||||
)
|
||||
} catch (exception: IOException) {
|
||||
LoadResult.Error(exception)
|
||||
@ -68,8 +65,5 @@ class FollowersPagingSource(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRefreshKey(state: PagingState<String, Account>): String? =
|
||||
state.anchorPosition?.run {
|
||||
state.closestItemToPosition(this)?.id
|
||||
}
|
||||
override fun getRefreshKey(state: PagingState<String, Account>): String? = null
|
||||
}
|
@ -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.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class ProfileContentRepository @ExperimentalPagingApi
|
||||
@Inject constructor(
|
||||
private val api: PixelfedAPI,
|
||||
private val accessToken: String,
|
||||
private val accountId: String
|
||||
) : UncachedContentRepository<Status> {
|
||||
override fun getStream(): Flow<PagingData<Status>> {
|
||||
@ -23,7 +22,7 @@ class ProfileContentRepository @ExperimentalPagingApi
|
||||
pageSize = NETWORK_PAGE_SIZE,
|
||||
enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
ProfilePagingSource(api, accessToken, accountId)
|
||||
ProfilePagingSource(api, accountId)
|
||||
}
|
||||
).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.PagingState
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ProfilePagingSource(
|
||||
private val api: PixelfedAPI,
|
||||
private val accessToken: String,
|
||||
private val accountId: String
|
||||
) : PagingSource<String, Status>() {
|
||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
||||
val position = params.key
|
||||
return try {
|
||||
val posts = api.accountPosts("Bearer $accessToken",
|
||||
account_id = accountId,
|
||||
max_id = position,
|
||||
limit = params.loadSize
|
||||
val posts = api.accountPosts(
|
||||
account_id = accountId,
|
||||
max_id = position,
|
||||
limit = params.loadSize
|
||||
)
|
||||
|
||||
val nextKey = posts.lastOrNull()?.id
|
||||
|
||||
LoadResult.Page(
|
||||
data = posts,
|
||||
prevKey = null,
|
||||
nextKey = posts.lastOrNull()?.id
|
||||
nextKey = if(nextKey == position) null else nextKey
|
||||
)
|
||||
} catch (exception: HttpException) {
|
||||
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.view.LayoutInflater
|
||||
@ -6,10 +6,10 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.*
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists.AccountAdapter
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.Results
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.*
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountAdapter
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.Results
|
||||
|
||||
/**
|
||||
* 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
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
||||
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||
SearchContentRepository<Account>(
|
||||
apiHolder.setDomainToCurrentUser(db),
|
||||
apiHolder.setToCurrentUser(),
|
||||
Results.SearchType.accounts,
|
||||
db.userDao().getActiveUser()!!.accessToken,
|
||||
query
|
||||
)
|
||||
)
|
||||
).get(FeedViewModel::class.java) as FeedViewModel<Account>
|
||||
).get("searchAccounts", FeedViewModel::class.java) as FeedViewModel<Account>
|
||||
|
||||
launch()
|
||||
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.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||
import com.h.pixeldroid.utils.api.objects.Results
|
||||
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 javax.inject.Inject
|
||||
|
||||
@ -15,14 +15,13 @@ import javax.inject.Inject
|
||||
* Repository class to perform searches
|
||||
*
|
||||
* 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].
|
||||
*/
|
||||
class SearchContentRepository<T: FeedContent> @ExperimentalPagingApi
|
||||
@Inject constructor(
|
||||
private val api: PixelfedAPI,
|
||||
private val type: Results.SearchType,
|
||||
private val accessToken: String,
|
||||
private val query: String,
|
||||
): UncachedContentRepository<T> {
|
||||
override fun getStream(): Flow<PagingData<T>> {
|
||||
@ -32,7 +31,7 @@ class SearchContentRepository<T: FeedContent> @ExperimentalPagingApi
|
||||
pageSize = NETWORK_PAGE_SIZE,
|
||||
enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
SearchPagingSource<T>(api, query, type, accessToken)
|
||||
SearchPagingSource<T>(api, query, type)
|
||||
}
|
||||
).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.os.Bundle
|
||||
@ -11,13 +11,14 @@ 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.databinding.FragmentTagsBinding
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||
import com.h.pixeldroid.utils.api.objects.Results
|
||||
import com.h.pixeldroid.utils.api.objects.Tag
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.FragmentTagsBinding
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||
import org.pixeldroid.app.utils.api.objects.Results
|
||||
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.
|
||||
@ -44,16 +45,15 @@ class SearchHashtagFragment : UncachedFeedFragment<Tag>() {
|
||||
|
||||
// get the view model
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(this, ViewModelFactory(
|
||||
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||
SearchContentRepository<Tag>(
|
||||
apiHolder.setDomainToCurrentUser(db),
|
||||
apiHolder.setToCurrentUser(),
|
||||
Results.SearchType.hashtags,
|
||||
db.userDao().getActiveUser()!!.accessToken,
|
||||
query
|
||||
)
|
||||
)
|
||||
)
|
||||
.get(FeedViewModel::class.java) as FeedViewModel<Tag>
|
||||
.get("searchHashtag", FeedViewModel::class.java) as FeedViewModel<Tag>
|
||||
|
||||
launch()
|
||||
initSearch()
|
||||
@ -87,7 +87,7 @@ class HashTagAdapter : PagingDataAdapter<Tag, RecyclerView.ViewHolder>(
|
||||
companion object {
|
||||
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Tag>() {
|
||||
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 =
|
||||
@ -108,7 +108,9 @@ class HashTagViewHolder(binding: FragmentTagsBinding) : RecyclerView.ViewHolder(
|
||||
|
||||
init {
|
||||
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.PagingState
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||
import com.h.pixeldroid.utils.api.objects.Results
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||
import org.pixeldroid.app.utils.api.objects.Results
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
@ -15,16 +15,16 @@ class SearchPagingSource<T: FeedContent>(
|
||||
private val api: PixelfedAPI,
|
||||
private val query: String,
|
||||
private val type: Results.SearchType,
|
||||
private val accessToken: String,
|
||||
) : PagingSource<Int, T>() {
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
|
||||
val position = params.key
|
||||
return try {
|
||||
val response = api.search(authorization = "Bearer $accessToken",
|
||||
offset = position?.toString(),
|
||||
q = query,
|
||||
val response = api.search(
|
||||
type = type,
|
||||
limit = params.loadSize.toString())
|
||||
q = query,
|
||||
limit = params.loadSize.toString(),
|
||||
offset = position?.toString()
|
||||
)
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ -34,10 +34,12 @@ class SearchPagingSource<T: FeedContent>(
|
||||
Results.SearchType.statuses -> response.statuses
|
||||
} as List<T>
|
||||
|
||||
val nextKey = if (repos.isEmpty()) null else (position ?: 0) + repos.size
|
||||
|
||||
LoadResult.Page(
|
||||
data = repos,
|
||||
prevKey = null,
|
||||
nextKey = if (repos.isEmpty()) null else (position ?: 0) + repos.size
|
||||
nextKey = if(nextKey == position) null else nextKey
|
||||
)
|
||||
} catch (exception: HttpException) {
|
||||
LoadResult.Error(exception)
|
||||
@ -46,8 +48,9 @@ class SearchPagingSource<T: FeedContent>(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, T>): Int? =
|
||||
state.anchorPosition?.run {
|
||||
state.closestItemToPosition(this)?.id?.toIntOrNull()
|
||||
}
|
||||
/**
|
||||
* FIXME if implemented with [PagingState.anchorPosition], this breaks refreshes? How is this
|
||||
* 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 com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||
import com.h.pixeldroid.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_ID_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_TAG
|
||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||
import org.pixeldroid.app.utils.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) {
|
||||
supportActionBar?.title =
|
||||
if (followers) {
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.profile
|
||||
package org.pixeldroid.app.profile
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
@ -7,6 +7,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -18,34 +19,31 @@ import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityProfileBinding
|
||||
import com.h.pixeldroid.databinding.FragmentProfilePostsBinding
|
||||
import com.h.pixeldroid.posts.PostActivity
|
||||
import com.h.pixeldroid.posts.feeds.initAdapter
|
||||
import com.h.pixeldroid.posts.feeds.launch
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
|
||||
import com.h.pixeldroid.posts.parseHTMLText
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Account
|
||||
import com.h.pixeldroid.utils.api.objects.FeedContent
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.db.entities.UserDatabaseEntity
|
||||
import com.h.pixeldroid.utils.openUrl
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
||||
import org.pixeldroid.app.databinding.FragmentProfilePostsBinding
|
||||
import org.pixeldroid.app.posts.PostActivity
|
||||
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||
import org.pixeldroid.app.posts.feeds.launch
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.profile.ProfileContentRepository
|
||||
import org.pixeldroid.app.posts.parseHTMLText
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.ImageConverter
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Account
|
||||
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||
import org.pixeldroid.app.utils.openUrl
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ProfileActivity : BaseActivity() {
|
||||
|
||||
private lateinit var pixelfedAPI : PixelfedAPI
|
||||
private lateinit var accessToken : String
|
||||
private lateinit var domain : String
|
||||
private lateinit var accountId : String
|
||||
private lateinit var binding: ActivityProfileBinding
|
||||
@ -66,8 +64,6 @@ class ProfileActivity : BaseActivity() {
|
||||
user = db.userDao().getActiveUser()
|
||||
|
||||
domain = user?.instance_uri.orEmpty()
|
||||
pixelfedAPI = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
accessToken = user?.accessToken.orEmpty()
|
||||
|
||||
// Set profile according to given account
|
||||
val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
|
||||
@ -77,9 +73,8 @@ class ProfileActivity : BaseActivity() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
viewModel = ViewModelProvider(this, ProfileViewModelFactory(
|
||||
ProfileContentRepository(
|
||||
apiHolder.setDomainToCurrentUser(db),
|
||||
db.userDao().getActiveUser()!!.accessToken,
|
||||
accountId
|
||||
apiHolder.setToCurrentUser(),
|
||||
accountId
|
||||
)
|
||||
)
|
||||
).get(FeedViewModel::class.java) as FeedViewModel<Status>
|
||||
@ -96,9 +91,7 @@ class ProfileActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
setContent(account)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
job = launch(job, lifecycleScope, viewModel as FeedViewModel<FeedContent>,
|
||||
profileAdapter as PagingDataAdapter<FeedContent, RecyclerView.ViewHolder>)
|
||||
job = launch(job, lifecycleScope, viewModel, profileAdapter)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,18 +107,14 @@ class ProfileActivity : BaseActivity() {
|
||||
binding.profileRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setContent(account: Account?) {
|
||||
if(account != null) {
|
||||
setViews(account)
|
||||
} else {
|
||||
lifecycleScope.launchWhenResumed {
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val myAccount: Account = try {
|
||||
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
||||
api.verifyCredentials()
|
||||
} catch (exception: IOException) {
|
||||
Log.e("ProfileActivity:", exception.toString())
|
||||
return@launchWhenResumed showError()
|
||||
@ -162,9 +151,9 @@ class ProfileActivity : BaseActivity() {
|
||||
)
|
||||
|
||||
binding.descriptionTextView.text = parseHTMLText(
|
||||
account.note ?: "", emptyList(), pixelfedAPI,
|
||||
applicationContext, "Bearer $accessToken",
|
||||
lifecycleScope
|
||||
account.note ?: "", emptyList(), apiHolder,
|
||||
applicationContext,
|
||||
lifecycleScope
|
||||
)
|
||||
|
||||
val displayName = account.getDisplayName()
|
||||
@ -235,13 +224,14 @@ class ProfileActivity : BaseActivity() {
|
||||
// Get relationship between the two users (credential and this) and set followButton accordingly
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val relationship = pixelfedAPI.checkRelationships(
|
||||
"Bearer $accessToken", listOf(account.id.orEmpty())
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val relationship = api.checkRelationships(
|
||||
listOf(account.id.orEmpty())
|
||||
).firstOrNull()
|
||||
|
||||
if(relationship != null){
|
||||
if (relationship.following) {
|
||||
setOnClickUnfollow(account)
|
||||
if (relationship.following == true || relationship.requested == true) {
|
||||
setOnClickUnfollow(account, relationship.requested == true)
|
||||
} else {
|
||||
setOnClickFollow(account)
|
||||
}
|
||||
@ -268,8 +258,10 @@ class ProfileActivity : BaseActivity() {
|
||||
setOnClickListener {
|
||||
lifecycleScope.launchWhenResumed {
|
||||
try {
|
||||
pixelfedAPI.follow(account.id.orEmpty(), "Bearer $accessToken")
|
||||
setOnClickUnfollow(account)
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
val rel = api.follow(account.id.orEmpty())
|
||||
if(rel.following == true) setOnClickUnfollow(account, rel.requested == true)
|
||||
else setOnClickFollow(account)
|
||||
} catch (exception: IOException) {
|
||||
Log.e("FOLLOW ERROR", exception.toString())
|
||||
Toast.makeText(
|
||||
@ -287,29 +279,46 @@ class ProfileActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOnClickUnfollow(account: Account) {
|
||||
private fun setOnClickUnfollow(account: Account, requested: Boolean) {
|
||||
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 {
|
||||
try {
|
||||
pixelfedAPI.unfollow(account.id.orEmpty(), "Bearer $accessToken")
|
||||
setOnClickFollow(account)
|
||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
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) {
|
||||
Log.e("FOLLOW ERROR", exception.toString())
|
||||
Toast.makeText(
|
||||
applicationContext, getString(R.string.unfollow_error),
|
||||
Toast.LENGTH_SHORT
|
||||
applicationContext, getString(R.string.unfollow_error),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} catch (exception: HttpException) {
|
||||
Toast.makeText(
|
||||
applicationContext, getString(R.string.unfollow_error),
|
||||
Toast.LENGTH_SHORT
|
||||
applicationContext, getString(R.string.unfollow_error),
|
||||
Toast.LENGTH_SHORT
|
||||
).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.view.LayoutInflater
|
||||
@ -7,11 +7,11 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.content.res.AppCompatResources.getDrawable
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.posts.PostActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromDrawable
|
||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setSquareImageFromURL
|
||||
import org.pixeldroid.app.posts.PostActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.utils.ImageConverter.Companion.setSquareImageFromDrawable
|
||||
import org.pixeldroid.app.utils.ImageConverter.Companion.setSquareImageFromURL
|
||||
|
||||
/**
|
||||
* [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.content.Intent
|
||||
@ -8,12 +8,12 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
||||
import com.h.pixeldroid.posts.feeds.uncachedFeeds.search.SearchPostsFragment
|
||||
import com.h.pixeldroid.utils.api.objects.Results
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
||||
import org.pixeldroid.app.utils.api.objects.Results
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
|
||||
class SearchActivity : BaseActivity() {
|
||||
|
||||
@ -47,14 +47,9 @@ class SearchActivity : BaseActivity() {
|
||||
setupTabs(tabs, searchType)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun createSearchTabs(query: String): Array<Fragment>{
|
||||
|
||||
val searchFeedFragment = SearchPostsFragment()
|
||||
val searchFeedFragment = UncachedPostsFragment()
|
||||
val searchAccountListFragment =
|
||||
SearchAccountFragment()
|
||||
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.searchDiscover
|
||||
package org.pixeldroid.app.searchDiscover
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
@ -11,21 +11,15 @@ import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.FragmentSearchBinding
|
||||
import com.h.pixeldroid.profile.ProfilePostViewHolder
|
||||
import com.h.pixeldroid.utils.api.PixelfedAPI
|
||||
import com.h.pixeldroid.utils.api.objects.Status
|
||||
import com.h.pixeldroid.posts.PostActivity
|
||||
import com.h.pixeldroid.utils.BaseFragment
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import com.h.pixeldroid.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 org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.FragmentSearchBinding
|
||||
import org.pixeldroid.app.profile.ProfilePostViewHolder
|
||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||
import org.pixeldroid.app.utils.api.objects.Status
|
||||
import org.pixeldroid.app.posts.PostActivity
|
||||
import org.pixeldroid.app.utils.BaseFragment
|
||||
import org.pixeldroid.app.utils.ImageConverter
|
||||
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
@ -37,7 +31,6 @@ class SearchDiscoverFragment : BaseFragment() {
|
||||
private lateinit var api: PixelfedAPI
|
||||
private lateinit var recycler : RecyclerView
|
||||
private lateinit var adapter : DiscoverRecyclerViewAdapter
|
||||
private lateinit var accessToken: String
|
||||
|
||||
var binding: FragmentSearchBinding by bindingLifecycleAware()
|
||||
|
||||
@ -66,9 +59,7 @@ class SearchDiscoverFragment : BaseFragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
|
||||
|
||||
accessToken = db.userDao().getActiveUser()?.accessToken.orEmpty()
|
||||
api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
|
||||
getDiscover()
|
||||
|
||||
@ -93,8 +84,9 @@ class SearchDiscoverFragment : BaseFragment() {
|
||||
private fun getDiscover() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
val discoverPosts = api.discover("Bearer $accessToken")
|
||||
val discoverPosts = api.discover()
|
||||
adapter.addPosts(discoverPosts.posts)
|
||||
binding.discoverNoInfiniteLoad.visibility = View.VISIBLE
|
||||
showError(show = false)
|
||||
} catch (exception: IOException) {
|
||||
showError()
|
||||
@ -108,7 +100,7 @@ class SearchDiscoverFragment : BaseFragment() {
|
||||
* [RecyclerView.Adapter] that can display a list of [Status]s' thumbnails for the discover view
|
||||
*/
|
||||
class DiscoverRecyclerViewAdapter: RecyclerView.Adapter<ProfilePostViewHolder>() {
|
||||
private val posts: ArrayList<Status> = ArrayList()
|
||||
private val posts: ArrayList<Status?> = ArrayList()
|
||||
|
||||
fun addPosts(newPosts : List<Status>) {
|
||||
posts.clear()
|
||||
@ -124,12 +116,12 @@ class SearchDiscoverFragment : BaseFragment() {
|
||||
|
||||
override fun onBindViewHolder(holder: ProfilePostViewHolder, position: Int) {
|
||||
val post = posts[position]
|
||||
if(post.media_attachments?.size ?: 0 > 1) {
|
||||
if(post?.media_attachments?.size ?: 0 > 1) {
|
||||
holder.albumIcon.visibility = View.VISIBLE
|
||||
} else {
|
||||
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 {
|
||||
val intent = Intent(holder.postView.context, PostActivity::class.java)
|
||||
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.os.Bundle
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityAboutBinding
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import org.pixeldroid.app.BuildConfig
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityAboutBinding
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
|
||||
class AboutActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
@ -1,9 +1,9 @@
|
||||
package com.h.pixeldroid.settings
|
||||
package org.pixeldroid.app.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.databinding.ActivityLicensesBinding
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.databinding.ActivityLicensesBinding
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
|
||||
class LicenseActivity : BaseActivity() {
|
||||
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.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.h.pixeldroid.MainActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.utils.BaseActivity
|
||||
import com.h.pixeldroid.utils.setThemeFromPreferences
|
||||
import org.pixeldroid.app.MainActivity
|
||||
import org.pixeldroid.app.R
|
||||
import org.pixeldroid.app.utils.BaseActivity
|
||||
import org.pixeldroid.app.utils.setThemeFromPreferences
|
||||
|
||||
class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
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.res.Configuration
|
||||
@ -7,8 +7,8 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -28,6 +28,11 @@ open class BaseActivity : AppCompatActivity() {
|
||||
super.attachBaseContext(updateBaseContextLocale(base))
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateBaseContextLocale(context: Context): Context {
|
||||
val language = PreferenceManager.getDefaultSharedPreferences(context).getString("language", "default") ?: "default"
|
||||
if(language == "default"){
|
@ -1,9 +1,9 @@
|
||||
package com.h.pixeldroid.utils
|
||||
package org.pixeldroid.app.utils
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.h.pixeldroid.utils.db.AppDatabase
|
||||
import com.h.pixeldroid.utils.di.PixelfedAPIHolder
|
||||
import org.pixeldroid.app.utils.db.AppDatabase
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
@ -1,4 +1,4 @@
|
||||
package com.h.pixeldroid.utils
|
||||
package org.pixeldroid.app.utils
|
||||
|
||||
/**
|
||||
* Blurhash implementation from blurhash project:
|
||||
@ -11,7 +11,6 @@ import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import com.h.pixeldroid.utils.api.objects.Attachment
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
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.view.View
|
||||
@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.R
|
||||
import org.pixeldroid.app.R
|
||||
|
||||
class ImageConverter {
|
||||
companion object {
|
@ -1,11 +1,10 @@
|
||||
package com.h.pixeldroid.utils
|
||||
package org.pixeldroid.app.utils
|
||||
|
||||
import android.app.Application
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.h.pixeldroid.utils.di.*
|
||||
import org.pixeldroid.app.utils.di.*
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import org.ligi.tracedroid.TraceDroid
|
||||
import org.ligi.tracedroid.sending.sendTraceDroidStackTracesIfExist
|
||||
|
||||
|
||||
class PixelDroidApplication: Application() {
|
||||
@ -27,8 +26,6 @@ class PixelDroidApplication: Application() {
|
||||
.aPIModule(APIModule())
|
||||
.build()
|
||||
mApplicationComponent.inject(this)
|
||||
|
||||
Iconics.init(applicationContext)
|
||||
}
|
||||
|
||||
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