Merge master
This commit is contained in:
commit
0fe2c54940
@ -2,6 +2,7 @@ image: registry.gitlab.com/fdroid/fdroidserver:buildserver-bullseye
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
GIT_SUBMODULE_FORCE_HTTPS: "true"
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- export GRADLE_USER_HOME=`pwd`/.gradle
|
- export GRADLE_USER_HOME=`pwd`/.gradle
|
||||||
@ -19,7 +20,7 @@ before_script:
|
|||||||
- test -e $cmdline_tools_latest && export PATH="$cmdline_tools_latest:$PATH"
|
- test -e $cmdline_tools_latest && export PATH="$cmdline_tools_latest:$PATH"
|
||||||
|
|
||||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdk\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||||
- echo y | sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" > /dev/null
|
- echo y | sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" > /dev/null
|
||||||
|
|
||||||
- apt-get update || apt-get update
|
- apt-get update || apt-get update
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[submodule "scrambler"]
|
[submodule "scrambler"]
|
||||||
path = scrambler
|
path = scrambler
|
||||||
url = https://gitlab.com/artectrex/scrambler.git
|
url = https://gitlab.com/artectrex/scrambler.git
|
||||||
|
[submodule "pixel_common"]
|
||||||
|
path = pixel_common
|
||||||
|
url = git@gitlab.shinice.net:pixeldroid/pixel_common.git
|
||||||
|
112
app/build.gradle
112
app/build.gradle
@ -1,41 +1,34 @@
|
|||||||
import com.android.build.api.dsl.ManagedVirtualDevice
|
import com.android.build.api.dsl.ManagedVirtualDevice
|
||||||
|
|
||||||
plugins {
|
|
||||||
id "com.mikepenz.aboutlibraries.plugin" version "10.5.2"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'jacoco'
|
apply plugin: 'jacoco'
|
||||||
apply plugin: "kotlin-parcelize"
|
apply plugin: "kotlin-parcelize"
|
||||||
|
apply plugin: 'com.google.devtools.ksp'
|
||||||
|
|
||||||
|
|
||||||
// Force latest version of Jacoco, initially done to resolve https://github.com/jacoco/jacoco/issues/1155
|
|
||||||
jacoco.toolVersion = "0.8.7"
|
|
||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
namespace 'org.pixeldroid.app'
|
namespace 'org.pixeldroid.app'
|
||||||
compileSdkVersion 33
|
compileSdk 34
|
||||||
buildToolsVersion '33.0.0'
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
generateLocaleConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(17)
|
||||||
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8
|
|
||||||
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"]
|
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"]
|
||||||
}
|
}
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 33
|
versionCode 26
|
||||||
versionCode 24
|
targetSdkVersion 34
|
||||||
versionName "1.0.beta" + versionCode
|
versionName "1.0.beta" + versionCode
|
||||||
|
|
||||||
//TODO add resConfigs("en", "fr", "ja",...) ?
|
//TODO add resConfigs("en", "fr", "ja",...) ?
|
||||||
@ -87,8 +80,9 @@ android {
|
|||||||
/**
|
/**
|
||||||
* Make a string with the application_id (available in xml etc)
|
* Make a string with the application_id (available in xml etc)
|
||||||
*/
|
*/
|
||||||
android.applicationVariants.all { variant ->
|
android.applicationVariants.configureEach { variant ->
|
||||||
variant.resValue 'string', 'application_id', variant.applicationId
|
variant.resValue 'string', 'application_id', variant.applicationId
|
||||||
|
variant.resValue "string", "versionName", variant.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
testOptions {
|
testOptions {
|
||||||
@ -113,11 +107,9 @@ android {
|
|||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
dataBinding = true
|
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
lint {
|
lint {
|
||||||
//We can't expect translators to always keep up immediately:
|
//We can't expect translators to always keep up immediately:
|
||||||
// don't fail if a a string is untranslated
|
// don't fail if a a string is untranslated
|
||||||
@ -131,40 +123,40 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.2'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AndroidX dependencies:
|
* AndroidX dependencies:
|
||||||
*/
|
*/
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.core:core-ktx:1.9.0'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
|
||||||
implementation "androidx.browser:browser:1.5.0"
|
implementation "androidx.browser:browser:1.7.0"
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
|
||||||
implementation 'androidx.paging:paging-runtime-ktx:3.1.1'
|
implementation 'androidx.paging:paging-runtime-ktx:3.2.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2'
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.6.1"
|
implementation "androidx.lifecycle:lifecycle-common-java8:2.6.2"
|
||||||
implementation "androidx.annotation:annotation:1.6.0"
|
implementation "androidx.annotation:annotation:1.7.1"
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation "androidx.activity:activity-ktx:1.7.0"
|
implementation "androidx.activity:activity-ktx:1.8.2"
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.6'
|
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
implementation 'androidx.work:work-runtime-ktx:2.9.0'
|
||||||
implementation 'androidx.media2:media2-widget:1.2.1'
|
implementation 'androidx.media2:media2-widget:1.2.1'
|
||||||
implementation 'androidx.media2:media2-player:1.2.1'
|
implementation 'androidx.media2:media2-player:1.2.1'
|
||||||
|
|
||||||
|
|
||||||
// Use the most recent version of CameraX
|
// Use the most recent version of CameraX
|
||||||
def cameraX_version = '1.2.2'
|
def cameraX_version = '1.3.1'
|
||||||
implementation "androidx.camera:camera-core:$cameraX_version"
|
implementation "androidx.camera:camera-core:$cameraX_version"
|
||||||
implementation "androidx.camera:camera-camera2:$cameraX_version"
|
implementation "androidx.camera:camera-camera2:$cameraX_version"
|
||||||
// CameraX Lifecycle library
|
// CameraX Lifecycle library
|
||||||
@ -173,9 +165,9 @@ dependencies {
|
|||||||
// CameraX View class
|
// CameraX View class
|
||||||
implementation "androidx.camera:camera-view:$cameraX_version"
|
implementation "androidx.camera:camera-view:$cameraX_version"
|
||||||
|
|
||||||
def room_version = "2.5.1"
|
def room_version = "2.6.1"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
ksp "androidx.room:room-compiler:$room_version"
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
implementation "androidx.room:room-paging:$room_version"
|
implementation "androidx.room:room-paging:$room_version"
|
||||||
|
|
||||||
@ -186,16 +178,13 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.8.0'
|
implementation 'com.google.android.material:material:1.11.0'
|
||||||
|
|
||||||
//Dagger (dependency injection)
|
//Dagger (dependency injection)
|
||||||
implementation 'com.google.dagger:dagger-android:2.45'
|
implementation 'com.google.dagger:dagger:2.48'
|
||||||
implementation 'com.google.dagger:dagger-android-support:2.44'
|
ksp 'com.google.dagger:dagger-compiler:2.48'
|
||||||
// if you use the support libraries
|
|
||||||
kapt 'com.google.dagger:dagger-android-processor:2.44'
|
|
||||||
kapt 'com.google.dagger:dagger-compiler:2.44'
|
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
|
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
|
||||||
@ -203,11 +192,11 @@ dependencies {
|
|||||||
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
|
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
|
||||||
implementation 'com.github.connyduck:sparkbutton:4.1.0'
|
implementation 'com.github.connyduck:sparkbutton:4.1.0'
|
||||||
|
|
||||||
|
implementation 'org.pixeldroid.pixeldroid:android-media-editor:1.5'
|
||||||
implementation 'org.pixeldroid.pixeldroid:android-media-editor:1.4'
|
|
||||||
implementation project(path: ':scrambler')
|
implementation project(path: ':scrambler')
|
||||||
|
implementation project(path: ':pixel_common')
|
||||||
|
|
||||||
implementation('com.github.bumptech.glide:glide:4.14.2') {
|
implementation('com.github.bumptech.glide:glide:4.16.0') {
|
||||||
exclude group: "com.android.support"
|
exclude group: "com.android.support"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,9 +205,9 @@ dependencies {
|
|||||||
// Excludes the support library because it's already included by Glide.
|
// Excludes the support library because it's already included by Glide.
|
||||||
transitive = false
|
transitive = false
|
||||||
}
|
}
|
||||||
implementation 'com.github.bumptech.glide:annotations:4.14.2'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
|
||||||
kapt 'com.github.bumptech.glide:compiler:4.14.2'
|
ksp 'com.github.bumptech.glide:ksp:4.14.2'
|
||||||
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
|
||||||
@ -232,15 +221,10 @@ dependencies {
|
|||||||
implementation 'com.mikepenz:iconics-views:5.4.0'
|
implementation 'com.mikepenz:iconics-views:5.4.0'
|
||||||
implementation 'com.mikepenz:google-material-typeface:4.0.0.2-kotlin@aar'
|
implementation 'com.mikepenz:google-material-typeface:4.0.0.2-kotlin@aar'
|
||||||
|
|
||||||
|
|
||||||
implementation 'com.karumi:dexter:6.2.3'
|
|
||||||
|
|
||||||
implementation 'com.github.ligi:tracedroid:4.1'
|
implementation 'com.github.ligi:tracedroid:4.1'
|
||||||
|
|
||||||
implementation 'me.relex:circleindicator:2.1.6'
|
implementation 'me.relex:circleindicator:2.1.6'
|
||||||
|
|
||||||
implementation 'com.mikepenz:aboutlibraries-core:10.6.0'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not in release, so not mentioned in licenses list
|
* Not in release, so not mentioned in licenses list
|
||||||
*/
|
*/
|
||||||
@ -251,7 +235,7 @@ dependencies {
|
|||||||
androidTestImplementation 'com.linkedin.testbutler:test-butler-library:2.2.1'
|
androidTestImplementation 'com.linkedin.testbutler:test-butler-library:2.2.1'
|
||||||
androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.2.1'
|
androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.2.1'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.work:work-testing:2.8.1'
|
androidTestImplementation 'androidx.work:work-testing:2.9.0'
|
||||||
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.34.0'
|
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.34.0'
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
@ -271,7 +255,7 @@ dependencies {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test) {
|
tasks.withType(Test).configureEach {
|
||||||
jacoco.includeNoLocationClasses = true
|
jacoco.includeNoLocationClasses = true
|
||||||
jacoco.excludes = ['jdk.internal.*']
|
jacoco.excludes = ['jdk.internal.*']
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:targetPackage="org.pixeldroid.app.debug"
|
android:targetPackage="org.pixeldroid.app.debug"
|
||||||
android:targetClass="org.pixeldroid.app.postCreation.camera.CameraActivityShortcut" />
|
android:targetClass="org.pixeldroid.app.postCreation.camera.CameraActivity" />
|
||||||
<categories android:name="android.shortcut.conversation" />
|
<categories android:name="android.shortcut.conversation" />
|
||||||
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
|
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
|
||||||
</shortcut>
|
</shortcut>
|
||||||
|
@ -5,14 +5,13 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.camera.any"
|
android:name="android.hardware.camera.any"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.location.gps" />
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.camera"
|
android:name="android.hardware.camera"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
@ -47,7 +46,6 @@
|
|||||||
android:theme="@style/AppTheme.ActionBar.Transparent" />
|
android:theme="@style/AppTheme.ActionBar.Transparent" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".profile.EditProfileActivity"
|
android:name=".profile.EditProfileActivity"
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.MediaViewerActivity"
|
android:name=".posts.MediaViewerActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
@ -69,12 +67,16 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".posts.ReportActivity"
|
android:name=".posts.ReportActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
|
android:theme="@style/BaseAppTheme"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".stories.StoriesActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".postCreation.PostCreationActivity"
|
android:name=".postCreation.PostCreationActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/BaseAppTheme.NoActionBar"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:theme="@style/BaseAppTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
@ -87,27 +89,32 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".profile.FollowsActivity"
|
android:name=".profile.FollowsActivity"
|
||||||
|
android:theme="@style/BaseAppTheme"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.feeds.uncachedFeeds.hashtags.HashTagActivity"
|
android:name=".posts.feeds.uncachedFeeds.hashtags.HashTagActivity"
|
||||||
|
android:theme="@style/BaseAppTheme"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.PostActivity"
|
android:name=".posts.PostActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity"
|
||||||
|
android:theme="@style/BaseAppTheme" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".profile.ProfileActivity"
|
android:name=".profile.ProfileActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity"
|
||||||
<activity android:name=".profile.CollectionActivity" />
|
android:theme="@style/BaseAppTheme"/>
|
||||||
|
<activity android:name=".profile.CollectionActivity"
|
||||||
|
android:theme="@style/BaseAppTheme"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".settings.SettingsActivity"
|
android:name=".settings.SettingsActivity"
|
||||||
android:label="@string/title_activity_settings2"
|
android:label="@string/title_activity_settings2"
|
||||||
android:parentActivityName=".MainActivity"
|
android:parentActivityName=".MainActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:theme="@style/BaseAppTheme" />
|
||||||
tools:ignore="LockedOrientationActivity" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -132,7 +139,7 @@
|
|||||||
android:name=".LoginActivity"
|
android:name=".LoginActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
android:theme="@style/BaseAppTheme.NoActionBar"
|
android:theme="@style/BaseAppTheme"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
tools:ignore="LockedOrientationActivity">
|
tools:ignore="LockedOrientationActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -149,6 +156,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".searchDiscover.SearchActivity"
|
android:name=".searchDiscover.SearchActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:theme="@style/BaseAppTheme"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity">
|
tools:ignore="LockedOrientationActivity">
|
||||||
@ -160,17 +168,8 @@
|
|||||||
android:name="android.app.searchable"
|
android:name="android.app.searchable"
|
||||||
android:resource="@xml/searchable" />
|
android:resource="@xml/searchable" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".searchDiscover.TrendingActivity" />
|
<activity android:name=".searchDiscover.TrendingActivity"
|
||||||
<activity
|
android:theme="@style/BaseAppTheme" />
|
||||||
android:name=".settings.AboutActivity"
|
|
||||||
android:parentActivityName=".settings.SettingsActivity"
|
|
||||||
android:screenOrientation="sensorPortrait"
|
|
||||||
tools:ignore="LockedOrientationActivity" />
|
|
||||||
<activity
|
|
||||||
android:name=".settings.LicenseActivity"
|
|
||||||
android:parentActivityName=".settings.AboutActivity"
|
|
||||||
android:screenOrientation="sensorPortrait"
|
|
||||||
tools:ignore="LockedOrientationActivity" />
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.pixeldroid.app
|
package org.pixeldroid.app
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
@ -16,7 +15,7 @@ import kotlinx.coroutines.async
|
|||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.databinding.ActivityLoginBinding
|
import org.pixeldroid.app.databinding.ActivityLoginBinding
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Application
|
import org.pixeldroid.app.utils.api.objects.Application
|
||||||
import org.pixeldroid.app.utils.api.objects.Instance
|
import org.pixeldroid.app.utils.api.objects.Instance
|
||||||
@ -45,7 +44,7 @@ since they do not depend on each other)
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class LoginActivity : BaseThemedWithoutBarActivity() {
|
class LoginActivity : BaseActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val PACKAGE_ID = BuildConfig.APPLICATION_ID
|
private const val PACKAGE_ID = BuildConfig.APPLICATION_ID
|
||||||
|
@ -12,6 +12,7 @@ import android.util.Log
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
@ -51,7 +52,7 @@ import org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment
|
|||||||
import org.pixeldroid.app.profile.ProfileActivity
|
import org.pixeldroid.app.profile.ProfileActivity
|
||||||
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||||
import org.pixeldroid.app.settings.SettingsActivity
|
import org.pixeldroid.app.settings.SettingsActivity
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Notification
|
import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
import org.pixeldroid.app.utils.db.addUser
|
import org.pixeldroid.app.utils.db.addUser
|
||||||
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity
|
||||||
@ -66,7 +67,7 @@ import org.pixeldroid.app.utils.notificationsWorker.removeNotificationChannelsFr
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : BaseThemedWithoutBarActivity() {
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var header: AccountHeaderView
|
private lateinit var header: AccountHeaderView
|
||||||
private var user: UserDatabaseEntity? = null
|
private var user: UserDatabaseEntity? = null
|
||||||
@ -231,10 +232,7 @@ class MainActivity : BaseThemedWithoutBarActivity() {
|
|||||||
nameRes = R.string.logout
|
nameRes = R.string.logout
|
||||||
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
||||||
},
|
},
|
||||||
primaryDrawerItem {
|
)
|
||||||
nameRes = R.string.logout
|
|
||||||
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
|
||||||
})
|
|
||||||
binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
|
binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
|
||||||
when (position){
|
when (position){
|
||||||
1 -> launchActivity(ProfileActivity())
|
1 -> launchActivity(ProfileActivity())
|
||||||
@ -244,6 +242,18 @@ class MainActivity : BaseThemedWithoutBarActivity() {
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closes the drawer if it is open, when we press the back button
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
// Handle the back button event
|
||||||
|
if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)){
|
||||||
|
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.isEnabled = false
|
||||||
|
super.onBackPressedDispatcher.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logOut(){
|
private fun logOut(){
|
||||||
@ -486,16 +496,4 @@ class MainActivity : BaseThemedWithoutBarActivity() {
|
|||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the drawer if it is open, when we press the back button
|
|
||||||
*/
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)){
|
|
||||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -5,13 +5,13 @@ import androidx.navigation.NavController
|
|||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
import org.pixeldroid.app.databinding.ActivityPostCreationBinding
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
|
||||||
const val TAG = "Post Creation Activity"
|
const val TAG = "Post Creation Activity"
|
||||||
|
|
||||||
class PostCreationActivity : BaseThemedWithoutBarActivity() {
|
class PostCreationActivity : BaseActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal const val PICTURE_DESCRIPTION = "picture_description"
|
internal const val PICTURE_DESCRIPTION = "picture_description"
|
||||||
|
@ -33,8 +33,10 @@ import kotlinx.coroutines.launch
|
|||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.FragmentPostCreationBinding
|
import org.pixeldroid.app.databinding.FragmentPostCreationBinding
|
||||||
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||||
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
import org.pixeldroid.app.postCreation.carousel.CarouselItem
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.fileExtension
|
import org.pixeldroid.app.utils.fileExtension
|
||||||
@ -52,7 +54,7 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
private var user: UserDatabaseEntity? = null
|
private var user: UserDatabaseEntity? = null
|
||||||
private var instance: InstanceDatabaseEntity = InstanceDatabaseEntity("", "")
|
private var instance: InstanceDatabaseEntity = InstanceDatabaseEntity("", "")
|
||||||
|
|
||||||
private lateinit var binding: FragmentPostCreationBinding
|
private var binding: FragmentPostCreationBinding by bindingLifecycleAware()
|
||||||
private lateinit var model: PostCreationViewModel
|
private lateinit var model: PostCreationViewModel
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -63,6 +65,7 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
|
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
binding = FragmentPostCreationBinding.inflate(layoutInflater)
|
binding = FragmentPostCreationBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +86,8 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
requireActivity().intent.clipData!!,
|
requireActivity().intent.clipData!!,
|
||||||
instance,
|
instance,
|
||||||
requireActivity().intent.getStringExtra(PostCreationActivity.PICTURE_DESCRIPTION),
|
requireActivity().intent.getStringExtra(PostCreationActivity.PICTURE_DESCRIPTION),
|
||||||
requireActivity().intent.getBooleanExtra(PostCreationActivity.POST_NSFW, false)
|
requireActivity().intent.getBooleanExtra(PostCreationActivity.POST_NSFW, false),
|
||||||
|
requireActivity().intent.getBooleanExtra(CameraFragment.CAMERA_ACTIVITY_STORY, false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
model = _model
|
model = _model
|
||||||
@ -99,6 +103,7 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
binding.postCreationNextButton.isEnabled = newPhotoData.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@ -119,13 +124,26 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
binding.toolbarPostCreation.visibility =
|
binding.toolbarPostCreation.visibility =
|
||||||
if (uiState.isCarousel) View.VISIBLE else View.INVISIBLE
|
if (uiState.isCarousel) View.VISIBLE else View.INVISIBLE
|
||||||
binding.carousel.layoutCarousel = uiState.isCarousel
|
binding.carousel.layoutCarousel = uiState.isCarousel
|
||||||
|
|
||||||
|
if(uiState.storyCreation){
|
||||||
|
binding.toggleStoryPost.check(binding.buttonStory.id)
|
||||||
|
binding.buttonStory.isPressed = true
|
||||||
|
binding.carousel.showLayoutSwitchButton = false
|
||||||
|
binding.carousel.showIndicator = false
|
||||||
|
} else {
|
||||||
|
binding.toggleStoryPost.check(binding.buttonPost.id)
|
||||||
|
binding.carousel.showLayoutSwitchButton = true
|
||||||
|
binding.carousel.showIndicator = true
|
||||||
|
}
|
||||||
|
binding.carousel.maxEntries = uiState.maxEntries
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.carousel.apply {
|
binding.carousel.apply {
|
||||||
layoutCarouselCallback = { model.becameCarousel(it)}
|
layoutCarouselCallback = { model.becameCarousel(it)}
|
||||||
maxEntries = instance.albumLimit
|
maxEntries = if(model.uiState.value.storyCreation) 1 else instance.albumLimit
|
||||||
addPhotoButtonCallback = {
|
addPhotoButtonCallback = {
|
||||||
addPhoto()
|
addPhoto()
|
||||||
}
|
}
|
||||||
@ -133,9 +151,10 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
model.updateDescription(position, description)
|
model.updateDescription(position, description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// get the description and send the post
|
|
||||||
binding.postCreationSendButton.setOnClickListener {
|
// Validate the post and go to the next step of the post creation process
|
||||||
if (validatePost() && model.isNotEmpty()) {
|
binding.postCreationNextButton.setOnClickListener {
|
||||||
|
if (validatePost()) {
|
||||||
findNavController().navigate(R.id.action_postCreationFragment_to_postSubmissionFragment)
|
findNavController().navigate(R.id.action_postCreationFragment_to_postSubmissionFragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,6 +182,23 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.toggleStoryPost.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||||
|
// Only handle checked events
|
||||||
|
if (!isChecked) return@addOnButtonCheckedListener
|
||||||
|
|
||||||
|
when (checkedId) {
|
||||||
|
R.id.buttonStory -> {
|
||||||
|
model.storyMode(true)
|
||||||
|
}
|
||||||
|
R.id.buttonPost -> {
|
||||||
|
model.storyMode(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.backbutton.setOnClickListener{requireActivity().onBackPressedDispatcher.onBackPressed()}
|
||||||
|
|
||||||
// Clean up temporary files, if any
|
// Clean up temporary files, if any
|
||||||
val tempFiles = requireActivity().intent.getStringArrayExtra(PostCreationActivity.TEMP_FILES)
|
val tempFiles = requireActivity().intent.getStringArrayExtra(PostCreationActivity.TEMP_FILES)
|
||||||
tempFiles?.asList()?.forEach {
|
tempFiles?.asList()?.forEach {
|
||||||
@ -275,15 +311,18 @@ class PostCreationFragment : BaseFragment() {
|
|||||||
|
|
||||||
|
|
||||||
private fun validatePost(): Boolean {
|
private fun validatePost(): Boolean {
|
||||||
if (model.getPhotoData().value?.all { !it.video || it.videoEncodeComplete } == false) {
|
if (model.getPhotoData().value?.none { it.video && it.videoEncodeComplete == false } == true) {
|
||||||
|
// Encoding is done, i.e. none of the items are both a video and not done encoding.
|
||||||
|
// We return true if the post is not empty, false otherwise.
|
||||||
|
return model.getPhotoData().value?.isNotEmpty() == true
|
||||||
|
}
|
||||||
|
// Encoding is not done, show a dialog and return false to indicate validation failed
|
||||||
MaterialAlertDialogBuilder(requireActivity()).apply {
|
MaterialAlertDialogBuilder(requireActivity()).apply {
|
||||||
setMessage(R.string.still_encoding)
|
setMessage(R.string.still_encoding)
|
||||||
setNegativeButton(android.R.string.ok) { _, _ -> }
|
setNegativeButton(android.R.string.ok) { _, _ -> }
|
||||||
}.show()
|
}.show()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private val editResultContract: ActivityResultLauncher<Intent> = registerForActivityResult(
|
private val editResultContract: ActivityResultLauncher<Intent> = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()){
|
ActivityResultContracts.StartActivityForResult()){
|
||||||
|
@ -22,6 +22,7 @@ import androidx.preference.PreferenceManager
|
|||||||
import com.jarsilio.android.scrambler.exceptions.UnsupportedFileFormatException
|
import com.jarsilio.android.scrambler.exceptions.UnsupportedFileFormatException
|
||||||
import com.jarsilio.android.scrambler.stripMetadata
|
import com.jarsilio.android.scrambler.stripMetadata
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.core.Observable
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -54,7 +55,6 @@ import kotlin.collections.forEach
|
|||||||
import kotlin.collections.get
|
import kotlin.collections.get
|
||||||
import kotlin.collections.getOrNull
|
import kotlin.collections.getOrNull
|
||||||
import kotlin.collections.indexOfFirst
|
import kotlin.collections.indexOfFirst
|
||||||
import kotlin.collections.isNotEmpty
|
|
||||||
import kotlin.collections.mutableListOf
|
import kotlin.collections.mutableListOf
|
||||||
import kotlin.collections.mutableMapOf
|
import kotlin.collections.mutableMapOf
|
||||||
import kotlin.collections.plus
|
import kotlin.collections.plus
|
||||||
@ -70,6 +70,7 @@ data class PostCreationActivityUiState(
|
|||||||
val addPhotoButtonEnabled: Boolean = true,
|
val addPhotoButtonEnabled: Boolean = true,
|
||||||
val editPhotoButtonEnabled: Boolean = true,
|
val editPhotoButtonEnabled: Boolean = true,
|
||||||
val removePhotoButtonEnabled: Boolean = true,
|
val removePhotoButtonEnabled: Boolean = true,
|
||||||
|
val maxEntries: Int?,
|
||||||
|
|
||||||
val isCarousel: Boolean = true,
|
val isCarousel: Boolean = true,
|
||||||
|
|
||||||
@ -86,6 +87,11 @@ data class PostCreationActivityUiState(
|
|||||||
val uploadErrorVisible: Boolean = false,
|
val uploadErrorVisible: Boolean = false,
|
||||||
val uploadErrorExplanationText: String = "",
|
val uploadErrorExplanationText: String = "",
|
||||||
val uploadErrorExplanationVisible: Boolean = false,
|
val uploadErrorExplanationVisible: Boolean = false,
|
||||||
|
|
||||||
|
val storyCreation: Boolean,
|
||||||
|
val storyDuration: Int = 10,
|
||||||
|
val storyReplies: Boolean = true,
|
||||||
|
val storyReactions: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@ -98,7 +104,7 @@ data class PhotoData(
|
|||||||
var video: Boolean,
|
var video: Boolean,
|
||||||
var videoEncodeProgress: Int? = null,
|
var videoEncodeProgress: Int? = null,
|
||||||
var videoEncodeStabilizationFirstPass: Boolean? = null,
|
var videoEncodeStabilizationFirstPass: Boolean? = null,
|
||||||
var videoEncodeComplete: Boolean = true,
|
var videoEncodeComplete: Boolean? = null,
|
||||||
var videoEncodeError: Boolean = false,
|
var videoEncodeError: Boolean = false,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@ -107,8 +113,10 @@ class PostCreationViewModel(
|
|||||||
clipdata: ClipData? = null,
|
clipdata: ClipData? = null,
|
||||||
val instance: InstanceDatabaseEntity? = null,
|
val instance: InstanceDatabaseEntity? = null,
|
||||||
existingDescription: String? = null,
|
existingDescription: String? = null,
|
||||||
existingNSFW: Boolean = false
|
existingNSFW: Boolean = false,
|
||||||
|
storyCreation: Boolean = false,
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
private var storyPhotoDataBackup: MutableList<PhotoData>? = null
|
||||||
private val photoData: MutableLiveData<MutableList<PhotoData>> by lazy {
|
private val photoData: MutableLiveData<MutableList<PhotoData>> by lazy {
|
||||||
MutableLiveData<MutableList<PhotoData>>().also {
|
MutableLiveData<MutableList<PhotoData>>().also {
|
||||||
it.value = clipdata?.let { it1 -> addPossibleImages(it1, mutableListOf()) }
|
it.value = clipdata?.let { it1 -> addPossibleImages(it1, mutableListOf()) }
|
||||||
@ -128,7 +136,9 @@ class PostCreationViewModel(
|
|||||||
|
|
||||||
_uiState = MutableStateFlow(PostCreationActivityUiState(
|
_uiState = MutableStateFlow(PostCreationActivityUiState(
|
||||||
newPostDescriptionText = existingDescription ?: templateDescription,
|
newPostDescriptionText = existingDescription ?: templateDescription,
|
||||||
nsfw = existingNSFW
|
nsfw = existingNSFW,
|
||||||
|
maxEntries = if(storyCreation) 1 else instance?.albumLimit,
|
||||||
|
storyCreation = storyCreation
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,23 +155,27 @@ class PostCreationViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only public view on [photoData]
|
||||||
|
*/
|
||||||
fun getPhotoData(): LiveData<MutableList<PhotoData>> = photoData
|
fun getPhotoData(): LiveData<MutableList<PhotoData>> = photoData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will add as many images as possible to [photoData], from the [clipData], and if
|
* Will add as many images as possible to [photoData], from the [clipData], and if
|
||||||
* ([photoData].size + [clipData].itemCount) > [InstanceDatabaseEntity.albumLimit] then it will only add as many images
|
* ([photoData].size + [clipData].itemCount) > uiState.value.maxEntries then it will only add as many images
|
||||||
* as are legal (if any) and a dialog will be shown to the user alerting them of this fact.
|
* as are legal (if any) and a dialog will be shown to the user alerting them of this fact.
|
||||||
*/
|
*/
|
||||||
fun addPossibleImages(clipData: ClipData, previousList: MutableList<PhotoData>? = photoData.value): MutableList<PhotoData> {
|
fun addPossibleImages(clipData: ClipData, previousList: MutableList<PhotoData>? = photoData.value): MutableList<PhotoData> {
|
||||||
val dataToAdd: ArrayList<PhotoData> = arrayListOf()
|
val dataToAdd: ArrayList<PhotoData> = arrayListOf()
|
||||||
var count = clipData.itemCount
|
var count = clipData.itemCount
|
||||||
if(count + (previousList?.size ?: 0) > instance!!.albumLimit){
|
uiState.value.maxEntries?.let {
|
||||||
|
if(count + (previousList?.size ?: 0) > it){
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(userMessage = getApplication<PixelDroidApplication>().getString(R.string.total_exceeds_album_limit).format(instance.albumLimit))
|
currentUiState.copy(userMessage = getApplication<PixelDroidApplication>().getString(R.string.total_exceeds_album_limit).format(it))
|
||||||
}
|
}
|
||||||
count = count.coerceAtMost(instance.albumLimit - (previousList?.size ?: 0))
|
count = count.coerceAtMost(it - (previousList?.size ?: 0))
|
||||||
}
|
}
|
||||||
if (count + (previousList?.size ?: 0) >= instance.albumLimit) {
|
if (count + (previousList?.size ?: 0) >= it) {
|
||||||
// Disable buttons to add more images
|
// Disable buttons to add more images
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(addPhotoButtonEnabled = false)
|
currentUiState.copy(addPhotoButtonEnabled = false)
|
||||||
@ -174,6 +188,8 @@ class PostCreationViewModel(
|
|||||||
dataToAdd.add(PhotoData(imageUri = it.uri, size = sizeAndVideoPair.first, video = sizeAndVideoPair.second, imageDescription = it.text?.toString()))
|
dataToAdd.add(PhotoData(imageUri = it.uri, size = sizeAndVideoPair.first, video = sizeAndVideoPair.second, imageDescription = it.text?.toString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return previousList?.plus(dataToAdd)?.toMutableList() ?: mutableListOf()
|
return previousList?.plus(dataToAdd)?.toMutableList() ?: mutableListOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +201,7 @@ class PostCreationViewModel(
|
|||||||
* Returns the size of the file of the Uri, and whether it is a video,
|
* Returns the size of the file of the Uri, and whether it is a video,
|
||||||
* and opens a dialog in case it is too big or in case the file is unsupported.
|
* and opens a dialog in case it is too big or in case the file is unsupported.
|
||||||
*/
|
*/
|
||||||
fun getSizeAndVideoValidate(uri: Uri, editPosition: Int): Pair<Long, Boolean> {
|
private fun getSizeAndVideoValidate(uri: Uri, editPosition: Int): Pair<Long, Boolean> {
|
||||||
val size: Long =
|
val size: Long =
|
||||||
if (uri.scheme =="content") {
|
if (uri.scheme =="content") {
|
||||||
getApplication<PixelDroidApplication>().contentResolver.query(uri, null, null, null, null)
|
getApplication<PixelDroidApplication>().contentResolver.query(uri, null, null, null, null)
|
||||||
@ -195,8 +211,10 @@ class PostCreationViewModel(
|
|||||||
* and display it.
|
* and display it.
|
||||||
*/
|
*/
|
||||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||||
|
if(sizeIndex >= 0) {
|
||||||
cursor.moveToFirst()
|
cursor.moveToFirst()
|
||||||
cursor.getLong(sizeIndex)
|
cursor.getLong(sizeIndex)
|
||||||
|
} else null
|
||||||
} ?: 0
|
} ?: 0
|
||||||
} else {
|
} else {
|
||||||
uri.toFile().length()
|
uri.toFile().length()
|
||||||
@ -213,6 +231,7 @@ class PostCreationViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((!isVideo && sizeInkBytes > instance!!.maxPhotoSize) || (isVideo && sizeInkBytes > instance!!.maxVideoSize)) {
|
if ((!isVideo && sizeInkBytes > instance!!.maxPhotoSize) || (isVideo && sizeInkBytes > instance!!.maxVideoSize)) {
|
||||||
|
//TODO Offer remedy for too big file: re-compress it
|
||||||
val maxSize = if (isVideo) instance.maxVideoSize else instance.maxPhotoSize
|
val maxSize = if (isVideo) instance.maxVideoSize else instance.maxPhotoSize
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
currentUiState.copy(
|
currentUiState.copy(
|
||||||
@ -223,8 +242,6 @@ class PostCreationViewModel(
|
|||||||
return Pair(size, isVideo)
|
return Pair(size, isVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNotEmpty(): Boolean = photoData.value?.isNotEmpty() ?: false
|
|
||||||
|
|
||||||
fun updateDescription(position: Int, description: String) {
|
fun updateDescription(position: Int, description: String) {
|
||||||
photoData.value?.getOrNull(position)?.imageDescription = description
|
photoData.value?.getOrNull(position)?.imageDescription = description
|
||||||
photoData.value = photoData.value
|
photoData.value = photoData.value
|
||||||
@ -234,7 +251,7 @@ class PostCreationViewModel(
|
|||||||
photoData.value?.removeAt(currentPosition)
|
photoData.value?.removeAt(currentPosition)
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
addPhotoButtonEnabled = true
|
addPhotoButtonEnabled = (photoData.value?.size ?: 0) < (uiState.value.maxEntries ?: 0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
photoData.value = photoData.value
|
photoData.value = photoData.value
|
||||||
@ -254,7 +271,7 @@ class PostCreationViewModel(
|
|||||||
videoEncodeProgress = 0
|
videoEncodeProgress = 0
|
||||||
videoEncodeComplete = false
|
videoEncodeComplete = false
|
||||||
|
|
||||||
VideoEditActivity.startEncoding(imageUri, it,
|
VideoEditActivity.startEncoding(imageUri, null, it,
|
||||||
context = getApplication<PixelDroidApplication>(),
|
context = getApplication<PixelDroidApplication>(),
|
||||||
registerNewFFmpegSession = ::registerNewFFmpegSession,
|
registerNewFFmpegSession = ::registerNewFFmpegSession,
|
||||||
trackTempFile = ::trackTempFile,
|
trackTempFile = ::trackTempFile,
|
||||||
@ -442,7 +459,10 @@ class PostCreationViewModel(
|
|||||||
apiHolder.setToCurrentUser(it)
|
apiHolder.setToCurrentUser(it)
|
||||||
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
val inter = api.mediaUpload(description, requestBody.parts[0])
|
val inter: Observable<Attachment> =
|
||||||
|
//TODO validate that image is correct (?) aspect ratio
|
||||||
|
if (uiState.value.storyCreation) api.storyUpload(requestBody.parts[0])
|
||||||
|
else api.mediaUpload(description, requestBody.parts[0])
|
||||||
|
|
||||||
apiHolder.api = null
|
apiHolder.api = null
|
||||||
postSub = inter
|
postSub = inter
|
||||||
@ -451,7 +471,11 @@ class PostCreationViewModel(
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
{ attachment: Attachment ->
|
{ attachment: Attachment ->
|
||||||
data.progress = 0
|
data.progress = 0
|
||||||
data.uploadId = attachment.id!!
|
data.uploadId = if(uiState.value.storyCreation){
|
||||||
|
attachment.media_id!!
|
||||||
|
} else {
|
||||||
|
attachment.id!!
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ e: Throwable ->
|
{ e: Throwable ->
|
||||||
_uiState.update { currentUiState ->
|
_uiState.update { currentUiState ->
|
||||||
@ -507,11 +531,23 @@ class PostCreationViewModel(
|
|||||||
apiHolder.setToCurrentUser(it)
|
apiHolder.setToCurrentUser(it)
|
||||||
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
} ?: apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
|
||||||
|
if(uiState.value.storyCreation){
|
||||||
|
val canReact = if (uiState.value.storyReactions) "1" else "0"
|
||||||
|
val canReply = if (uiState.value.storyReplies) "1" else "0"
|
||||||
|
|
||||||
|
api.storyPublish(
|
||||||
|
media_id = getPhotoData().value!!.firstNotNullOf { it.uploadId },
|
||||||
|
can_react = canReact,
|
||||||
|
can_reply = canReply,
|
||||||
|
duration = uiState.value.storyDuration
|
||||||
|
)
|
||||||
|
} else {
|
||||||
api.postStatus(
|
api.postStatus(
|
||||||
statusText = description,
|
statusText = description,
|
||||||
media_ids = getPhotoData().value!!.mapNotNull { it.uploadId }.toList(),
|
media_ids = getPhotoData().value!!.mapNotNull { it.uploadId }.toList(),
|
||||||
sensitive = nsfw
|
sensitive = nsfw
|
||||||
)
|
)
|
||||||
|
}
|
||||||
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_success),
|
Toast.makeText(getApplication(), getApplication<PixelDroidApplication>().getString(R.string.upload_post_success),
|
||||||
Toast.LENGTH_SHORT).show()
|
Toast.LENGTH_SHORT).show()
|
||||||
val intent = Intent(getApplication(), MainActivity::class.java)
|
val intent = Intent(getApplication(), MainActivity::class.java)
|
||||||
@ -551,10 +587,52 @@ class PostCreationViewModel(
|
|||||||
fun chooseAccount(which: UserDatabaseEntity) {
|
fun chooseAccount(which: UserDatabaseEntity) {
|
||||||
_uiState.update { it.copy(chosenAccount = which) }
|
_uiState.update { it.copy(chosenAccount = which) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun storyMode(storyMode: Boolean) {
|
||||||
|
//TODO check ratio of files in story mode? What is acceptable?
|
||||||
|
|
||||||
|
val newMaxEntries = if (storyMode) 1 else instance?.albumLimit
|
||||||
|
var newUiState = _uiState.value.copy(
|
||||||
|
storyCreation = storyMode,
|
||||||
|
maxEntries = newMaxEntries,
|
||||||
|
addPhotoButtonEnabled = (photoData.value?.size ?: 0) < (newMaxEntries ?: 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Carousel on if in story mode
|
||||||
|
if (storyMode) newUiState = newUiState.copy(isCarousel = true)
|
||||||
|
|
||||||
|
// If switching to story, and there are too many pictures, keep the first and backup the rest
|
||||||
|
if (storyMode && (photoData.value?.size ?: 0) > 1){
|
||||||
|
storyPhotoDataBackup = photoData.value
|
||||||
|
|
||||||
|
photoData.value = photoData.value?.let { mutableListOf(it.firstOrNull()).filterNotNull().toMutableList() }
|
||||||
|
|
||||||
|
//Show message saying extraneous pictures were removed but can be restored
|
||||||
|
newUiState = newUiState.copy(
|
||||||
|
userMessage = getApplication<PixelDroidApplication>().getString(R.string.extraneous_pictures_stories)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Restore if backup not null and first value is unchanged
|
||||||
|
else if (storyPhotoDataBackup != null && storyPhotoDataBackup?.firstOrNull() == photoData.value?.firstOrNull()){
|
||||||
|
photoData.value = storyPhotoDataBackup
|
||||||
|
storyPhotoDataBackup = null
|
||||||
|
}
|
||||||
|
_uiState.update { newUiState }
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostCreationViewModelFactory(val application: Application, val clipdata: ClipData, val instance: InstanceDatabaseEntity, val existingDescription: String?, val existingNSFW: Boolean) : ViewModelProvider.Factory {
|
fun storyDuration(value: Int) {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(storyDuration = value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateStoryReactions(checked: Boolean) { _uiState.update { it.copy(storyReactions = checked) } }
|
||||||
|
|
||||||
|
fun updateStoryReplies(checked: Boolean) { _uiState.update { it.copy(storyReplies = checked) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostCreationViewModelFactory(val application: Application, val clipdata: ClipData, val instance: InstanceDatabaseEntity, val existingDescription: String?, val existingNSFW: Boolean, val storyCreation: Boolean) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return modelClass.getConstructor(Application::class.java, ClipData::class.java, InstanceDatabaseEntity::class.java, String::class.java, Boolean::class.java).newInstance(application, clipdata, instance, existingDescription, existingNSFW)
|
return modelClass.getConstructor(Application::class.java, ClipData::class.java, InstanceDatabaseEntity::class.java, String::class.java, Boolean::class.java, Boolean::class.java).newInstance(application, clipdata, instance, existingDescription, existingNSFW, storyCreation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.FragmentPostSubmissionBinding
|
import org.pixeldroid.app.databinding.FragmentPostSubmissionBinding
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
import org.pixeldroid.app.utils.setSquareImageFromURL
|
import org.pixeldroid.app.utils.setSquareImageFromURL
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
class PostSubmissionFragment : BaseFragment() {
|
class PostSubmissionFragment : BaseFragment() {
|
||||||
@ -34,7 +37,7 @@ class PostSubmissionFragment : BaseFragment() {
|
|||||||
private var user: UserDatabaseEntity? = null
|
private var user: UserDatabaseEntity? = null
|
||||||
private lateinit var instance: InstanceDatabaseEntity
|
private lateinit var instance: InstanceDatabaseEntity
|
||||||
|
|
||||||
private lateinit var binding: FragmentPostSubmissionBinding
|
private var binding: FragmentPostSubmissionBinding by bindingLifecycleAware()
|
||||||
private lateinit var model: PostCreationViewModel
|
private lateinit var model: PostCreationViewModel
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -68,7 +71,8 @@ class PostSubmissionFragment : BaseFragment() {
|
|||||||
requireActivity().intent.clipData!!,
|
requireActivity().intent.clipData!!,
|
||||||
instance,
|
instance,
|
||||||
requireActivity().intent.getStringExtra(PostCreationActivity.PICTURE_DESCRIPTION),
|
requireActivity().intent.getStringExtra(PostCreationActivity.PICTURE_DESCRIPTION),
|
||||||
requireActivity().intent.getBooleanExtra(PostCreationActivity.POST_NSFW, false)
|
requireActivity().intent.getBooleanExtra(PostCreationActivity.POST_NSFW, false),
|
||||||
|
requireActivity().intent.getBooleanExtra(CameraFragment.CAMERA_ACTIVITY_STORY, false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
model = _model
|
model = _model
|
||||||
@ -77,6 +81,18 @@ class PostSubmissionFragment : BaseFragment() {
|
|||||||
binding.nsfwSwitch.isChecked = model.uiState.value.nsfw
|
binding.nsfwSwitch.isChecked = model.uiState.value.nsfw
|
||||||
binding.newPostDescriptionInputField.setText(model.uiState.value.newPostDescriptionText)
|
binding.newPostDescriptionInputField.setText(model.uiState.value.newPostDescriptionText)
|
||||||
|
|
||||||
|
if(model.uiState.value.storyCreation){
|
||||||
|
binding.nsfwSwitch.visibility = View.GONE
|
||||||
|
binding.postTextInputLayout.visibility = View.GONE
|
||||||
|
binding.privateTitle.visibility = View.GONE
|
||||||
|
binding.postPreview.visibility = View.GONE
|
||||||
|
|
||||||
|
binding.storyOptions.visibility = View.VISIBLE
|
||||||
|
binding.storyDurationSlider.value = model.uiState.value.storyDuration.toFloat()
|
||||||
|
binding.storyRepliesSwitch.isChecked = model.uiState.value.storyReplies
|
||||||
|
binding.storyReactionsSwitch.isChecked = model.uiState.value.storyReactions
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
model.uiState.collect { uiState ->
|
model.uiState.collect { uiState ->
|
||||||
@ -114,13 +130,24 @@ class PostSubmissionFragment : BaseFragment() {
|
|||||||
binding.nsfwSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.nsfwSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
model.updateNSFW(isChecked)
|
model.updateNSFW(isChecked)
|
||||||
}
|
}
|
||||||
|
binding.storyRepliesSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
model.updateStoryReplies(isChecked)
|
||||||
|
}
|
||||||
|
binding.storyReactionsSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
model.updateStoryReactions(isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
binding.postTextInputLayout.counterMaxLength = instance.maxStatusChars
|
binding.postTextInputLayout.counterMaxLength = instance.maxStatusChars
|
||||||
|
|
||||||
|
binding.storyDurationSlider.addOnChangeListener { _, value, _ ->
|
||||||
|
// Responds to when slider's value is changed
|
||||||
|
model.storyDuration(value.roundToInt())
|
||||||
|
}
|
||||||
|
|
||||||
setSquareImageFromURL(View(requireActivity()), model.getPhotoData().value?.get(0)?.imageUri.toString(), binding.postPreview)
|
setSquareImageFromURL(View(requireActivity()), model.getPhotoData().value?.get(0)?.imageUri.toString(), binding.postPreview)
|
||||||
|
|
||||||
// Get the description and send the post
|
// Get the description and send the post
|
||||||
binding.postCreationSendButton.setOnClickListener {
|
binding.postSubmissionSendButton.setOnClickListener {
|
||||||
if (validatePost()) model.upload()
|
if (validatePost()) model.upload()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,13 +206,13 @@ class PostSubmissionFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun enableButton(enable: Boolean = true){
|
private fun enableButton(enable: Boolean = true){
|
||||||
binding.postCreationSendButton.isEnabled = enable
|
binding.postSubmissionSendButton.isEnabled = enable
|
||||||
if(enable){
|
if(enable){
|
||||||
binding.postingProgressBar.visibility = View.GONE
|
binding.postingProgressBar.visibility = View.GONE
|
||||||
binding.postCreationSendButton.visibility = View.VISIBLE
|
binding.postSubmissionSendButton.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.postingProgressBar.visibility = View.VISIBLE
|
binding.postingProgressBar.visibility = View.VISIBLE
|
||||||
binding.postCreationSendButton.visibility = View.GONE
|
binding.postSubmissionSendButton.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,47 +5,51 @@ import android.os.Bundle
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import org.pixeldroid.app.MainActivity
|
import org.pixeldroid.app.MainActivity
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.databinding.ActivityCameraBinding
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment.Companion.CAMERA_ACTIVITY
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment.Companion.CAMERA_ACTIVITY_STORY
|
||||||
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
|
|
||||||
class CameraActivity : BaseThemedWithBarActivity() {
|
class CameraActivity : BaseActivity() {
|
||||||
|
private lateinit var binding: ActivityCameraBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_camera)
|
|
||||||
|
binding = ActivityCameraBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setTitle(R.string.add_photo)
|
|
||||||
|
|
||||||
val cameraFragment = CameraFragment()
|
val cameraFragment = CameraFragment()
|
||||||
|
|
||||||
|
val story: Boolean = intent.getBooleanExtra(CAMERA_ACTIVITY_STORY, false)
|
||||||
|
|
||||||
|
if(story) supportActionBar?.setTitle(R.string.add_story)
|
||||||
|
else supportActionBar?.setTitle(R.string.add_photo)
|
||||||
|
|
||||||
|
// If this CameraActivity wasn't started from the shortcut,
|
||||||
|
// tell the fragment it's in an activity (so that it sends back the result instead of
|
||||||
|
// starting a new post creation process)
|
||||||
|
if (intent.action != "android.intent.action.VIEW") {
|
||||||
val arguments = Bundle()
|
val arguments = Bundle()
|
||||||
arguments.putBoolean("CameraActivity", true)
|
arguments.putBoolean(CAMERA_ACTIVITY, true)
|
||||||
|
arguments.putBoolean(CAMERA_ACTIVITY_STORY, story)
|
||||||
cameraFragment.arguments = arguments
|
cameraFragment.arguments = arguments
|
||||||
|
} else {
|
||||||
supportFragmentManager.beginTransaction()
|
|
||||||
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Launch without arguments so that it will open the
|
|
||||||
* [org.pixeldroid.app.postCreation.PostCreationActivity] instead of "returning" to a non-existent
|
|
||||||
* [org.pixeldroid.app.postCreation.PostCreationActivity]
|
|
||||||
*/
|
|
||||||
class CameraActivityShortcut : BaseThemedWithBarActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.activity_camera)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
supportActionBar?.setTitle(R.string.new_post_shortcut_long)
|
supportActionBar?.setTitle(R.string.new_post_shortcut_long)
|
||||||
|
}
|
||||||
val cameraFragment = CameraFragment()
|
|
||||||
|
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Start a new MainActivity when "going back" on this activity
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
// If this CameraActivity wasn't started from the shortcut, behave as usual
|
||||||
|
if (intent.action != "android.intent.action.VIEW") return super.onOptionsItemSelected(item)
|
||||||
|
|
||||||
|
// Else, start a new MainActivity when "going back" on this activity
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
@ -34,13 +34,12 @@ import androidx.core.view.setPadding
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.R
|
|
||||||
import org.pixeldroid.app.databinding.FragmentCameraBinding
|
import org.pixeldroid.app.databinding.FragmentCameraBinding
|
||||||
import org.pixeldroid.app.postCreation.PostCreationActivity
|
import org.pixeldroid.app.postCreation.PostCreationActivity
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -61,7 +60,7 @@ class CameraFragment : BaseFragment() {
|
|||||||
|
|
||||||
private val cameraLifecycleOwner = CameraLifecycleOwner()
|
private val cameraLifecycleOwner = CameraLifecycleOwner()
|
||||||
|
|
||||||
private lateinit var binding: FragmentCameraBinding
|
private var binding: FragmentCameraBinding by bindingLifecycleAware()
|
||||||
|
|
||||||
private var displayId: Int = -1
|
private var displayId: Int = -1
|
||||||
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
|
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
|
||||||
@ -70,6 +69,7 @@ class CameraFragment : BaseFragment() {
|
|||||||
private var camera: Camera? = null
|
private var camera: Camera? = null
|
||||||
|
|
||||||
private var inActivity by Delegates.notNull<Boolean>()
|
private var inActivity by Delegates.notNull<Boolean>()
|
||||||
|
private var addToStory by Delegates.notNull<Boolean>()
|
||||||
|
|
||||||
private var filePermissionDialogLaunched: Boolean = false
|
private var filePermissionDialogLaunched: Boolean = false
|
||||||
|
|
||||||
@ -89,7 +89,8 @@ class CameraFragment : BaseFragment() {
|
|||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
super.onCreateView(inflater, container, savedInstanceState)
|
super.onCreateView(inflater, container, savedInstanceState)
|
||||||
inActivity = arguments?.getBoolean("CameraActivity") ?: false
|
inActivity = arguments?.getBoolean(CAMERA_ACTIVITY) ?: false
|
||||||
|
addToStory = arguments?.getBoolean(CAMERA_ACTIVITY_STORY) ?: false
|
||||||
|
|
||||||
binding = FragmentCameraBinding.inflate(layoutInflater)
|
binding = FragmentCameraBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ class CameraFragment : BaseFragment() {
|
|||||||
thumbnail.setPadding(10)
|
thumbnail.setPadding(10)
|
||||||
|
|
||||||
// Load thumbnail into circular button using Glide
|
// Load thumbnail into circular button using Glide
|
||||||
Glide.with(thumbnail)
|
if(activity?.isDestroyed == false) Glide.with(thumbnail)
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.apply(RequestOptions.circleCropTransform())
|
.apply(RequestOptions.circleCropTransform())
|
||||||
.into(thumbnail)
|
.into(thumbnail)
|
||||||
@ -337,7 +338,8 @@ class CameraFragment : BaseFragment() {
|
|||||||
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
||||||
action = Intent.ACTION_GET_CONTENT
|
action = Intent.ACTION_GET_CONTENT
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
// Don't allow multiple for story
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !addToStory)
|
||||||
uploadImageResultContract.launch(
|
uploadImageResultContract.launch(
|
||||||
Intent.createChooser(this, null)
|
Intent.createChooser(this, null)
|
||||||
)
|
)
|
||||||
@ -464,15 +466,20 @@ class CameraFragment : BaseFragment() {
|
|||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inActivity){
|
if(inActivity && !addToStory){
|
||||||
requireActivity().setResult(Activity.RESULT_OK, intent)
|
requireActivity().setResult(Activity.RESULT_OK, intent)
|
||||||
requireActivity().finish()
|
requireActivity().finish()
|
||||||
} else {
|
} else {
|
||||||
|
if(addToStory){
|
||||||
|
intent.putExtra(CAMERA_ACTIVITY_STORY, addToStory)
|
||||||
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val CAMERA_ACTIVITY = "CameraActivity"
|
||||||
|
const val CAMERA_ACTIVITY_STORY = "CameraActivityStory"
|
||||||
|
|
||||||
private const val TAG = "CameraFragment"
|
private const val TAG = "CameraFragment"
|
||||||
private const val RATIO_4_3_VALUE = 4.0 / 3.0
|
private const val RATIO_4_3_VALUE = 4.0 / 3.0
|
||||||
|
@ -8,6 +8,6 @@ data class CarouselItem constructor(
|
|||||||
val video: Boolean,
|
val video: Boolean,
|
||||||
var encodeProgress: Int?,
|
var encodeProgress: Int?,
|
||||||
var stabilizationFirstPass: Boolean?,
|
var stabilizationFirstPass: Boolean?,
|
||||||
var encodeComplete: Boolean = false,
|
var encodeComplete: Boolean? = null,
|
||||||
var encodeError: Boolean = false,
|
var encodeError: Boolean = false,
|
||||||
)
|
)
|
@ -18,6 +18,9 @@ import androidx.recyclerview.widget.*
|
|||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ImageCarouselBinding
|
import org.pixeldroid.app.databinding.ImageCarouselBinding
|
||||||
import me.relex.circleindicator.CircleIndicator2
|
import me.relex.circleindicator.CircleIndicator2
|
||||||
|
import org.pixeldroid.common.dpToPx
|
||||||
|
import org.pixeldroid.common.getSnapPosition
|
||||||
|
import org.pixeldroid.common.spToPx
|
||||||
|
|
||||||
class ImageCarousel(
|
class ImageCarousel(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -40,7 +43,6 @@ class ImageCarousel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
private lateinit var tvCaption: TextView
|
|
||||||
private var snapHelper: SnapHelper = PagerSnapHelper()
|
private var snapHelper: SnapHelper = PagerSnapHelper()
|
||||||
|
|
||||||
var indicator: CircleIndicator2? = null
|
var indicator: CircleIndicator2? = null
|
||||||
@ -107,7 +109,7 @@ class ImageCarousel(
|
|||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
|
||||||
tvCaption.visibility = if (showCaption) View.VISIBLE else View.GONE
|
binding.tvCaption.visibility = if (showCaption) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
@Dimension(unit = Dimension.PX)
|
@Dimension(unit = Dimension.PX)
|
||||||
@ -115,7 +117,7 @@ class ImageCarousel(
|
|||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
|
||||||
tvCaption.setTextSize(TypedValue.COMPLEX_UNIT_PX, captionTextSize.toFloat())
|
binding.tvCaption.setTextSize(TypedValue.COMPLEX_UNIT_PX, captionTextSize.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
var showIndicator = false
|
var showIndicator = false
|
||||||
@ -245,14 +247,14 @@ class ImageCarousel(
|
|||||||
showNavigationButtons = showNavigationButtons
|
showNavigationButtons = showNavigationButtons
|
||||||
|
|
||||||
binding.editMediaDescriptionLayout.visibility = if(editingMediaDescription) VISIBLE else INVISIBLE
|
binding.editMediaDescriptionLayout.visibility = if(editingMediaDescription) VISIBLE else INVISIBLE
|
||||||
tvCaption.visibility = if(editingMediaDescription) INVISIBLE else VISIBLE
|
binding.tvCaption.visibility = if(editingMediaDescription || !showCaption) INVISIBLE else VISIBLE
|
||||||
} else {
|
} else {
|
||||||
recyclerView.layoutManager = GridLayoutManager(context, 3)
|
recyclerView.layoutManager = GridLayoutManager(context, 3)
|
||||||
binding.btnNext.visibility = GONE
|
binding.btnNext.visibility = GONE
|
||||||
binding.btnPrevious.visibility = GONE
|
binding.btnPrevious.visibility = GONE
|
||||||
|
|
||||||
binding.editMediaDescriptionLayout.visibility = INVISIBLE
|
binding.editMediaDescriptionLayout.visibility = INVISIBLE
|
||||||
tvCaption.visibility = INVISIBLE
|
binding.tvCaption.visibility = INVISIBLE
|
||||||
}
|
}
|
||||||
showIndicator = value
|
showIndicator = value
|
||||||
|
|
||||||
@ -279,8 +281,7 @@ class ImageCarousel(
|
|||||||
updateDescriptionCallback?.invoke(currentPosition, description)
|
updateDescriptionCallback?.invoke(currentPosition, description)
|
||||||
}
|
}
|
||||||
binding.editMediaDescriptionLayout.visibility = if(value) VISIBLE else INVISIBLE
|
binding.editMediaDescriptionLayout.visibility = if(value) VISIBLE else INVISIBLE
|
||||||
tvCaption.visibility = if(value) INVISIBLE else VISIBLE
|
binding.tvCaption.visibility = if(value || !showCaption) INVISIBLE else VISIBLE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -289,10 +290,10 @@ class ImageCarousel(
|
|||||||
set(value) {
|
set(value) {
|
||||||
if(!value.isNullOrEmpty()) {
|
if(!value.isNullOrEmpty()) {
|
||||||
field = value
|
field = value
|
||||||
tvCaption.text = value
|
binding.tvCaption.text = value
|
||||||
} else {
|
} else {
|
||||||
field = null
|
field = null
|
||||||
tvCaption.text = context.getText(R.string.no_media_description)
|
binding.tvCaption.text = context.getText(R.string.no_media_description)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -317,12 +318,11 @@ class ImageCarousel(
|
|||||||
binding = ImageCarouselBinding.inflate(LayoutInflater.from(context),this, true)
|
binding = ImageCarouselBinding.inflate(LayoutInflater.from(context),this, true)
|
||||||
|
|
||||||
recyclerView = binding.recyclerView
|
recyclerView = binding.recyclerView
|
||||||
tvCaption = binding.tvCaption
|
|
||||||
|
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
|
|
||||||
// For marquee effect
|
// For marquee effect
|
||||||
tvCaption.isSelected = true
|
binding.tvCaption.isSelected = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -441,7 +441,7 @@ class ImageCarousel(
|
|||||||
caption.apply {
|
caption.apply {
|
||||||
if(layoutCarousel){
|
if(layoutCarousel){
|
||||||
binding.editMediaDescriptionLayout.visibility = INVISIBLE
|
binding.editMediaDescriptionLayout.visibility = INVISIBLE
|
||||||
tvCaption.visibility = VISIBLE
|
showCaption = true
|
||||||
}
|
}
|
||||||
currentDescription = this
|
currentDescription = this
|
||||||
}
|
}
|
||||||
@ -472,7 +472,7 @@ class ImageCarousel(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
tvCaption.setOnClickListener {
|
binding.tvCaption.setOnClickListener {
|
||||||
editingMediaDescription = true
|
editingMediaDescription = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,7 +562,7 @@ class ImageCarousel(
|
|||||||
binding.encodeInfoText.setText(R.string.encode_error)
|
binding.encodeInfoText.setText(R.string.encode_error)
|
||||||
binding.encodeInfoText.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context, R.drawable.error),
|
binding.encodeInfoText.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context, R.drawable.error),
|
||||||
null, null, null)
|
null, null, null)
|
||||||
} else if(it.encodeComplete){
|
} else if(it.encodeComplete == true){
|
||||||
binding.encodeInfoCard.visibility = VISIBLE
|
binding.encodeInfoCard.visibility = VISIBLE
|
||||||
binding.encodeProgress.visibility = GONE
|
binding.encodeProgress.visibility = GONE
|
||||||
binding.encodeInfoText.setText(R.string.encode_success)
|
binding.encodeInfoText.setText(R.string.encode_success)
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
package org.pixeldroid.app.postCreation.carousel
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.DisplayMetrics
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.recyclerview.widget.SnapHelper
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method converts device specific pixels to density independent pixels.
|
|
||||||
*/
|
|
||||||
fun Int.pxToDp(context: Context): Int {
|
|
||||||
return (this / (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method converts dp unit to equivalent pixels, depending on device density.
|
|
||||||
*/
|
|
||||||
fun Int.dpToPx(context: Context): Int {
|
|
||||||
return TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
this.toFloat(),
|
|
||||||
context.resources.displayMetrics
|
|
||||||
).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method converts sp unit to equivalent pixels, depending on device density.
|
|
||||||
*/
|
|
||||||
fun Int.spToPx(context: Context): Int {
|
|
||||||
return TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_SP,
|
|
||||||
this.toFloat(),
|
|
||||||
context.resources.displayMetrics
|
|
||||||
).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current snap item position of a recyclerView.
|
|
||||||
*
|
|
||||||
* @param layoutManager Target recyclerView
|
|
||||||
* @return Position of the item or RecyclerView.NO_POSITION (-1)
|
|
||||||
*/
|
|
||||||
fun SnapHelper.getSnapPosition(layoutManager: RecyclerView.LayoutManager?): Int {
|
|
||||||
if (layoutManager == null) {
|
|
||||||
return RecyclerView.NO_POSITION
|
|
||||||
}
|
|
||||||
val snapView: View = this.findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION
|
|
||||||
return layoutManager.getPosition(snapView)
|
|
||||||
}
|
|
@ -1,12 +1,14 @@
|
|||||||
package org.pixeldroid.app.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import org.pixeldroid.app.databinding.ActivityAlbumBinding
|
import org.pixeldroid.app.databinding.ActivityAlbumBinding
|
||||||
import org.pixeldroid.app.utils.BaseActivity
|
|
||||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||||
|
|
||||||
class AlbumActivity : BaseActivity() {
|
|
||||||
|
class AlbumActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val binding = ActivityAlbumBinding.inflate(layoutInflater)
|
val binding = ActivityAlbumBinding.inflate(layoutInflater)
|
||||||
@ -36,4 +38,17 @@ class AlbumActivity : BaseActivity() {
|
|||||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||||
supportActionBar?.setBackgroundDrawable(null)
|
supportActionBar?.setBackgroundDrawable(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
// Handle up arrow manually,
|
||||||
|
// since "up" isn't defined for this activity
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ import android.view.View
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.text.toSpanned
|
import androidx.core.text.toSpanned
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
||||||
@ -106,7 +107,7 @@ fun parseHTMLText(
|
|||||||
override fun onClick(widget: View) {
|
override fun onClick(widget: View) {
|
||||||
|
|
||||||
// Retrieve the account for the given profile
|
// Retrieve the account for the given profile
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launch {
|
||||||
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
openAccountFromId(accountId, api, context)
|
openAccountFromId(accountId, api, context)
|
||||||
}
|
}
|
||||||
@ -130,7 +131,7 @@ fun parseHTMLText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Boolean, context: Context) {
|
fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Boolean) {
|
||||||
val now = Date.from(Instant.now()).time
|
val now = Date.from(Instant.now()).time
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -140,7 +141,7 @@ fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Bool
|
|||||||
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
||||||
android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||||
|
|
||||||
textView.text = if(absoluteTime) context.getString(R.string.posted_on).format(date)
|
textView.text = if(absoluteTime) textView.context.getString(R.string.posted_on).format(date)
|
||||||
else formattedDate
|
else formattedDate
|
||||||
|
|
||||||
} catch (e: ParseException) {
|
} catch (e: ParseException) {
|
||||||
|
@ -14,9 +14,9 @@ import androidx.media2.common.MediaMetadata
|
|||||||
import androidx.media2.common.UriMediaItem
|
import androidx.media2.common.UriMediaItem
|
||||||
import androidx.media2.player.MediaPlayer
|
import androidx.media2.player.MediaPlayer
|
||||||
import org.pixeldroid.app.databinding.ActivityMediaviewerBinding
|
import org.pixeldroid.app.databinding.ActivityMediaviewerBinding
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
class MediaViewerActivity : BaseThemedWithoutBarActivity() {
|
class MediaViewerActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var mediaPlayer: MediaPlayer
|
private lateinit var mediaPlayer: MediaPlayer
|
||||||
private lateinit var binding: ActivityMediaviewerBinding
|
private lateinit var binding: ActivityMediaviewerBinding
|
||||||
|
@ -96,11 +96,12 @@ class NestedScrollableHost(context: Context, attrs: AttributeSet? = null) :
|
|||||||
return super.onSingleTapConfirmed(e)
|
return super.onSingleTapConfirmed(e)
|
||||||
}
|
}
|
||||||
override fun onScroll(
|
override fun onScroll(
|
||||||
e1: MotionEvent,
|
e1: MotionEvent?,
|
||||||
e2: MotionEvent,
|
e2: MotionEvent,
|
||||||
distanceX: Float,
|
distanceX: Float,
|
||||||
distanceY: Float
|
distanceY: Float
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
if (e1 == null) return false
|
||||||
val orientation = parentViewPager?.orientation ?: return true
|
val orientation = parentViewPager?.orientation ?: return true
|
||||||
|
|
||||||
val dx = e2.x - e1.x
|
val dx = e2.x - e1.x
|
||||||
|
@ -5,13 +5,15 @@ import android.util.Log
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
|
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityPostBinding
|
import org.pixeldroid.app.databinding.ActivityPostBinding
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment.Companion.COMMENT_DOMAIN
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment.Companion.COMMENT_DOMAIN
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment.Companion.COMMENT_STATUS_ID
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.comments.CommentFragment.Companion.COMMENT_STATUS_ID
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Status
|
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_COMMENT_TAG
|
||||||
@ -19,7 +21,7 @@ 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.api.objects.Status.Companion.VIEW_COMMENTS_TAG
|
||||||
import org.pixeldroid.app.utils.displayDimensionsInPx
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
|
|
||||||
class PostActivity : BaseThemedWithBarActivity() {
|
class PostActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivityPostBinding
|
private lateinit var binding: ActivityPostBinding
|
||||||
|
|
||||||
private var commentFragment = CommentFragment()
|
private var commentFragment = CommentFragment()
|
||||||
@ -30,7 +32,7 @@ class PostActivity : BaseThemedWithBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityPostBinding.inflate(layoutInflater)
|
binding = ActivityPostBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
status = intent.getSerializableExtra(POST_TAG) as Status
|
status = intent.getSerializableExtra(POST_TAG) as Status
|
||||||
@ -43,7 +45,10 @@ class PostActivity : BaseThemedWithBarActivity() {
|
|||||||
|
|
||||||
val holder = StatusViewHolder(binding.postFragmentSingle)
|
val holder = StatusViewHolder(binding.postFragmentSingle)
|
||||||
|
|
||||||
holder.bind(status, apiHolder, db, lifecycleScope, displayDimensionsInPx(), isActivity = true)
|
holder.bind(
|
||||||
|
status, apiHolder, db, lifecycleScope, displayDimensionsInPx(),
|
||||||
|
requestPermissionDownloadPic, isActivity = true
|
||||||
|
)
|
||||||
|
|
||||||
activateCommenter()
|
activateCommenter()
|
||||||
initCommentsFragment(domain = user?.instance_uri.orEmpty())
|
initCommentsFragment(domain = user?.instance_uri.orEmpty())
|
||||||
@ -60,6 +65,17 @@ class PostActivity : BaseThemedWithBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val requestPermissionDownloadPic =
|
||||||
|
registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { isGranted: Boolean ->
|
||||||
|
if (!isGranted) {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setMessage(R.string.write_permission_download_pic)
|
||||||
|
.setNegativeButton(android.R.string.ok) { _, _ -> }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
private fun activateCommenter() {
|
private fun activateCommenter() {
|
||||||
//Activate commenter
|
//Activate commenter
|
||||||
binding.submitComment.setOnClickListener {
|
binding.submitComment.setOnClickListener {
|
||||||
|
@ -5,10 +5,10 @@ import android.view.View
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityReportBinding
|
import org.pixeldroid.app.databinding.ActivityReportBinding
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
|
||||||
class ReportActivity : BaseThemedWithBarActivity() {
|
class ReportActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityReportBinding
|
private lateinit var binding: ActivityReportBinding
|
||||||
|
|
||||||
@ -16,9 +16,9 @@ class ReportActivity : BaseThemedWithBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityReportBinding.inflate(layoutInflater)
|
binding = ActivityReportBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setTitle(R.string.report)
|
|
||||||
|
|
||||||
val status = intent.getSerializableExtra(Status.POST_TAG) as Status?
|
val status = intent.getSerializableExtra(Status.POST_TAG) as Status?
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package org.pixeldroid.app.posts
|
package org.pixeldroid.app.posts
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager.PERMISSION_DENIED
|
||||||
|
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.AnimatedVectorDrawable
|
import android.graphics.drawable.AnimatedVectorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -17,6 +19,7 @@ import android.view.Menu
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
@ -36,10 +39,6 @@ import com.davemorrissey.labs.subscaleview.ImageSource
|
|||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.karumi.dexter.Dexter
|
|
||||||
import com.karumi.dexter.listener.PermissionDeniedResponse
|
|
||||||
import com.karumi.dexter.listener.PermissionGrantedResponse
|
|
||||||
import com.karumi.dexter.listener.single.BasePermissionListener
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okio.BufferedSink
|
import okio.BufferedSink
|
||||||
@ -75,7 +74,11 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
|
|
||||||
private var status: Status? = null
|
private var status: Status? = null
|
||||||
|
|
||||||
fun bind(status: Status?, pixelfedAPI: PixelfedAPIHolder, 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>,
|
||||||
|
requestPermissionDownloadPic: ActivityResultLauncher<String>, isActivity: Boolean = false
|
||||||
|
) {
|
||||||
|
|
||||||
this.itemView.visibility = View.VISIBLE
|
this.itemView.visibility = View.VISIBLE
|
||||||
this.status = status
|
this.status = status
|
||||||
@ -104,7 +107,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
|
|
||||||
setupPost(picRequest, user.instance_uri, isActivity)
|
setupPost(picRequest, user.instance_uri, isActivity)
|
||||||
|
|
||||||
activateButtons(pixelfedAPI, db, lifecycleScope, isActivity)
|
activateButtons(pixelfedAPI, db, lifecycleScope, isActivity, requestPermissionDownloadPic)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,8 +142,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
setTextViewFromISO8601(
|
setTextViewFromISO8601(
|
||||||
status?.created_at!!,
|
status?.created_at!!,
|
||||||
binding.postDate,
|
binding.postDate,
|
||||||
isActivity,
|
isActivity
|
||||||
binding.root.context
|
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
|
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
|
||||||
@ -233,6 +235,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
db: AppDatabase,
|
db: AppDatabase,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
isActivity: Boolean,
|
isActivity: Boolean,
|
||||||
|
requestPermissionDownloadPic: ActivityResultLauncher<String>,
|
||||||
){
|
){
|
||||||
//Set the special HTML text
|
//Set the special HTML text
|
||||||
setDescription(apiHolder, lifecycleScope)
|
setDescription(apiHolder, lifecycleScope)
|
||||||
@ -262,7 +265,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
|
|
||||||
showComments(lifecycleScope, isActivity)
|
showComments(lifecycleScope, isActivity)
|
||||||
|
|
||||||
activateMoreButton(apiHolder, db, lifecycleScope)
|
activateMoreButton(apiHolder, db, lifecycleScope, requestPermissionDownloadPic)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateReblogger(
|
private fun activateReblogger(
|
||||||
@ -364,7 +367,12 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateMoreButton(apiHolder: PixelfedAPIHolder, db: AppDatabase, lifecycleScope: LifecycleCoroutineScope){
|
private fun activateMoreButton(
|
||||||
|
apiHolder: PixelfedAPIHolder,
|
||||||
|
db: AppDatabase,
|
||||||
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
|
requestPermissionDownloadPic: ActivityResultLauncher<String>
|
||||||
|
){
|
||||||
var bookmarked: Boolean? = null
|
var bookmarked: Boolean? = null
|
||||||
binding.statusMore.setOnClickListener {
|
binding.statusMore.setOnClickListener {
|
||||||
PopupMenu(it.context, it).apply {
|
PopupMenu(it.context, it).apply {
|
||||||
@ -402,18 +410,11 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.post_more_menu_save_to_gallery -> {
|
R.id.post_more_menu_save_to_gallery -> {
|
||||||
Dexter.withContext(binding.root.context)
|
// Check permissions on old Android versions: on new versions it is not
|
||||||
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
// needed when storing a file.
|
||||||
.withListener(object : BasePermissionListener() {
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(binding.root.context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_DENIED) {
|
||||||
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
|
requestPermissionDownloadPic.launch(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
Toast.makeText(
|
} else {
|
||||||
binding.root.context,
|
|
||||||
binding.root.context.getString(R.string.write_permission_download_pic),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
|
|
||||||
status?.downloadImage(
|
status?.downloadImage(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
status?.media_attachments?.getOrNull(binding.postPager.currentItem)?.url
|
status?.media_attachments?.getOrNull(binding.postPager.currentItem)?.url
|
||||||
@ -421,22 +422,10 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
binding.root
|
binding.root
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}).check()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.post_more_menu_share_picture -> {
|
|
||||||
Dexter.withContext(binding.root.context)
|
|
||||||
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
.withListener(object : BasePermissionListener() {
|
|
||||||
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
|
|
||||||
Toast.makeText(
|
|
||||||
binding.root.context,
|
|
||||||
binding.root.context.getString(R.string.write_permission_share_pic),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
|
R.id.post_more_menu_share_picture -> {
|
||||||
status?.downloadImage(
|
status?.downloadImage(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
status?.media_attachments?.getOrNull(binding.postPager.currentItem)?.url
|
status?.media_attachments?.getOrNull(binding.postPager.currentItem)?.url
|
||||||
@ -444,8 +433,6 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
binding.root,
|
binding.root,
|
||||||
share = true,
|
share = true,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}).check()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.post_more_menu_delete -> {
|
R.id.post_more_menu_delete -> {
|
||||||
|
@ -6,13 +6,16 @@ import android.widget.ProgressBar
|
|||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
|
import androidx.paging.CombinedLoadStates
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.LoadStateAdapter
|
import androidx.paging.LoadStateAdapter
|
||||||
import androidx.paging.PagingDataAdapter
|
import androidx.paging.PagingDataAdapter
|
||||||
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -20,6 +23,7 @@ import org.pixeldroid.app.R
|
|||||||
import org.pixeldroid.app.databinding.ErrorLayoutBinding
|
import org.pixeldroid.app.databinding.ErrorLayoutBinding
|
||||||
import org.pixeldroid.app.databinding.LoadStateFooterViewItemBinding
|
import org.pixeldroid.app.databinding.LoadStateFooterViewItemBinding
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.FeedViewModel
|
||||||
|
import org.pixeldroid.app.stories.StoriesAdapter
|
||||||
import org.pixeldroid.app.utils.api.objects.FeedContent
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
import org.pixeldroid.app.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
@ -48,14 +52,28 @@ private fun showError(
|
|||||||
internal fun <T: Any> initAdapter(
|
internal fun <T: Any> initAdapter(
|
||||||
progressBar: ProgressBar, swipeRefreshLayout: SwipeRefreshLayout,
|
progressBar: ProgressBar, swipeRefreshLayout: SwipeRefreshLayout,
|
||||||
recyclerView: RecyclerView, motionLayout: MotionLayout, errorLayout: ErrorLayoutBinding,
|
recyclerView: RecyclerView, motionLayout: MotionLayout, errorLayout: ErrorLayoutBinding,
|
||||||
adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>) {
|
adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>,
|
||||||
|
header: StoriesAdapter? = null
|
||||||
|
) {
|
||||||
|
|
||||||
recyclerView.adapter = adapter.withLoadStateFooter(
|
|
||||||
footer = ReposLoadStateAdapter { adapter.retry() }
|
val footer = ReposLoadStateAdapter { adapter.retry() }
|
||||||
|
|
||||||
|
adapter.addLoadStateListener { loadStates: CombinedLoadStates ->
|
||||||
|
footer.loadState = loadStates.append
|
||||||
|
}
|
||||||
|
|
||||||
|
recyclerView.adapter = ConcatAdapter(
|
||||||
|
*listOfNotNull(
|
||||||
|
header, // need to filter it if null
|
||||||
|
adapter,
|
||||||
|
footer
|
||||||
|
).toTypedArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
swipeRefreshLayout.setOnRefreshListener {
|
swipeRefreshLayout.setOnRefreshListener {
|
||||||
adapter.refresh()
|
adapter.refresh()
|
||||||
|
header?.refreshStories()
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.addLoadStateListener { loadState ->
|
adapter.addLoadStateListener { loadState ->
|
||||||
@ -80,6 +98,11 @@ internal fun <T: Any> initAdapter(
|
|||||||
?: loadState.append as? LoadState.Error
|
?: loadState.append as? LoadState.Error
|
||||||
?: loadState.prepend as? LoadState.Error
|
?: loadState.prepend as? LoadState.Error
|
||||||
?: loadState.refresh as? LoadState.Error
|
?: loadState.refresh as? LoadState.Error
|
||||||
|
|
||||||
|
if(errorState?.error is CancellationException){
|
||||||
|
return@addLoadStateListener
|
||||||
|
}
|
||||||
|
|
||||||
errorState?.let {
|
errorState?.let {
|
||||||
val error: String = (it.error as? HttpException)?.response()?.errorBody()?.string()?.ifEmpty { null }?.let { s ->
|
val error: String = (it.error as? HttpException)?.response()?.errorBody()?.string()?.ifEmpty { null }?.let { s ->
|
||||||
try {
|
try {
|
||||||
@ -143,6 +166,8 @@ class ReposLoadStateAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [RecyclerView.ViewHolder] that is shown at the end of the feed to indicate loading or errors
|
* [RecyclerView.ViewHolder] that is shown at the end of the feed to indicate loading or errors
|
||||||
* in the loading of appending values.
|
* in the loading of appending values.
|
||||||
|
@ -18,8 +18,10 @@ import kotlinx.coroutines.flow.distinctUntilChangedBy
|
|||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
import org.pixeldroid.app.databinding.FragmentFeedBinding
|
||||||
import org.pixeldroid.app.posts.feeds.initAdapter
|
import org.pixeldroid.app.posts.feeds.initAdapter
|
||||||
|
import org.pixeldroid.app.stories.StoriesAdapter
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
import org.pixeldroid.app.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||||
import org.pixeldroid.app.utils.limitedLengthSmoothScrollToPosition
|
import org.pixeldroid.app.utils.limitedLengthSmoothScrollToPosition
|
||||||
@ -31,8 +33,9 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
|
|
||||||
internal lateinit var viewModel: FeedViewModel<T>
|
internal lateinit var viewModel: FeedViewModel<T>
|
||||||
internal lateinit var adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>
|
internal lateinit var adapter: PagingDataAdapter<T, RecyclerView.ViewHolder>
|
||||||
|
internal var headerAdapter: StoriesAdapter? = null
|
||||||
|
|
||||||
private lateinit var binding: FragmentFeedBinding
|
private var binding: FragmentFeedBinding by bindingLifecycleAware()
|
||||||
|
|
||||||
|
|
||||||
private var job: Job? = null
|
private var job: Job? = null
|
||||||
@ -49,6 +52,7 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO rename function to something that makes sense
|
||||||
internal fun initSearch() {
|
internal fun initSearch() {
|
||||||
// Scroll to top when the list is refreshed from network.
|
// Scroll to top when the list is refreshed from network.
|
||||||
lifecycleScope.launchWhenStarted {
|
lifecycleScope.launchWhenStarted {
|
||||||
@ -73,7 +77,9 @@ open class CachedFeedFragment<T: FeedContentDatabase> : BaseFragment() {
|
|||||||
binding = FragmentFeedBinding.inflate(layoutInflater)
|
binding = FragmentFeedBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
|
||||||
binding.list, binding.motionLayout, binding.errorLayout, adapter)
|
binding.list, binding.motionLayout, binding.errorLayout, adapter,
|
||||||
|
headerAdapter
|
||||||
|
)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
@ -221,8 +221,7 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
setTextViewFromISO8601(
|
setTextViewFromISO8601(
|
||||||
it,
|
it,
|
||||||
notificationTime,
|
notificationTime,
|
||||||
false,
|
false
|
||||||
itemView.context
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class HomeFeedRemoteMediator @Inject constructor(
|
|||||||
HomeStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
HomeStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val endOfPaginationReached = apiResponse.isEmpty()
|
val endOfPaginationReached = apiResponse.isEmpty() || maxId == apiResponse.sortedBy { it.created_at }.last().id
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
// Clear table in the database
|
// Clear table in the database
|
||||||
|
@ -11,14 +11,14 @@ import androidx.paging.PagingDataAdapter
|
|||||||
import androidx.paging.RemoteMediator
|
import androidx.paging.RemoteMediator
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.pixeldroid.app.R
|
|
||||||
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
|
||||||
import org.pixeldroid.app.posts.StatusViewHolder
|
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.CachedFeedFragment
|
||||||
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
|
||||||
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
|
||||||
|
import org.pixeldroid.app.stories.StoriesAdapter
|
||||||
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
|
||||||
import org.pixeldroid.app.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
|
||||||
import org.pixeldroid.app.utils.displayDimensionsInPx
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
@ -38,14 +38,18 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
home = requireArguments().getBoolean("home")
|
||||||
|
|
||||||
home = requireArguments().get("home") as Boolean
|
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (home){
|
if (home){
|
||||||
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
||||||
dao = db.homePostDao() as FeedContentDao<T>
|
dao = db.homePostDao() as FeedContentDao<T>
|
||||||
|
headerAdapter = StoriesAdapter(lifecycleScope, apiHolder)
|
||||||
|
headerAdapter?.showStories = false
|
||||||
|
|
||||||
|
headerAdapter?.refreshStories()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mediator = PublicFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
mediator = PublicFeedRemoteMediator(apiHolder, db) as RemoteMediator<Int, T>
|
||||||
@ -55,7 +59,7 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
|
|
||||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||||
@ -70,6 +74,7 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<T, RecyclerView.ViewHolder>(
|
inner class PostsAdapter(private val displayDimensionsInPx: Pair<Int, Int>) : PagingDataAdapter<T, RecyclerView.ViewHolder>(
|
||||||
object : DiffUtil.ItemCallback<T>() {
|
object : DiffUtil.ItemCallback<T>() {
|
||||||
override fun areItemsTheSame (oldItem: T, newItem: T): Boolean = oldItem.id == newItem.id
|
override fun areItemsTheSame (oldItem: T, newItem: T): Boolean = oldItem.id == newItem.id
|
||||||
@ -81,14 +86,18 @@ class PostFeedFragment<T: FeedContentDatabase>: CachedFeedFragment<T>() {
|
|||||||
return StatusViewHolder.create(parent)
|
return StatusViewHolder.create(parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
|
||||||
return R.layout.post_fragment
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
holder.itemView.visibility = View.VISIBLE
|
||||||
|
holder.itemView.layoutParams =
|
||||||
|
RecyclerView.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
val uiModel = getItem(position) as Status?
|
val uiModel = getItem(position) as Status?
|
||||||
uiModel?.let {
|
uiModel?.let {
|
||||||
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
|
(holder as StatusViewHolder).bind(
|
||||||
|
it, apiHolder, db, lifecycleScope, displayDimensionsInPx, requestPermissionDownloadPic
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ class PublicFeedRemoteMediator @Inject constructor(
|
|||||||
val dbObjects = apiResponse.map{
|
val dbObjects = apiResponse.map{
|
||||||
PublicFeedStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
PublicFeedStatusDatabaseEntity(user.user_id, user.instance_uri, it)
|
||||||
}
|
}
|
||||||
val endOfPaginationReached = apiResponse.isEmpty()
|
val endOfPaginationReached = apiResponse.isEmpty() || maxId == apiResponse.sortedBy { it.created_at }.last().id
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
// Clear table in the database
|
// Clear table in the database
|
||||||
|
@ -61,8 +61,10 @@ open class UncachedFeedFragment<T: FeedContent> : BaseFragment() {
|
|||||||
|
|
||||||
binding = FragmentFeedBinding.inflate(layoutInflater)
|
binding = FragmentFeedBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
initAdapter(binding.progressBar, binding.swipeRefreshLayout, binding.list,
|
initAdapter(
|
||||||
binding.motionLayout, binding.errorLayout, adapter)
|
binding.progressBar, binding.swipeRefreshLayout, binding.list,
|
||||||
|
binding.motionLayout, binding.errorLayout, adapter
|
||||||
|
)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,9 @@ class UncachedPostsFragment : UncachedFeedFragment<Status>() {
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
getItem(position)?.let {
|
getItem(position)?.let {
|
||||||
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
|
(holder as StatusViewHolder).bind(
|
||||||
|
it, apiHolder, db, lifecycleScope, displayDimensionsInPx, requestPermissionDownloadPic
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,23 @@ package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.databinding.ActivityFollowersBinding
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
||||||
|
|
||||||
|
|
||||||
class HashTagActivity : BaseThemedWithBarActivity() {
|
class HashTagActivity : BaseActivity() {
|
||||||
private var tagFragment = UncachedPostsFragment()
|
private var tagFragment = UncachedPostsFragment()
|
||||||
|
private lateinit var binding: ActivityFollowersBinding
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_followers)
|
binding = ActivityFollowersBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
// Get hashtag tag
|
// Get hashtag tag
|
||||||
|
@ -13,12 +13,12 @@ import org.pixeldroid.app.R
|
|||||||
import org.pixeldroid.app.databinding.ActivityCollectionBinding
|
import org.pixeldroid.app.databinding.ActivityCollectionBinding
|
||||||
import org.pixeldroid.app.profile.ProfileFeedFragment.Companion.COLLECTION
|
import org.pixeldroid.app.profile.ProfileFeedFragment.Companion.COLLECTION
|
||||||
import org.pixeldroid.app.profile.ProfileFeedFragment.Companion.COLLECTION_ID
|
import org.pixeldroid.app.profile.ProfileFeedFragment.Companion.COLLECTION_ID
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Collection
|
import org.pixeldroid.app.utils.api.objects.Collection
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
class CollectionActivity : BaseThemedWithBarActivity() {
|
class CollectionActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivityCollectionBinding
|
private lateinit var binding: ActivityCollectionBinding
|
||||||
|
|
||||||
private lateinit var collection: Collection
|
private lateinit var collection: Collection
|
||||||
@ -37,6 +37,7 @@ class CollectionActivity : BaseThemedWithBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityCollectionBinding.inflate(layoutInflater)
|
binding = ActivityCollectionBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import android.os.Bundle
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
@ -19,10 +20,10 @@ import com.google.android.material.snackbar.Snackbar
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityEditProfileBinding
|
import org.pixeldroid.app.databinding.ActivityEditProfileBinding
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.openUrl
|
import org.pixeldroid.app.utils.openUrl
|
||||||
|
|
||||||
class EditProfileActivity : BaseThemedWithBarActivity() {
|
class EditProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var model: EditProfileViewModel
|
private lateinit var model: EditProfileViewModel
|
||||||
private lateinit var binding: ActivityEditProfileBinding
|
private lateinit var binding: ActivityEditProfileBinding
|
||||||
@ -31,12 +32,29 @@ class EditProfileActivity : BaseThemedWithBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityEditProfileBinding.inflate(layoutInflater)
|
binding = ActivityEditProfileBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setTitle(R.string.edit_profile)
|
|
||||||
|
|
||||||
val _model: EditProfileViewModel by viewModels { EditProfileViewModelFactory(application) }
|
val _model: EditProfileViewModel by viewModels { EditProfileViewModelFactory(application) }
|
||||||
model = _model
|
model = _model
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
// Handle the back button event
|
||||||
|
if(model.madeChanges()){
|
||||||
|
MaterialAlertDialogBuilder(binding.root.context).apply {
|
||||||
|
setMessage(getString(R.string.profile_save_changes))
|
||||||
|
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
this@addCallback.isEnabled = false
|
||||||
|
super.onBackPressedDispatcher.onBackPressed()
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
} else {
|
||||||
|
this.isEnabled = false
|
||||||
|
super.onBackPressedDispatcher.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
model.uiState.collect { uiState ->
|
model.uiState.collect { uiState ->
|
||||||
@ -132,18 +150,6 @@ class EditProfileActivity : BaseThemedWithBarActivity() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if(model.madeChanges()){
|
|
||||||
MaterialAlertDialogBuilder(binding.root.context).apply {
|
|
||||||
setMessage(getString(R.string.profile_save_changes))
|
|
||||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ -> super.onBackPressed()}
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
else super.onBackPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId){
|
when (item.itemId){
|
||||||
R.id.action_apply -> {
|
R.id.action_apply -> {
|
||||||
|
@ -2,20 +2,25 @@ package org.pixeldroid.app.profile
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.databinding.ActivityFollowersBinding
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountListFragment
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Account
|
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_ID_TAG
|
||||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.ACCOUNT_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.api.objects.Account.Companion.FOLLOWERS_TAG
|
||||||
|
|
||||||
|
|
||||||
class FollowsActivity : BaseThemedWithBarActivity() {
|
class FollowsActivity : BaseActivity() {
|
||||||
private var followsFragment = AccountListFragment()
|
private var followsFragment = AccountListFragment()
|
||||||
|
private lateinit var binding: ActivityFollowersBinding
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_followers)
|
binding = ActivityFollowersBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.launch
|
|||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
import org.pixeldroid.app.databinding.ActivityProfileBinding
|
||||||
import org.pixeldroid.app.posts.parseHTMLText
|
import org.pixeldroid.app.posts.parseHTMLText
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
@ -25,7 +25,7 @@ import org.pixeldroid.app.utils.setProfileImageFromURL
|
|||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProfileActivity : BaseThemedWithBarActivity() {
|
class ProfileActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var domain : String
|
private lateinit var domain : String
|
||||||
private lateinit var accountId : String
|
private lateinit var accountId : String
|
||||||
@ -36,7 +36,10 @@ class ProfileActivity : BaseThemedWithBarActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityProfileBinding.inflate(layoutInflater)
|
binding = ActivityProfileBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
@ -178,8 +178,10 @@ class ProfileFeedFragment : UncachedFeedFragment<FeedContent>() {
|
|||||||
deleteFromCollection
|
deleteFromCollection
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(holder as StatusViewHolder).bind(it as Status, apiHolder, db,
|
(holder as StatusViewHolder).bind(
|
||||||
lifecycleScope, requireContext().displayDimensionsInPx())
|
it as Status, apiHolder, db, lifecycleScope,
|
||||||
|
requireContext().displayDimensionsInPx(), requestPermissionDownloadPic
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,17 +9,21 @@ import androidx.viewpager2.widget.ViewPager2
|
|||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.databinding.ActivitySearchBinding
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
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.SearchAccountFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
|
||||||
class SearchActivity : BaseThemedWithBarActivity() {
|
class SearchActivity : BaseActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_search)
|
val binding = ActivitySearchBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
var query = ""
|
var query = ""
|
||||||
|
@ -11,22 +11,24 @@ import androidx.core.content.ContextCompat
|
|||||||
import org.pixeldroid.app.databinding.FragmentSearchBinding
|
import org.pixeldroid.app.databinding.FragmentSearchBinding
|
||||||
import org.pixeldroid.app.searchDiscover.TrendingActivity.Companion.TRENDING_TAG
|
import org.pixeldroid.app.searchDiscover.TrendingActivity.Companion.TRENDING_TAG
|
||||||
import org.pixeldroid.app.searchDiscover.TrendingActivity.Companion.TrendingType
|
import org.pixeldroid.app.searchDiscover.TrendingActivity.Companion.TrendingType
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
|
||||||
import org.pixeldroid.app.utils.BaseFragment
|
import org.pixeldroid.app.utils.BaseFragment
|
||||||
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.bindingLifecycleAware
|
import org.pixeldroid.app.utils.bindingLifecycleAware
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This fragment lets you search and use Pixelfed's Discover feature
|
* This fragment lets you search and use Pixelfed's Discover feature
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SearchDiscoverFragment : BaseFragment() {
|
class SearchDiscoverFragment : BaseFragment() {
|
||||||
|
|
||||||
private lateinit var api: PixelfedAPI
|
private lateinit var api: PixelfedAPI
|
||||||
|
|
||||||
var binding: FragmentSearchBinding by bindingLifecycleAware()
|
var binding: FragmentSearchBinding by bindingLifecycleAware()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?,
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentSearchBinding.inflate(inflater, container, false)
|
binding = FragmentSearchBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
@ -56,4 +58,5 @@ class SearchDiscoverFragment : BaseFragment() {
|
|||||||
intent.putExtra(TRENDING_TAG, type)
|
intent.putExtra(TRENDING_TAG, type)
|
||||||
ContextCompat.startActivity(binding.root.context, intent, null)
|
ContextCompat.startActivity(binding.root.context, intent, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import org.pixeldroid.app.posts.PostActivity
|
|||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountViewHolder
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.accountLists.AccountViewHolder
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.HashTagViewHolder
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.HashTagViewHolder
|
||||||
import org.pixeldroid.app.profile.ProfilePostViewHolder
|
import org.pixeldroid.app.profile.ProfilePostViewHolder
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Account
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
import org.pixeldroid.app.utils.api.objects.Attachment
|
import org.pixeldroid.app.utils.api.objects.Attachment
|
||||||
@ -24,7 +24,7 @@ import org.pixeldroid.app.utils.api.objects.Status
|
|||||||
import org.pixeldroid.app.utils.api.objects.Tag
|
import org.pixeldroid.app.utils.api.objects.Tag
|
||||||
import org.pixeldroid.app.utils.setSquareImageFromURL
|
import org.pixeldroid.app.utils.setSquareImageFromURL
|
||||||
|
|
||||||
class TrendingActivity : BaseThemedWithBarActivity() {
|
class TrendingActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityTrendingBinding
|
private lateinit var binding: ActivityTrendingBinding
|
||||||
private lateinit var trendingAdapter : TrendingRecyclerViewAdapter
|
private lateinit var trendingAdapter : TrendingRecyclerViewAdapter
|
||||||
@ -33,6 +33,7 @@ class TrendingActivity : BaseThemedWithBarActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityTrendingBinding.inflate(layoutInflater)
|
binding = ActivityTrendingBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
val recycler = binding.list
|
val recycler = binding.list
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
package org.pixeldroid.app.settings
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import org.pixeldroid.app.BuildConfig
|
|
||||||
import org.pixeldroid.app.R
|
|
||||||
import org.pixeldroid.app.databinding.ActivityAboutBinding
|
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
|
||||||
|
|
||||||
class AboutActivity : BaseThemedWithBarActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
val binding = ActivityAboutBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
supportActionBar?.setTitle(R.string.about_pixeldroid)
|
|
||||||
|
|
||||||
binding.aboutVersionNumber.text = BuildConfig.VERSION_NAME
|
|
||||||
binding.licensesButton.setOnClickListener{
|
|
||||||
val intent = Intent(this, LicenseActivity::class.java)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package org.pixeldroid.app.settings
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
|
||||||
import org.pixeldroid.app.R
|
|
||||||
import org.pixeldroid.app.databinding.OpenSourceLicenseBinding
|
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays licenses for all app dependencies. JSON is
|
|
||||||
* generated by the plugin https://github.com/cookpad/LicenseToolsPlugin.
|
|
||||||
*/
|
|
||||||
class LicenseActivity: BaseThemedWithBarActivity() {
|
|
||||||
|
|
||||||
private lateinit var binding: OpenSourceLicenseBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = OpenSourceLicenseBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
supportActionBar?.setTitle(R.string.dependencies_licenses)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
supportActionBar?.setHomeButtonEnabled(true)
|
|
||||||
|
|
||||||
setupRecyclerView()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
|
||||||
val aboutLibsJson: String = applicationContext.resources.openRawResource(R.raw.aboutlibraries)
|
|
||||||
.bufferedReader().use { it.readText() }
|
|
||||||
|
|
||||||
val libs = Libs.Builder()
|
|
||||||
.withJson(aboutLibsJson)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val adapter = OpenSourceLicenseAdapter(libs)
|
|
||||||
binding.openSourceLicenseRecyclerView.adapter = adapter
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package org.pixeldroid.app.settings
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
|
||||||
import com.mikepenz.aboutlibraries.entity.Library
|
|
||||||
import org.pixeldroid.app.databinding.OpenSourceItemBinding
|
|
||||||
|
|
||||||
class OpenSourceLicenseAdapter(private val openSourceItems: Libs) :
|
|
||||||
RecyclerView.Adapter<OpenSourceLicenseAdapter.OpenSourceLicenceViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OpenSourceLicenceViewHolder
|
|
||||||
{
|
|
||||||
val itemBinding = OpenSourceItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return OpenSourceLicenceViewHolder(itemBinding)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: OpenSourceLicenceViewHolder, position: Int) {
|
|
||||||
val item = openSourceItems.libraries[position]
|
|
||||||
holder.bind(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = openSourceItems.libraries.size
|
|
||||||
|
|
||||||
class OpenSourceLicenceViewHolder(val binding: OpenSourceItemBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
fun bind(item: Library) {
|
|
||||||
with(binding) {
|
|
||||||
if (item.name.isNotEmpty()) {
|
|
||||||
title.isVisible = true
|
|
||||||
title.text = item.name
|
|
||||||
} else {
|
|
||||||
title.isVisible = false
|
|
||||||
}
|
|
||||||
val license = item.licenses.firstOrNull()
|
|
||||||
val licenseName = license?.name ?: ""
|
|
||||||
val licenseUrl = license?.url?.let { " (${it} )" } ?: ""
|
|
||||||
copyright.isVisible = true
|
|
||||||
copyright.apply {
|
|
||||||
text = "$licenseName$licenseUrl"
|
|
||||||
movementMethod = LinkMovementMethod.getInstance()
|
|
||||||
}
|
|
||||||
url.isVisible = true
|
|
||||||
url.apply {
|
|
||||||
text = "${item.developers.firstOrNull()?.name ?: ""} ${item.website}"
|
|
||||||
movementMethod = LinkMovementMethod.getInstance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,7 @@ import android.content.SharedPreferences
|
|||||||
import android.content.res.XmlResourceParser
|
import android.content.res.XmlResourceParser
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.activity.addCallback
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
@ -16,23 +17,39 @@ import androidx.preference.PreferenceManager
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.pixeldroid.app.MainActivity
|
import org.pixeldroid.app.MainActivity
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithBarActivity
|
import org.pixeldroid.app.databinding.SettingsBinding
|
||||||
|
import org.pixeldroid.common.ThemedActivity
|
||||||
import org.pixeldroid.app.utils.setThemeFromPreferences
|
import org.pixeldroid.app.utils.setThemeFromPreferences
|
||||||
|
|
||||||
|
|
||||||
class SettingsActivity : BaseThemedWithBarActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private var restartMainOnExit = false
|
private var restartMainOnExit = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
val binding = SettingsBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
setSupportActionBar(binding.topBar)
|
||||||
|
|
||||||
setContentView(R.layout.settings)
|
|
||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, SettingsFragment())
|
.replace(R.id.settings, SettingsFragment())
|
||||||
.commit()
|
.commit()
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setTitle(R.string.menu_settings)
|
|
||||||
|
onBackPressedDispatcher.addCallback(this /* lifecycle owner */) {
|
||||||
|
// Handle the back button event
|
||||||
|
// If a setting (for example language or theme) was changed, the main activity should be
|
||||||
|
// started without history so that the change is applied to the whole back stack
|
||||||
|
if (restartMainOnExit) {
|
||||||
|
val intent = Intent(this@SettingsActivity, MainActivity::class.java)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
super@SettingsActivity.startActivity(intent)
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
restartMainOnExit = intent.getBooleanExtra("restartMain", false)
|
restartMainOnExit = intent.getBooleanExtra("restartMain", false)
|
||||||
}
|
}
|
||||||
@ -51,28 +68,20 @@ class SettingsActivity : BaseThemedWithBarActivity(), SharedPreferences.OnShared
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
// If a setting (for example language or theme) was changed, the main activity should be
|
sharedPreferences?.let {
|
||||||
// started without history so that the change is applied to the whole back stack
|
|
||||||
if (restartMainOnExit) {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
super.startActivity(intent)
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
|
||||||
when (key) {
|
when (key) {
|
||||||
"theme" -> {
|
"theme" -> {
|
||||||
setThemeFromPreferences(sharedPreferences, resources)
|
setThemeFromPreferences(it, resources)
|
||||||
recreateWithRestartStatus()
|
recreateWithRestartStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
"themeColor" -> {
|
"themeColor" -> {
|
||||||
recreateWithRestartStatus()
|
recreateWithRestartStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark main activity to be changed and recreate the current one
|
* Mark main activity to be changed and recreate the current one
|
||||||
@ -125,7 +134,8 @@ class SettingsActivity : BaseThemedWithBarActivity(), SharedPreferences.OnShared
|
|||||||
class LanguageSettingFragment : DialogFragment() {
|
class LanguageSettingFragment : DialogFragment() {
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val list: MutableList<String> = mutableListOf()
|
val list: MutableList<String> = mutableListOf()
|
||||||
resources.getXml(R.xml.locales_config).use {
|
// IDE doesn't find it, but compiling works apparently?
|
||||||
|
resources.getXml(R.xml._generated_res_locale_config).use {
|
||||||
var eventType = it.eventType
|
var eventType = it.eventType
|
||||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||||
when (eventType) {
|
when (eventType) {
|
||||||
|
228
app/src/main/java/org/pixeldroid/app/stories/StoriesActivity.kt
Normal file
228
app/src/main/java/org/pixeldroid/app/stories/StoriesActivity.kt
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package org.pixeldroid.app.stories
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View.OnClickListener
|
||||||
|
import android.view.View.OnTouchListener
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
|
import com.bumptech.glide.request.RequestListener
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.databinding.ActivityStoriesBinding
|
||||||
|
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
||||||
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Account
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Story
|
||||||
|
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
||||||
|
|
||||||
|
|
||||||
|
class StoriesActivity: BaseActivity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val STORY_CAROUSEL = "LaunchStoryCarousel"
|
||||||
|
const val STORY_CAROUSEL_SELF = "LaunchStoryCarouselSelf"
|
||||||
|
const val STORY_CAROUSEL_USER_ID = "LaunchStoryUserId"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityStoriesBinding
|
||||||
|
|
||||||
|
private lateinit var storyProgress: StoryProgress
|
||||||
|
|
||||||
|
private lateinit var model: StoriesViewModel
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
//force night mode always
|
||||||
|
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val carousel = intent.getSerializableExtra(STORY_CAROUSEL) as? StoryCarousel
|
||||||
|
val userId = intent.getStringExtra(STORY_CAROUSEL_USER_ID)
|
||||||
|
val selfCarousel: Array<Story>? = intent.getSerializableExtra(STORY_CAROUSEL_SELF) as? Array<Story>
|
||||||
|
|
||||||
|
binding = ActivityStoriesBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val _model: StoriesViewModel by viewModels {
|
||||||
|
StoriesViewModelFactory(application, carousel, userId, selfCarousel?.asList())
|
||||||
|
}
|
||||||
|
model = _model
|
||||||
|
|
||||||
|
storyProgress = StoryProgress(model.uiState.value.imageList.size)
|
||||||
|
binding.storyProgressImage.setImageDrawable(storyProgress)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
model.uiState.collect { uiState ->
|
||||||
|
binding.pause.isSelected = uiState.paused
|
||||||
|
|
||||||
|
uiState.age?.let { setTextViewFromISO8601(it, binding.storyAge, false) }
|
||||||
|
|
||||||
|
if (uiState.errorMessage != null) {
|
||||||
|
binding.storyErrorText.setText(uiState.errorMessage)
|
||||||
|
binding.storyErrorCard.isVisible = true
|
||||||
|
} else binding.storyErrorCard.isVisible = false
|
||||||
|
|
||||||
|
if (uiState.snackBar != null) {
|
||||||
|
Snackbar.make(
|
||||||
|
binding.root, uiState.snackBar,
|
||||||
|
Snackbar.LENGTH_SHORT
|
||||||
|
).setAnchorView(binding.storyReplyField).show()
|
||||||
|
model.shownSnackbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uiState.username != null) {
|
||||||
|
binding.storyReplyField.hint = getString(R.string.replyToStory).format(uiState.username)
|
||||||
|
} else binding.storyReplyField.hint = null
|
||||||
|
|
||||||
|
uiState.profilePicture?.let {
|
||||||
|
Glide.with(binding.storyAuthorProfilePicture)
|
||||||
|
.load(it)
|
||||||
|
.apply(RequestOptions.circleCropTransform())
|
||||||
|
.into(binding.storyAuthorProfilePicture)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyAuthor.text = uiState.username
|
||||||
|
|
||||||
|
storyProgress.currentStory = uiState.currentImage
|
||||||
|
|
||||||
|
uiState.imageList.getOrNull(uiState.currentImage)?.let {
|
||||||
|
Glide.with(binding.storyImage)
|
||||||
|
.load(it)
|
||||||
|
.listener(object : RequestListener<Drawable> {
|
||||||
|
override fun onLoadFailed(
|
||||||
|
e: GlideException?,
|
||||||
|
model: Any?,
|
||||||
|
target: Target<Drawable>,
|
||||||
|
isFirstResource: Boolean,
|
||||||
|
): Boolean = false
|
||||||
|
|
||||||
|
override fun onResourceReady(
|
||||||
|
resource: Drawable,
|
||||||
|
model: Any,
|
||||||
|
target: Target<Drawable>?,
|
||||||
|
dataSource: DataSource,
|
||||||
|
isFirstResource: Boolean,
|
||||||
|
): Boolean {
|
||||||
|
Glide.with(binding.storyImage)
|
||||||
|
.load(uiState.imageList.getOrNull(uiState.currentImage + 1))
|
||||||
|
.preload()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into(binding.storyImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Pause when clicked on text field
|
||||||
|
binding.storyReplyField.editText?.setOnFocusChangeListener { view, isFocused ->
|
||||||
|
if (view.isInTouchMode && isFocused) {
|
||||||
|
view.performClick() // picks up first tap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.storyReplyField.editText?.setOnClickListener {
|
||||||
|
if (!model.uiState.value.paused) {
|
||||||
|
model.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyReplyField.editText?.doAfterTextChanged {
|
||||||
|
it?.let { text ->
|
||||||
|
val string = text.toString()
|
||||||
|
if(string != model.uiState.value.reply) model.replyChanged(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyReplyField.setEndIconOnClickListener {
|
||||||
|
binding.storyReplyField.editText?.text?.let { text ->
|
||||||
|
model.sendReply(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.storyErrorCard.setOnClickListener{
|
||||||
|
model.dismissError()
|
||||||
|
}
|
||||||
|
|
||||||
|
model.count.observe(this) { state ->
|
||||||
|
// Render state in UI
|
||||||
|
model.uiState.value.durationList.getOrNull(model.uiState.value.currentImage)?.let {
|
||||||
|
storyProgress.progress = 1 - (state/it.toFloat())
|
||||||
|
binding.storyProgressImage.postInvalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.pause.setOnClickListener {
|
||||||
|
//Set the button's appearance
|
||||||
|
it.isSelected = !it.isSelected
|
||||||
|
model.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
val authorOnClickListener = OnClickListener {
|
||||||
|
if (!model.uiState.value.paused) {
|
||||||
|
model.pause()
|
||||||
|
}
|
||||||
|
model.currentProfileId()?.let {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
Account.openAccountFromId(
|
||||||
|
it,
|
||||||
|
apiHolder.api ?: apiHolder.setToCurrentUser(),
|
||||||
|
this@StoriesActivity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.storyAuthorProfilePicture.setOnClickListener(authorOnClickListener)
|
||||||
|
binding.storyAuthor.setOnClickListener(authorOnClickListener)
|
||||||
|
|
||||||
|
val onTouchListener = OnTouchListener { v, event ->
|
||||||
|
when (event.action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> if (!model.uiState.value.paused) {
|
||||||
|
model.pause()
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP -> if(event.eventTime - event.downTime < 500) {
|
||||||
|
v.performClick()
|
||||||
|
return@OnTouchListener false
|
||||||
|
} else model.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.viewMiddle.setOnTouchListener{ v, event ->
|
||||||
|
when (event.action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> model.pause()
|
||||||
|
MotionEvent.ACTION_UP -> if(event.eventTime - event.downTime < 500) {
|
||||||
|
v.performClick()
|
||||||
|
return@setOnTouchListener false
|
||||||
|
} else model.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
binding.viewLeft.setOnTouchListener(onTouchListener)
|
||||||
|
binding.viewRight.setOnTouchListener(onTouchListener)
|
||||||
|
|
||||||
|
binding.viewRight.setOnClickListener {
|
||||||
|
model.goToNext()
|
||||||
|
}
|
||||||
|
binding.viewLeft.setOnClickListener {
|
||||||
|
model.goToPrevious()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
229
app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
Normal file
229
app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
package org.pixeldroid.app.stories
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import android.text.Editable
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||||
|
import org.pixeldroid.app.utils.api.objects.CarouselUserContainer
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Story
|
||||||
|
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
||||||
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
|
import java.time.Instant
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
data class StoriesUiState(
|
||||||
|
val profilePicture: String? = null,
|
||||||
|
val username: String? = null,
|
||||||
|
val age: Instant? = null,
|
||||||
|
val currentImage: Int = 0,
|
||||||
|
val imageList: List<String> = emptyList(),
|
||||||
|
val durationList: List<Int> = emptyList(),
|
||||||
|
val paused: Boolean = false,
|
||||||
|
@StringRes
|
||||||
|
val errorMessage: Int? = null,
|
||||||
|
@StringRes
|
||||||
|
val snackBar: Int? = null,
|
||||||
|
val reply: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
class StoriesViewModel(
|
||||||
|
application: Application,
|
||||||
|
val carousel: StoryCarousel?,
|
||||||
|
userId: String?,
|
||||||
|
val selfCarousel: List<Story>?
|
||||||
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var apiHolder: PixelfedAPIHolder
|
||||||
|
@Inject
|
||||||
|
lateinit var db: AppDatabase
|
||||||
|
|
||||||
|
private var currentAccount: CarouselUserContainer?
|
||||||
|
|
||||||
|
private val _uiState: MutableStateFlow<StoriesUiState>
|
||||||
|
|
||||||
|
val uiState: StateFlow<StoriesUiState>
|
||||||
|
|
||||||
|
val count = MutableLiveData<Float>()
|
||||||
|
|
||||||
|
private var timer: CountDownTimer? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||||
|
currentAccount =
|
||||||
|
if (selfCarousel != null) {
|
||||||
|
db.userDao().getActiveUser()?.let { CarouselUserContainer(it, selfCarousel) }
|
||||||
|
} else carousel?.nodes?.firstOrNull { it?.user?.id == userId }
|
||||||
|
|
||||||
|
_uiState = MutableStateFlow(newUiStateFromCurrentAccount())
|
||||||
|
uiState = _uiState
|
||||||
|
|
||||||
|
startTimerForCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTimer(timerLength: Float) {
|
||||||
|
count.value = timerLength
|
||||||
|
timer = object: CountDownTimer((timerLength * 1000).toLong(), 50){
|
||||||
|
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
count.value = millisUntilFinished.toFloat() / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish() {
|
||||||
|
goToNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun newUiStateFromCurrentAccount(): StoriesUiState = StoriesUiState(
|
||||||
|
profilePicture = currentAccount?.user?.avatar,
|
||||||
|
age = currentAccount?.nodes?.getOrNull(0)?.created_at,
|
||||||
|
username = currentAccount?.user?.username, //TODO check if not username_acct, think about falling back on other option?
|
||||||
|
errorMessage = null,
|
||||||
|
currentImage = 0,
|
||||||
|
imageList = currentAccount?.nodes?.mapNotNull { it?.src } ?: emptyList(),
|
||||||
|
durationList = currentAccount?.nodes?.mapNotNull { it?.duration } ?: emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun goTo(index: Int){
|
||||||
|
if((0 until uiState.value.imageList.size).contains(index)) {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(
|
||||||
|
currentImage = index,
|
||||||
|
age = currentAccount?.nodes?.getOrNull(index)?.created_at,
|
||||||
|
paused = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(selfCarousel != null) return
|
||||||
|
val currentUserId = currentAccount?.user?.id
|
||||||
|
val currentAccountIndex = carousel?.nodes?.indexOfFirst { it?.user?.id == currentUserId } ?: return
|
||||||
|
currentAccount = when (index) {
|
||||||
|
uiState.value.imageList.size -> {
|
||||||
|
// Go to next user
|
||||||
|
if(currentAccountIndex + 1 >= carousel.nodes.size) return
|
||||||
|
carousel.nodes.getOrNull(currentAccountIndex + 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-1 -> {
|
||||||
|
// Go to previous user
|
||||||
|
if(currentAccountIndex <= 0) return
|
||||||
|
carousel.nodes.getOrNull(currentAccountIndex - 1)
|
||||||
|
}
|
||||||
|
else -> return // Do nothing, given index does not make sense
|
||||||
|
}
|
||||||
|
_uiState.update { newUiStateFromCurrentAccount() }
|
||||||
|
}
|
||||||
|
|
||||||
|
timer?.cancel()
|
||||||
|
startTimerForCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun goToNext() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
val story = currentAccount?.nodes?.getOrNull(uiState.value.currentImage)
|
||||||
|
|
||||||
|
if (story?.seen == true){
|
||||||
|
//TODO update seen when marked successfully as seen?
|
||||||
|
story.id?.let { api.storySeen(it) }
|
||||||
|
}
|
||||||
|
} catch (exception: Exception){
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(errorMessage = R.string.story_could_not_see)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
goTo(uiState.value.currentImage + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun goToPrevious() = goTo(uiState.value.currentImage - 1)
|
||||||
|
|
||||||
|
private fun startTimerForCurrent(){
|
||||||
|
uiState.value.let {
|
||||||
|
it.durationList.getOrNull(it.currentImage)?.toLong()?.let { time ->
|
||||||
|
setTimer(time.toFloat())
|
||||||
|
timer?.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pause() {
|
||||||
|
if(_uiState.value.paused){
|
||||||
|
timer?.start()
|
||||||
|
} else {
|
||||||
|
timer?.cancel()
|
||||||
|
count.value?.let { setTimer(it) }
|
||||||
|
}
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(paused = !currentUiState.paused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendReply(text: Editable) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
currentStoryId()?.let { api.storyComment(it, text.toString()) }
|
||||||
|
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(snackBar = R.string.sent_reply_story)
|
||||||
|
}
|
||||||
|
} catch (exception: Exception){
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(errorMessage = R.string.story_reply_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun currentStoryId(): String? = currentAccount?.nodes?.getOrNull(uiState.value.currentImage)?.id
|
||||||
|
|
||||||
|
fun replyChanged(text: String) {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(reply = text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissError() {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(errorMessage = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shownSnackbar() {
|
||||||
|
_uiState.update { currentUiState ->
|
||||||
|
currentUiState.copy(snackBar = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun currentProfileId(): String? = currentAccount?.user?.id
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class StoriesViewModelFactory(
|
||||||
|
val application: Application,
|
||||||
|
val carousel: StoryCarousel?,
|
||||||
|
val userId: String?,
|
||||||
|
val selfCarousel: List<Story>?
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
return modelClass.getConstructor(Application::class.java, StoryCarousel::class.java, String::class.java, List::class.java).newInstance(application, carousel, userId, selfCarousel)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
package org.pixeldroid.app.stories
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.graphics.RenderEffect
|
||||||
|
import android.graphics.Shader
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.databinding.StoryCarouselBinding
|
||||||
|
import org.pixeldroid.app.databinding.StoryCarouselItemBinding
|
||||||
|
import org.pixeldroid.app.databinding.StoryCarouselSelfBinding
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraActivity
|
||||||
|
import org.pixeldroid.app.postCreation.camera.CameraFragment
|
||||||
|
import org.pixeldroid.app.utils.api.objects.CarouselUserContainer
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Story
|
||||||
|
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
||||||
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that has either 1 or 0 items, to show stories widget or not
|
||||||
|
*/
|
||||||
|
class StoriesAdapter(val lifecycleScope: LifecycleCoroutineScope, val apiHolder: PixelfedAPIHolder) : RecyclerView.Adapter<StoryCarouselViewHolder>() {
|
||||||
|
var carousel: StoryCarousel? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show stories or not.
|
||||||
|
*
|
||||||
|
* Changing this property will immediately notify the Adapter to change the item it's
|
||||||
|
* presenting.
|
||||||
|
*/
|
||||||
|
var showStories: Boolean = false
|
||||||
|
set(newValue) {
|
||||||
|
val oldValue = field
|
||||||
|
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
notifyItemRemoved(0)
|
||||||
|
} else if (newValue && !oldValue) {
|
||||||
|
notifyItemInserted(0)
|
||||||
|
} else if (oldValue && newValue) {
|
||||||
|
notifyItemChanged(0)
|
||||||
|
}
|
||||||
|
field = newValue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StoryCarouselViewHolder {
|
||||||
|
return StoryCarouselViewHolder.create(parent, ::noStories)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: StoryCarouselViewHolder, position: Int) {
|
||||||
|
holder.bind(carousel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int = 0
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = if (showStories) 1 else 0
|
||||||
|
|
||||||
|
private fun noStories(){
|
||||||
|
showStories = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun gotStories(newCarousel: StoryCarousel) {
|
||||||
|
carousel = newCarousel
|
||||||
|
showStories = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshStories(){
|
||||||
|
lifecycleScope.launch {
|
||||||
|
try{
|
||||||
|
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||||
|
val carousel = api.carousel()
|
||||||
|
|
||||||
|
// If there are stories from someone else or our stories to show, show them
|
||||||
|
if (carousel.nodes?.isEmpty() == false || carousel.self?.nodes?.isEmpty() == false) {
|
||||||
|
// Pass carousel to adapter
|
||||||
|
gotStories(carousel)
|
||||||
|
} else {
|
||||||
|
noStories()
|
||||||
|
}
|
||||||
|
} catch (exception: Exception){
|
||||||
|
noStories()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class StoryCarouselViewHolder(val binding: StoryCarouselBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
fun bind(carousel: StoryCarousel?) {
|
||||||
|
val adapter = StoriesListAdapter()
|
||||||
|
binding.storyCarousel.adapter = adapter
|
||||||
|
|
||||||
|
carousel?.let { adapter.initCarousel(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun create(parent: ViewGroup, noStories: () -> Unit): StoryCarouselViewHolder {
|
||||||
|
val itemBinding = StoryCarouselBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context), parent, false
|
||||||
|
)
|
||||||
|
return StoryCarouselViewHolder(itemBinding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class StoriesListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private var storyCarousel: StoryCarousel? = null
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return if(viewType == R.layout.story_carousel_self){
|
||||||
|
val v = StoryCarouselSelfBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
v.myStory.visibility =
|
||||||
|
if (storyCarousel?.self?.nodes?.isEmpty() == false) View.VISIBLE
|
||||||
|
else View.GONE
|
||||||
|
|
||||||
|
AddViewHolder(v)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val v = StoryCarouselItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
ViewHolder(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if(position == 0) R.layout.story_carousel_self
|
||||||
|
else R.layout.story_carousel_item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
if(position > 0) {
|
||||||
|
val carouselPosition = position - 1
|
||||||
|
storyCarousel?.nodes?.get(carouselPosition)?.let { (holder as ViewHolder).bindItem(it) }
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
storyCarousel?.nodes?.get(carouselPosition)?.user?.id?.let { userId ->
|
||||||
|
val intent = Intent(holder.itemView.context, StoriesActivity::class.java)
|
||||||
|
intent.putExtra(StoriesActivity.STORY_CAROUSEL, storyCarousel)
|
||||||
|
intent.putExtra(StoriesActivity.STORY_CAROUSEL_USER_ID, userId)
|
||||||
|
holder.itemView.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
storyCarousel?.self?.nodes?.let { (holder as? AddViewHolder)?.bindItem(it.filterNotNull()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
// If the storyCarousel is not set, the carousel is not shown, so itemCount of 0
|
||||||
|
return (storyCarousel?.nodes?.size?.plus(1)) ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun initCarousel(carousel: StoryCarousel){
|
||||||
|
storyCarousel = carousel
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddViewHolder(private val itemBinding: StoryCarouselSelfBinding) : RecyclerView.ViewHolder(itemBinding.root) {
|
||||||
|
fun bindItem(nodes: List<Story>) {
|
||||||
|
itemBinding.addStory.setOnClickListener {
|
||||||
|
val intent = Intent(itemView.context, CameraActivity::class.java)
|
||||||
|
intent.putExtra(CameraFragment.CAMERA_ACTIVITY_STORY, true)
|
||||||
|
itemView.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
itemBinding.myStory.setOnClickListener {
|
||||||
|
val intent = Intent(itemView.context, StoriesActivity::class.java)
|
||||||
|
intent.putExtra(StoriesActivity.STORY_CAROUSEL_SELF, nodes.toTypedArray())
|
||||||
|
itemView.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only show image on new Android versions, because the transformations need it and the
|
||||||
|
// text is not legible without the transformations
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
Glide.with(itemBinding.root).load(nodes.firstOrNull()?.src).into(itemBinding.carouselImageView)
|
||||||
|
val value = 70 * 255 / 100
|
||||||
|
val darkFilterRenderEffect = PorterDuffColorFilter(Color.argb(value, 0, 0, 0), PorterDuff.Mode.SRC_ATOP)
|
||||||
|
val blurRenderEffect =
|
||||||
|
RenderEffect.createBlurEffect(
|
||||||
|
4f, 4f, Shader.TileMode.MIRROR
|
||||||
|
)
|
||||||
|
val combinedEffect = RenderEffect.createColorFilterEffect(darkFilterRenderEffect, blurRenderEffect)
|
||||||
|
itemBinding.carouselImageView.setRenderEffect(combinedEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(private val itemBinding: StoryCarouselItemBinding) :
|
||||||
|
RecyclerView.ViewHolder(itemBinding.root) {
|
||||||
|
fun bindItem(user: CarouselUserContainer) {
|
||||||
|
Glide.with(itemBinding.root).load(user.nodes?.firstOrNull()?.src).into(itemBinding.carouselImageView)
|
||||||
|
Glide.with(itemBinding.root).load(user.user?.avatar).circleCrop().into(itemBinding.storyAuthorProfilePicture)
|
||||||
|
|
||||||
|
itemBinding.username.text = user.user?.username ?: "" //TODO check which one to use here!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package org.pixeldroid.app.stories
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.ColorFilter
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied & adapted from AntennaPod's EchoProgress class because it looked great and is very simple
|
||||||
|
* AntennaPod/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoProgress.java
|
||||||
|
*/
|
||||||
|
class StoryProgress(private val numStories: Int) : Drawable() {
|
||||||
|
private val paint: Paint = Paint().apply {
|
||||||
|
flags = Paint.ANTI_ALIAS_FLAG
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeJoin = Paint.Join.ROUND
|
||||||
|
strokeCap = Paint.Cap.ROUND
|
||||||
|
color = -0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress = 0f
|
||||||
|
var currentStory: Int = 0
|
||||||
|
|
||||||
|
override fun draw(canvas: Canvas) {
|
||||||
|
paint.strokeWidth = 0.5f * bounds.height()
|
||||||
|
val y = 0.5f * bounds.height()
|
||||||
|
val sectionWidth = 1.0f * bounds.width() / numStories
|
||||||
|
val sectionPadding = 0.03f * sectionWidth
|
||||||
|
// Iterate over stories
|
||||||
|
for (i in 0 until numStories) {
|
||||||
|
if (i < currentStory) {
|
||||||
|
// If current drawing position is smaller than current story, the paint we will use
|
||||||
|
// should be opaque: this story is already "seen"
|
||||||
|
paint.alpha = 255
|
||||||
|
} else {
|
||||||
|
// Otherwise it should be somewhat transparent, denoting it is not yet seen
|
||||||
|
paint.alpha = 100
|
||||||
|
}
|
||||||
|
// Draw an entire line with the paint, for now ignoring partial progress within the
|
||||||
|
// current story
|
||||||
|
canvas.drawLine(
|
||||||
|
i * sectionWidth + sectionPadding,
|
||||||
|
y,
|
||||||
|
(i + 1) * sectionWidth - sectionPadding,
|
||||||
|
y,
|
||||||
|
paint
|
||||||
|
)
|
||||||
|
// If current position is equal to progress, we are drawing the current story. Thus we
|
||||||
|
// should account for partial progress and paint the beginning of the line opaquely
|
||||||
|
if (i == currentStory) {
|
||||||
|
paint.alpha = 255
|
||||||
|
canvas.drawLine(
|
||||||
|
currentStory * sectionWidth + sectionPadding,
|
||||||
|
y,
|
||||||
|
currentStory * sectionWidth + sectionPadding + progress * (sectionWidth - 2 * sectionPadding),
|
||||||
|
y,
|
||||||
|
paint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun getOpacity(): Int {
|
||||||
|
return PixelFormat.TRANSLUCENT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAlpha(alpha: Int) {}
|
||||||
|
override fun setColorFilter(cf: ColorFilter?) {}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
|||||||
package org.pixeldroid.app.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import org.pixeldroid.app.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
open class BaseActivity : AppCompatActivity() {
|
open class BaseActivity : org.pixeldroid.common.ThemedActivity() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var db: AppDatabase
|
lateinit var db: AppDatabase
|
||||||
@ -19,7 +18,7 @@ open class BaseActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,10 @@
|
|||||||
package org.pixeldroid.app.utils
|
package org.pixeldroid.app.utils
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -22,4 +25,18 @@ open class BaseFragment: Fragment() {
|
|||||||
(requireActivity().application as PixelDroidApplication).getAppComponent().inject(this)
|
(requireActivity().application as PixelDroidApplication).getAppComponent().inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val requestPermissionDownloadPic =
|
||||||
|
registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { isGranted: Boolean ->
|
||||||
|
if (!isGranted) {
|
||||||
|
context?.let {
|
||||||
|
MaterialAlertDialogBuilder(it)
|
||||||
|
.setMessage(R.string.write_permission_download_pic)
|
||||||
|
.setNegativeButton(android.R.string.ok) { _, _ -> }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package org.pixeldroid.app.utils
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
|
|
||||||
open class BaseThemedWithBarActivity : BaseActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
// Set theme when we chose one
|
|
||||||
themeActionBar()?.let { setTheme(it) }
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package org.pixeldroid.app.utils
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
|
|
||||||
open class BaseThemedWithoutBarActivity : BaseActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
// Set theme when we chose one
|
|
||||||
themeNoActionBar()?.let { setTheme(it) }
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,6 +24,7 @@ fun setProfileImageFromURL(view : View, url : String?, image : ImageView) {
|
|||||||
* @param image, the imageView into which we will load the image
|
* @param image, the imageView into which we will load the image
|
||||||
*/
|
*/
|
||||||
fun setSquareImageFromURL(view : View, url : String?, image : ImageView, blurhash: String? = null) {
|
fun setSquareImageFromURL(view : View, url : String?, image : ImageView, blurhash: String? = null) {
|
||||||
|
//TODO performance: placeholder here takes a lot of time to compute and this is not async!
|
||||||
Glide.with(view).load(url).placeholder(
|
Glide.with(view).load(url).placeholder(
|
||||||
blurhash?.let { BlurHashDecoder.blurHashBitmap(view.resources, it, 32, 32) }
|
blurhash?.let { BlurHashDecoder.blurHashBitmap(view.resources, it, 32, 32) }
|
||||||
).apply(RequestOptions().centerCrop()).into(image)
|
).apply(RequestOptions().centerCrop()).into(image)
|
||||||
|
@ -161,30 +161,6 @@ fun setThemeFromPreferences(preferences: SharedPreferences, resources: Resources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
fun Context.themeNoActionBar(): Int? {
|
|
||||||
return when(PreferenceManager.getDefaultSharedPreferences(this).getInt("themeColor", 0)) {
|
|
||||||
// No theme was chosen: the user wants to use the system dynamic color (from wallpaper for example)
|
|
||||||
-1 -> null
|
|
||||||
1 -> R.style.AppTheme2_NoActionBar
|
|
||||||
2 -> R.style.AppTheme3_NoActionBar
|
|
||||||
3 -> R.style.AppTheme4_NoActionBar
|
|
||||||
else -> R.style.AppTheme5_NoActionBar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
fun Context.themeActionBar(): Int? {
|
|
||||||
return when(PreferenceManager.getDefaultSharedPreferences(this).getInt("themeColor", 0)) {
|
|
||||||
// No theme was chosen: the user wants to use the system dynamic color (from wallpaper for example)
|
|
||||||
-1 -> null
|
|
||||||
1 -> R.style.AppTheme2
|
|
||||||
2 -> R.style.AppTheme3
|
|
||||||
3 -> R.style.AppTheme4
|
|
||||||
else -> R.style.AppTheme5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun Context.getColorFromAttr(@AttrRes attrColor: Int): Int = MaterialColors.getColor(this, attrColor, Color.BLACK)
|
fun Context.getColorFromAttr(@AttrRes attrColor: Int): Int = MaterialColors.getColor(this, attrColor, Color.BLACK)
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import retrofit2.converter.gson.GsonConverterFactory
|
|||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
import retrofit2.http.Field
|
import retrofit2.http.Field
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -51,7 +52,9 @@ interface PixelfedAPI {
|
|||||||
.client(
|
.client(
|
||||||
OkHttpClient().newBuilder().addNetworkInterceptor(headerInterceptor)
|
OkHttpClient().newBuilder().addNetworkInterceptor(headerInterceptor)
|
||||||
// Only do secure-ish TLS connections (no HTTP or very old SSL/TLS)
|
// Only do secure-ish TLS connections (no HTTP or very old SSL/TLS)
|
||||||
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS)).build()
|
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS))
|
||||||
|
.readTimeout(20, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
)
|
)
|
||||||
.build().create(PixelfedAPI::class.java)
|
.build().create(PixelfedAPI::class.java)
|
||||||
}
|
}
|
||||||
@ -74,6 +77,7 @@ interface PixelfedAPI {
|
|||||||
OkHttpClient().newBuilder().addNetworkInterceptor(headerInterceptor)
|
OkHttpClient().newBuilder().addNetworkInterceptor(headerInterceptor)
|
||||||
// Only do secure-ish TLS connections (no HTTP or very old SSL/TLS)
|
// Only do secure-ish TLS connections (no HTTP or very old SSL/TLS)
|
||||||
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS))
|
.connectionSpecs(listOf(ConnectionSpec.MODERN_TLS))
|
||||||
|
.readTimeout(20, TimeUnit.SECONDS)
|
||||||
.authenticator(TokenAuthenticator(user, db, pixelfedAPIHolder))
|
.authenticator(TokenAuthenticator(user, db, pixelfedAPIHolder))
|
||||||
.addInterceptor {
|
.addInterceptor {
|
||||||
it.request().newBuilder().run {
|
it.request().newBuilder().run {
|
||||||
@ -161,6 +165,7 @@ interface PixelfedAPI {
|
|||||||
@Field("poll[expires_in]") poll_expires: List<String>? = null,
|
@Field("poll[expires_in]") poll_expires: List<String>? = null,
|
||||||
@Field("poll[multiple]") poll_multiple: List<String>? = null,
|
@Field("poll[multiple]") poll_multiple: List<String>? = null,
|
||||||
@Field("poll[hide_totals]") poll_hideTotals: List<String>? = null,
|
@Field("poll[hide_totals]") poll_hideTotals: List<String>? = null,
|
||||||
|
//FIXME this should be able to take a boolean or at least "true"/"false" but only "0"/"1" works
|
||||||
@Field("sensitive") sensitive: Int? = null,
|
@Field("sensitive") sensitive: Int? = null,
|
||||||
@Field("spoiler_text") spoiler_text: String? = null,
|
@Field("spoiler_text") spoiler_text: String? = null,
|
||||||
@Field("visibility") visibility: String = "public",
|
@Field("visibility") visibility: String = "public",
|
||||||
@ -231,6 +236,43 @@ interface PixelfedAPI {
|
|||||||
@Query("post_id") post_id: String,
|
@Query("post_id") post_id: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@GET("/api/pixelfed/v1/stories/self-carousel")
|
||||||
|
suspend fun carousel(): StoryCarousel
|
||||||
|
|
||||||
|
@POST("/api/v1.1/stories/seen")
|
||||||
|
suspend fun storySeen(
|
||||||
|
@Query("id") id: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@POST("/api/v1.1/stories/comment")
|
||||||
|
suspend fun storyComment(
|
||||||
|
@Query("sid") sid: String,
|
||||||
|
@Query("caption") caption: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Multipart
|
||||||
|
@POST("/api/v1.1/stories/add")
|
||||||
|
fun storyUpload(
|
||||||
|
@Part file: MultipartBody.Part,
|
||||||
|
// The API takes this value but then overwrites it in /api/v1.1/stories/publish, so ignore this
|
||||||
|
@Part duration: MultipartBody.Part? = null,
|
||||||
|
): Observable<Attachment>
|
||||||
|
|
||||||
|
@POST("/api/v1.1/stories/publish")
|
||||||
|
suspend fun storyPublish(
|
||||||
|
@Query("media_id") media_id: String,
|
||||||
|
//From 0 to 30, duration in seconds of the story
|
||||||
|
@Query("duration") duration: Int = 10,
|
||||||
|
//FIXME this should be able to take a boolean or at least "true"/"false" but only "0"/"1" works. Same issue as sensitive boolean in postStatus
|
||||||
|
@Query("can_reply") can_reply: String,
|
||||||
|
@Query("can_react") can_react: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@POST("/api/v1.1/stories/self-expire/{id}")
|
||||||
|
suspend fun deleteCarousel(
|
||||||
|
@Path("id") storyId: String
|
||||||
|
)
|
||||||
|
|
||||||
//Used in our case to retrieve comments for a given status
|
//Used in our case to retrieve comments for a given status
|
||||||
@GET("/api/v1/statuses/{id}/context")
|
@GET("/api/v1/statuses/{id}/context")
|
||||||
suspend fun statusComments(
|
suspend fun statusComments(
|
||||||
|
@ -57,11 +57,13 @@ data class Account(
|
|||||||
suspend fun openAccountFromId(id: String, api : PixelfedAPI, context: Context) {
|
suspend fun openAccountFromId(id: String, api : PixelfedAPI, context: Context) {
|
||||||
val account = try {
|
val account = try {
|
||||||
api.getAccount(id)
|
api.getAccount(id)
|
||||||
} catch (exception: IOException) {
|
} catch (exception: Exception) {
|
||||||
Log.e("GET ACCOUNT ERROR", exception.toString())
|
val toLog = if (exception is HttpException) {
|
||||||
return
|
exception.code().toString()
|
||||||
} catch (exception: HttpException) {
|
} else {
|
||||||
Log.e("ERROR CODE", exception.code().toString())
|
exception.toString()
|
||||||
|
}
|
||||||
|
Log.e("GET ACCOUNT ERROR", toLog)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//Open the account page in a separate activity
|
//Open the account page in a separate activity
|
||||||
|
@ -18,6 +18,12 @@ data class Attachment(
|
|||||||
|
|
||||||
//Deprecated attributes
|
//Deprecated attributes
|
||||||
val text_url: String? = null, //URL
|
val text_url: String? = null, //URL
|
||||||
|
|
||||||
|
//Pixelfed's Story upload response... TODO make the server return a regular Attachment?
|
||||||
|
val msg: String? = null,
|
||||||
|
val media_id: String? = null,
|
||||||
|
val media_url: String? = null,
|
||||||
|
val media_type: String? = null,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
enum class AttachmentType: Serializable {
|
enum class AttachmentType: Serializable {
|
||||||
unknown, image, gifv, video, audio
|
unknown, image, gifv, video, audio
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package org.pixeldroid.app.utils.api.objects
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
@ -11,6 +13,7 @@ import androidx.core.net.toUri
|
|||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.posts.getDomain
|
import org.pixeldroid.app.posts.getDomain
|
||||||
|
import org.pixeldroid.app.utils.getMimeType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -148,11 +151,13 @@ open class Status(
|
|||||||
)
|
)
|
||||||
val file = path.toUri()
|
val file = path.toUri()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
val shareIntent: Intent = Intent.createChooser(Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
putExtra(Intent.EXTRA_STREAM, file)
|
putExtra(Intent.EXTRA_STREAM, file)
|
||||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
type = "image/$ext"
|
type = file.getMimeType(context.contentResolver)
|
||||||
}, null)
|
}, null)
|
||||||
|
|
||||||
context.startActivity(shareIntent)
|
context.startActivity(shareIntent)
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
|
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
data class StoryCarousel(
|
||||||
|
val self: CarouselUserContainer?,
|
||||||
|
val nodes: List<CarouselUserContainer?>?
|
||||||
|
): Serializable
|
||||||
|
|
||||||
|
data class CarouselUser(
|
||||||
|
val id: String?,
|
||||||
|
val username: String?,
|
||||||
|
val username_acct: String?,
|
||||||
|
val avatar: String?, // URL to account avatar
|
||||||
|
val local: Boolean?, // Is this story from the local instance?
|
||||||
|
val is_author: Boolean?, // Is this me? (seems redundant with id)
|
||||||
|
): Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container with a description of the [user] and a list of stories ([nodes])
|
||||||
|
*/
|
||||||
|
data class CarouselUserContainer(
|
||||||
|
val user: CarouselUser?,
|
||||||
|
val nodes: List<Story?>?,
|
||||||
|
): Serializable {
|
||||||
|
constructor(user: UserDatabaseEntity, nodes: List<Story?>?) : this(
|
||||||
|
CarouselUser(user.user_id, user.username, null, user.avatar_static,
|
||||||
|
local = true,
|
||||||
|
is_author = true
|
||||||
|
), nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Story(
|
||||||
|
val id: String?,
|
||||||
|
val pid: String?, // id of author
|
||||||
|
val type: String?, //TODO make enum of this? examples: "photo", ???
|
||||||
|
val src: String?, // URL to photo of story
|
||||||
|
val duration: Int?, //Time in seconds that the Story should be shown
|
||||||
|
val seen: Boolean?, //Indication of whether this story has been seen. Set to true using carouselSeen
|
||||||
|
val created_at: Instant?, //ISO 8601 Datetime
|
||||||
|
): Serializable
|
@ -10,6 +10,8 @@ import dagger.Component
|
|||||||
import org.pixeldroid.app.directMessages.ui.main.ConversationsViewModel
|
import org.pixeldroid.app.directMessages.ui.main.ConversationsViewModel
|
||||||
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
||||||
import org.pixeldroid.app.profile.EditProfileViewModel
|
import org.pixeldroid.app.profile.EditProfileViewModel
|
||||||
|
import org.pixeldroid.app.stories.StoriesViewModel
|
||||||
|
import org.pixeldroid.app.stories.StoryCarouselViewHolder
|
||||||
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
|
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ interface ApplicationComponent {
|
|||||||
fun inject(postCreationViewModel: PostCreationViewModel)
|
fun inject(postCreationViewModel: PostCreationViewModel)
|
||||||
fun inject(editProfileViewModel: EditProfileViewModel)
|
fun inject(editProfileViewModel: EditProfileViewModel)
|
||||||
fun inject(editProfileViewModel: ConversationsViewModel)
|
fun inject(editProfileViewModel: ConversationsViewModel)
|
||||||
|
fun inject(storiesViewModel: StoriesViewModel)
|
||||||
|
|
||||||
val context: Context?
|
val context: Context?
|
||||||
val application: Application?
|
val application: Application?
|
||||||
|
19
app/src/main/res/color/selector_story_post.xml
Normal file
19
app/src/main/res/color/selector_story_post.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2020 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checked="true" android:color="?attr/colorSecondary"/>
|
||||||
|
</selector>
|
20
app/src/main/res/color/selector_story_post_text.xml
Normal file
20
app/src/main/res/color/selector_story_post_text.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2020 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checked="true" android:color="?attr/colorOnSecondary"/>
|
||||||
|
<item android:state_checked="false" android:color="?attr/colorOnSecondaryContainer"/>
|
||||||
|
</selector>
|
5
app/src/main/res/drawable/arrow_forward.xml
Normal file
5
app/src/main/res/drawable/arrow_forward.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||||
|
</vector>
|
@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"
|
|
||||||
android:fillColor="?attr/colorOnBackground"/>
|
|
||||||
</vector>
|
|
@ -1,8 +1,11 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:viewportWidth="201.8771"
|
android:viewportWidth="403.75"
|
||||||
android:viewportHeight="218.8104"
|
android:viewportHeight="437.6"
|
||||||
android:width="254dp"
|
android:width="100dp"
|
||||||
android:height="275dp">
|
android:height="108dp">
|
||||||
|
<group android:translateX="100"
|
||||||
|
android:translateY="115">
|
||||||
|
|
||||||
<group
|
<group
|
||||||
android:translateX="-1.41459"
|
android:translateX="-1.41459"
|
||||||
android:translateY="-24.00768">
|
android:translateY="-24.00768">
|
||||||
@ -808,4 +811,5 @@
|
|||||||
android:strokeColor="#000000"
|
android:strokeColor="#000000"
|
||||||
android:strokeWidth="1.32292"
|
android:strokeWidth="1.32292"
|
||||||
android:strokeLineCap="round" />
|
android:strokeLineCap="round" />
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
File diff suppressed because one or more lines are too long
5
app/src/main/res/drawable/pause.xml
Normal file
5
app/src/main/res/drawable/pause.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/play.xml
Normal file
5
app/src/main/res/drawable/play.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
||||||
|
</vector>
|
8
app/src/main/res/drawable/play_pause.xml
Normal file
8
app/src/main/res/drawable/play_pause.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/play"
|
||||||
|
android:state_selected="true" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/pause"/>
|
||||||
|
</selector>
|
5
app/src/main/res/drawable/story_play.xml
Normal file
5
app/src/main/res/drawable/story_play.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z"/>
|
||||||
|
</vector>
|
@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"
|
|
||||||
android:fillColor="?attr/colorOnBackground"/>
|
|
||||||
</vector>
|
|
@ -1,136 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fadeScrollbars="false">
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:context=".settings.AboutActivity">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:id="@+id/imageView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/mascot" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/aboutAppName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="11dp"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/aboutVersionNumber"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/aboutAppName"
|
|
||||||
tools:text="v1.0.realversion" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/aboutAppDescription"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/license_info"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/aboutVersionNumber" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/aboutWebsite"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="@string/project_website"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/aboutAppDescription" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contributeTranslationsText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:drawablePadding="6dp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="@string/help_translate"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/aboutWebsite"
|
|
||||||
app:drawableLeftCompat="@drawable/translate_black_24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contributeTranslationsUrl"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="https://weblate.pixeldroid.org"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contributeTranslationsText"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contributeForgeText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:drawablePadding="6dp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="@string/issues_contribute"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contributeTranslationsUrl"
|
|
||||||
app:drawableLeftCompat="@drawable/bug_report_black_24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/contributeForgeUrl"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="https://gitlab.shinice.net/pixeldroid/PixelDroid"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contributeForgeText"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/licensesButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:text="@string/dependencies_licenses"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contributeForgeUrl" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,13 +1,34 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
tools:context=".postCreation.camera.CameraActivity">
|
tools:context=".postCreation.camera.CameraActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/camera_activity_fragment"
|
android:id="@+id/camera_activity_fragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/top_bar" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,14 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".searchDiscover.TrendingActivity">
|
tools:context=".searchDiscover.TrendingActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
android:id = "@+id/collectionFragment"
|
android:id = "@+id/collectionFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,9 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/edit_profile" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/profilePic"
|
android:id="@+id/profilePic"
|
||||||
@ -11,11 +34,11 @@
|
|||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="24dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
|
android:contentDescription="@string/profile_picture"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/textInputLayoutName"
|
app:layout_constraintBottom_toTopOf="@+id/textInputLayoutName"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@tools:sample/avatars"
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
android:contentDescription="@string/profile_picture" />
|
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
@ -34,8 +57,8 @@
|
|||||||
android:id="@+id/nameEditText"
|
android:id="@+id/nameEditText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/your_name"
|
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
|
android:hint="@string/your_name"
|
||||||
android:imeOptions="actionDone" />
|
android:imeOptions="actionDone" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
@ -73,10 +96,11 @@
|
|||||||
android:id="@+id/privateText"
|
android:id="@+id/privateText"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/privateSwitch"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/privateSwitch"
|
app:layout_constraintStart_toEndOf="@+id/privateSwitch"
|
||||||
app:layout_constraintTop_toTopOf="@+id/privateSwitch"
|
app:layout_constraintTop_toTopOf="@+id/privateSwitch">
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/privateSwitch">
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/privateTitle"
|
android:id="@+id/privateTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -84,8 +108,8 @@
|
|||||||
android:text="@string/private_account"
|
android:text="@string/private_account"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -103,20 +127,20 @@
|
|||||||
android:id="@+id/editButton"
|
android:id="@+id/editButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/more_profile_settings"
|
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="@string/more_profile_settings"
|
||||||
|
app:icon="@drawable/ic_baseline_open_in_browser_24"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:icon="@drawable/ic_baseline_open_in_browser_24"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/privateText" />
|
app:layout_constraintTop_toBottomOf="@+id/privateText" />
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/progressCard"
|
android:id="@+id/progressCard"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -124,9 +148,9 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="8dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/progressIcon"
|
android:id="@+id/progressIcon"
|
||||||
@ -135,6 +159,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/savingProgressBar"
|
android:id="@+id/savingProgressBar"
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
@ -146,39 +171,41 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/error"
|
android:id="@+id/error"
|
||||||
app:tint="?attr/colorOnSecondaryContainer"
|
|
||||||
android:src="@drawable/error"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/profile_saved"
|
||||||
|
android:src="@drawable/error"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:contentDescription="@string/profile_saved" />
|
app:tint="?attr/colorOnSecondaryContainer" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/done"
|
android:id="@+id/done"
|
||||||
android:src="@drawable/check_circle_24"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/profile_saved"
|
||||||
|
android:src="@drawable/check_circle_24"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:contentDescription="@string/profile_saved" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/progressText"
|
android:id="@+id/progressText"
|
||||||
tools:text="@string/fetching_profile"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/progressIcon"/>
|
app:layout_constraintTop_toBottomOf="@+id/progressIcon"
|
||||||
|
tools:text="@string/fetching_profile" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,5 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/followsFragment"
|
android:id="@+id/followsFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_bar" />
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -21,7 +21,9 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/mascotImage"
|
android:id="@+id/mascotImage"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="508dp"
|
||||||
|
android:layout_marginTop="-130dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="20dp"
|
android:layout_marginBottom="20dp"
|
||||||
android:contentDescription="@string/mascot_description"
|
android:contentDescription="@string/mascot_description"
|
||||||
@ -30,6 +32,7 @@
|
|||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/login_activity_instance_input_layout"
|
android:id="@+id/login_activity_instance_input_layout"
|
||||||
android:layout_width="250dp"
|
android:layout_width="250dp"
|
||||||
|
android:layout_marginTop="-130dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:hint="@string/domain_of_your_instance"
|
android:hint="@string/domain_of_your_instance"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
android:id="@+id/main_drawer_button"
|
android:id="@+id/main_drawer_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorSurface"
|
android:background="?attr/colorSurfaceContainer"
|
||||||
android:contentDescription="@string/open_drawer_menu"
|
android:contentDescription="@string/open_drawer_menu"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:src="@drawable/ic_baseline_menu_24" />
|
android:src="@drawable/ic_baseline_menu_24" />
|
||||||
|
@ -3,16 +3,27 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/scrollview"
|
android:id="@+id/scrollview"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".posts.PostActivity">
|
tools:context=".posts.PostActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"/>
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
android:id="@+id/collapsing_toolbar_layout"
|
android:id="@+id/collapsing_toolbar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".postCreation.PostCreationActivity">
|
tools:context=".postCreation.PostCreationActivity">
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
@ -11,6 +13,7 @@
|
|||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
app:defaultNavHost="true"
|
app:defaultNavHost="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
@ -18,4 +21,4 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:navGraph="@navigation/post_creation_graph" />
|
app:navGraph="@navigation/post_creation_graph" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -3,16 +3,27 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".profile.ProfileActivity">
|
tools:context=".profile.ProfileActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/app_bar_layout"
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"/>
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:id="@+id/collapsing_toolbar_layout"
|
android:id="@+id/collapsing_toolbar_layout"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||||
|
@ -1,11 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
tools:context=".posts.ReportActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".posts.ReportActivity">
|
tools:context=".posts.ReportActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/report"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/report_target_textview"
|
android:id="@+id/report_target_textview"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -14,8 +32,8 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
|
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toBottomOf="@+id/top_bar"
|
||||||
tools:text="Reporting @user's post:" />
|
tools:text="Report @user's post" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/textInputLayout"
|
android:id="@+id/textInputLayout"
|
||||||
@ -96,3 +114,4 @@
|
|||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,13 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/menu_settings" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
@ -26,5 +42,4 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
168
app/src/main/res/layout/activity_stories.xml
Normal file
168
app/src/main/res/layout/activity_stories.xml
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/storyErrorCard"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:minHeight="48dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/storyErrorIcon"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/error"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="?attr/colorOnSecondaryContainer" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyErrorText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/storyErrorIcon"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/storyErrorIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/storyErrorIcon"
|
||||||
|
tools:text="Something is wrong with stories" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/storyImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:contentDescription="@string/story_image"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/story_progress_image"
|
||||||
|
app:layout_constraintVertical_bias="1.0"
|
||||||
|
tools:scaleType="centerCrop"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic[10]" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/pause"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/story_pause"
|
||||||
|
android:src="@drawable/play_pause"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/storyAuthor"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/storyAge"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/story_progress_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/storyAuthorProfilePicture"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:contentDescription="@string/profile_picture"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/story_progress_image"
|
||||||
|
tools:srcCompat="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyAuthor"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/storyAuthorProfilePicture"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||||
|
tools:text="username" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyAge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/storyAuthor"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/storyAuthor"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||||
|
tools:text="48m" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/storyReplyField"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:endIconContentDescription="TODO"
|
||||||
|
app:endIconDrawable="@drawable/ic_send_blue"
|
||||||
|
app:endIconMode="custom"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
tools:hint="Reply to PixelDroid">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/viewRight"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/storyReplyField"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/viewMiddle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyAuthorProfilePicture" />
|
||||||
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/viewMiddle"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/storyReplyField"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/viewRight"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/viewLeft"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyAuthorProfilePicture" />
|
||||||
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/viewLeft"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/storyReplyField"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/viewMiddle"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyAuthorProfilePicture" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,11 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
tools:context=".searchDiscover.TrendingActivity">
|
tools:context=".searchDiscover.TrendingActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
@ -25,7 +42,7 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toBottomOf="@+id/top_bar">
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -59,3 +76,4 @@
|
|||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,17 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/errorLayout"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/errorLayout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:showIn="@layout/fragment_feed"
|
tools:showIn="@layout/fragment_feed"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imageView4"
|
android:id="@+id/imageView4"
|
||||||
|
@ -11,29 +11,81 @@
|
|||||||
android:id="@+id/carousel"
|
android:id="@+id/carousel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:showCaption="true"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/buttonConstraints"
|
app:layout_constraintTop_toBottomOf="@+id/top_bar"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:showCaption="true" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/buttonConstraints"
|
android:id="@+id/top_bar"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/post_creation_send_button"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:enabled="true"
|
android:minHeight="?attr/actionBarSize"
|
||||||
android:text="@string/upload_next_step"
|
android:theme="?attr/actionBarTheme"
|
||||||
android:visibility="visible"
|
android:background="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:titleTextColor="?attr/colorOnSecondaryContainer"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/backbutton"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:tint="?attr/colorOnSecondaryContainer"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@android:string/cancel"
|
||||||
|
android:src="?attr/homeAsUpIndicator"
|
||||||
|
android:tooltipText='@android:string/cancel'
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
android:id="@+id/toggleStoryPost"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:checkedButton="@+id/buttonPost"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/post_creation_next_button"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/backbutton"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:selectionRequired="true"
|
||||||
|
app:singleSelection="true">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonStory"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
app:backgroundTint="@color/selector_story_post"
|
||||||
|
android:textColor="@color/selector_story_post_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/type_story" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonPost"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
app:backgroundTint="@color/selector_story_post"
|
||||||
|
android:textColor="@color/selector_story_post_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/type_post" />
|
||||||
|
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||||
|
app:icon="@drawable/arrow_forward"
|
||||||
|
app:iconGravity="end"
|
||||||
|
android:id="@+id/post_creation_next_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:enabled="true"
|
||||||
|
android:text="@string/continue_post_creation"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@ -44,7 +96,7 @@
|
|||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toBottomOf="@id/top_bar">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageButton
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
android:id="@+id/savePhotoButton"
|
android:id="@+id/savePhotoButton"
|
||||||
@ -53,8 +105,8 @@
|
|||||||
android:layout_marginStart="30dp"
|
android:layout_marginStart="30dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/save_to_gallery"
|
android:contentDescription="@string/save_to_gallery"
|
||||||
android:tooltipText='@string/save_to_gallery'
|
|
||||||
android:src="@drawable/download_file_30dp"
|
android:src="@drawable/download_file_30dp"
|
||||||
|
android:tooltipText='@string/save_to_gallery'
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -66,8 +118,8 @@
|
|||||||
android:layout_marginStart="30dp"
|
android:layout_marginStart="30dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/delete"
|
android:contentDescription="@string/delete"
|
||||||
android:tooltipText='@string/delete'
|
|
||||||
android:src="@drawable/delete_30dp"
|
android:src="@drawable/delete_30dp"
|
||||||
|
android:tooltipText='@string/delete'
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/savePhotoButton"
|
app:layout_constraintStart_toEndOf="@+id/savePhotoButton"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -79,8 +131,8 @@
|
|||||||
android:layout_marginStart="30dp"
|
android:layout_marginStart="30dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/edit"
|
android:contentDescription="@string/edit"
|
||||||
android:tooltipText='@string/edit'
|
|
||||||
android:src="@drawable/ic_baseline_edit_30"
|
android:src="@drawable/ic_baseline_edit_30"
|
||||||
|
android:tooltipText='@string/edit'
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/removePhotoButton"
|
app:layout_constraintStart_toEndOf="@+id/removePhotoButton"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -92,8 +144,8 @@
|
|||||||
android:layout_marginEnd="30dp"
|
android:layout_marginEnd="30dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/add_photo"
|
android:contentDescription="@string/add_photo"
|
||||||
android:tooltipText='@string/add_photo'
|
|
||||||
android:src="@drawable/add_photo_button"
|
android:src="@drawable/add_photo_button"
|
||||||
|
android:tooltipText='@string/add_photo'
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
android:theme="?attr/actionBarTheme"
|
android:theme="?attr/actionBarTheme"
|
||||||
|
app:titleTextColor="?attr/colorOnSecondaryContainer"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -76,6 +78,7 @@
|
|||||||
app:layout_constraintHorizontal_bias="0.498"
|
app:layout_constraintHorizontal_bias="0.498"
|
||||||
app:layout_constraintStart_toStartOf="@id/upload_error_text_view"
|
app:layout_constraintStart_toStartOf="@id/upload_error_text_view"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/upload_error_text_explanation" />
|
app:layout_constraintTop_toBottomOf="@+id/upload_error_text_explanation" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +106,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/post_creation_send_button"
|
android:id="@+id/post_submission_send_button"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
@ -182,4 +185,84 @@
|
|||||||
app:layout_constraintStart_toEndOf="@+id/nsfwSwitch"
|
app:layout_constraintStart_toEndOf="@+id/nsfwSwitch"
|
||||||
app:layout_constraintTop_toTopOf="@+id/nsfwSwitch" />
|
app:layout_constraintTop_toTopOf="@+id/nsfwSwitch" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/storyOptions"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/top_bar"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/story_duration_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="@string/story_duration"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
android:id="@+id/story_duration_slider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:stepSize="1.0"
|
||||||
|
android:valueFrom="3.0"
|
||||||
|
android:valueTo="15.0"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/story_duration_title" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/storyRepliesSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/story_duration_slider"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/story_duration_slider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyRepliesTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Allow replies"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/storyRepliesSwitch"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/storyRepliesSwitch"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/storyRepliesSwitch" />
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/storyReactionsSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/storyRepliesSwitch"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/storyRepliesSwitch" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/storyReactionsTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Allow reactions"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/storyReactionsSwitch"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/storyReactionsSwitch"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/storyReactionsSwitch"/>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,9 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:fillViewport="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent">
|
||||||
android:layout_gravity="center_horizontal">
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
<androidx.appcompat.widget.SearchView
|
||||||
android:id="@+id/search"
|
android:id="@+id/search"
|
||||||
@ -26,10 +29,10 @@
|
|||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/trendingCardView"
|
android:id="@+id/trendingCardView"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/hashtagsCardView"
|
app:layout_constraintBottom_toTopOf="@+id/hashtagsCardView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -45,28 +48,28 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
android:text="@string/trending_posts"
|
android:text="@string/trending_posts"
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
android:textColor="?attr/colorOnSecondaryContainer"
|
||||||
app:drawableLeftCompat="@drawable/baseline_auto_graph_24" />
|
app:drawableLeftCompat="@drawable/baseline_auto_graph_24" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/daily_trending"
|
android:text="@string/daily_trending"
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||||
android:layout_marginTop="8dp" />
|
android:textColor="?attr/colorOnSecondaryContainer" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/hashtagsCardView"
|
android:id="@+id/hashtagsCardView"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintBottom_toTopOf="@id/accountsCardView"
|
app:layout_constraintBottom_toTopOf="@id/accountsCardView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -82,33 +85,33 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
android:text="@string/trending_hashtags"
|
android:text="@string/trending_hashtags"
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
android:textColor="?attr/colorOnSecondaryContainer"
|
||||||
app:drawableStartCompat="@drawable/baseline_tag" />
|
app:drawableStartCompat="@drawable/baseline_tag" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/explore_hashtags"
|
android:text="@string/explore_hashtags"
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||||
android:layout_marginTop="8dp" />
|
android:textColor="?attr/colorOnSecondaryContainer" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/accountsCardView"
|
android:id="@+id/accountsCardView"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintTop_toBottomOf="@id/hashtagsCardView"
|
app:layout_constraintBottom_toTopOf="@id/discoverCardView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@id/discoverCardView">
|
app:layout_constraintTop_toBottomOf="@id/hashtagsCardView">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -119,33 +122,33 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
android:text="@string/popular_accounts"
|
android:text="@string/popular_accounts"
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
android:textColor="?attr/colorOnSecondaryContainer"
|
||||||
app:drawableStartCompat="@drawable/baseline_person_add" />
|
app:drawableStartCompat="@drawable/baseline_person_add" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/explore_accounts"
|
android:text="@string/explore_accounts"
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||||
android:layout_marginTop="8dp" />
|
android:textColor="?attr/colorOnSecondaryContainer" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/discoverCardView"
|
android:id="@+id/discoverCardView"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
app:cardBackgroundColor="?attr/colorSecondaryContainer"
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountsCardView"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
app:layout_constraintTop_toBottomOf="@id/accountsCardView">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -156,22 +159,23 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:drawablePadding="4dp"
|
||||||
android:text="@string/discover"
|
android:text="@string/discover"
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
android:textColor="?attr/colorOnSecondaryContainer"
|
||||||
app:drawableStartCompat="@drawable/explore_24dp" />
|
app:drawableStartCompat="@drawable/explore_24dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/explore_posts"
|
android:text="@string/explore_posts"
|
||||||
android:textColor="?attr/colorOnSecondaryContainer"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||||
android:layout_marginTop="8dp" />
|
android:textColor="?attr/colorOnSecondaryContainer" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</ScrollView>
|
@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingHorizontal="@dimen/spacing_xlarge"
|
|
||||||
android:paddingBottom="@dimen/spacing_xlarge">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginVertical="@dimen/spacing_xlarge"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:layout_editor_absoluteX="24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/copyright"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginVertical="@dimen/spacing_large"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:linksClickable="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
|
||||||
tools:layout_editor_absoluteX="24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/url"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/spacing_large"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:linksClickable="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/copyright"
|
|
||||||
tools:layout_editor_absoluteX="24dp" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/open_source_license_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fadeScrollbars="false"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,9 +1,27 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorSecondaryContainer"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/menu_settings" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
android:id="@+id/settings"
|
android:id="@+id/settings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</LinearLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
17
app/src/main/res/layout/story_carousel.xml
Normal file
17
app/src/main/res/layout/story_carousel.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/story_carousel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:layout_height="216dp"
|
||||||
|
tools:listitem="@layout/story_carousel_item"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="com.google.android.material.carousel.CarouselLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/search" />
|
50
app/src/main/res/layout/story_carousel_item.xml
Normal file
50
app/src/main/res/layout/story_carousel_item.xml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.carousel.MaskableFrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/carousel_item_container"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
tools:context="androidx.recyclerview.widget.RecyclerView"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
app:shapeAppearance="?attr/shapeAppearanceCornerExtraLarge">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/carousel_image_view"
|
||||||
|
android:contentDescription="@string/story_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/storyAuthorProfilePicture"
|
||||||
|
android:contentDescription="@string/profile_picture"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/username"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/username"
|
||||||
|
tools:srcCompat="@tools:sample/avatars[3]" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:text="pixeldroid" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.carousel.MaskableFrameLayout>
|
102
app/src/main/res/layout/story_carousel_self.xml
Normal file
102
app/src/main/res/layout/story_carousel_self.xml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.carousel.MaskableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/carousel_add_story"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:background="?attr/colorStoryImage"
|
||||||
|
app:shapeAppearance="?attr/shapeAppearanceCornerExtraLarge"
|
||||||
|
tools:context="androidx.recyclerview.widget.RecyclerView">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/carousel_image_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:contentDescription="@string/story_image"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/add_story"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/my_story"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/carousel_add_story_icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:contentDescription="@string/add_story"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/collection_add"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/textView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="?attr/colorOnStoryImage" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/add_story"
|
||||||
|
android:textColor="?attr/colorOnStoryImage"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/carousel_add_story_icon" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/my_story"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_story"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/my_story_icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:contentDescription="@string/my_story"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/story_play"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/my_story_text"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="?attr/colorOnStoryImage" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/my_story_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/my_story"
|
||||||
|
android:textColor="?attr/colorOnStoryImage"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/my_story_icon" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.carousel.MaskableFrameLayout>
|
3
app/src/main/res/raw/keep.xml
Normal file
3
app/src/main/res/raw/keep.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:keep="@drawable/mascot" />
|
1
app/src/main/res/resources.properties
Normal file
1
app/src/main/res/resources.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
unqualifiedResLocale=en-US
|
@ -52,7 +52,6 @@
|
|||||||
<string name="comment_posted">التعليق: تم نشر%1$s!</string>
|
<string name="comment_posted">التعليق: تم نشر%1$s!</string>
|
||||||
<string name="comment_error">خطأ في التعليق!</string>
|
<string name="comment_error">خطأ في التعليق!</string>
|
||||||
<string name="share_image">مشاركة الصورة</string>
|
<string name="share_image">مشاركة الصورة</string>
|
||||||
<string name="write_permission_share_pic">يجب عليك منح تصريح للكتابة قصد مشاركة الصور!</string>
|
|
||||||
<string name="write_permission_download_pic">تحتاج إلى منح إذن الكتابة لتنزيل الصور!</string>
|
<string name="write_permission_download_pic">تحتاج إلى منح إذن الكتابة لتنزيل الصور!</string>
|
||||||
<string name="empty_comment">لا يجب ان يكون التعليق فارغًا!</string>
|
<string name="empty_comment">لا يجب ان يكون التعليق فارغًا!</string>
|
||||||
<string name="posted_on">نُشِر في %1$s</string>
|
<string name="posted_on">نُشِر في %1$s</string>
|
||||||
|
@ -60,7 +60,6 @@
|
|||||||
<string name="comment_posted">Comentari: %1$s publicat!</string>
|
<string name="comment_posted">Comentari: %1$s publicat!</string>
|
||||||
<string name="comment_error">Error de comentari!</string>
|
<string name="comment_error">Error de comentari!</string>
|
||||||
<string name="share_image">Compartir imatge</string>
|
<string name="share_image">Compartir imatge</string>
|
||||||
<string name="write_permission_share_pic">Necessites concedir permís d’escriptura per compartir imatges!</string>
|
|
||||||
<string name="write_permission_download_pic">Has de concedir permís d’escriptura per baixar imatges!</string>
|
<string name="write_permission_download_pic">Has de concedir permís d’escriptura per baixar imatges!</string>
|
||||||
<string name="empty_comment">El comentari no ha de estar buit!</string>
|
<string name="empty_comment">El comentari no ha de estar buit!</string>
|
||||||
<string name="posted_on">Publica\'t el %1$s</string>
|
<string name="posted_on">Publica\'t el %1$s</string>
|
||||||
|
@ -69,7 +69,6 @@
|
|||||||
<string name="posted_on">Zveřejněno na %1$s</string>
|
<string name="posted_on">Zveřejněno na %1$s</string>
|
||||||
<string name="NoCommentsToShow">U tohoto příspěvku nejsou žádné komentáře…</string>
|
<string name="NoCommentsToShow">U tohoto příspěvku nejsou žádné komentáře…</string>
|
||||||
<string name="empty_comment">Komentář nesmí být prázdný!</string>
|
<string name="empty_comment">Komentář nesmí být prázdný!</string>
|
||||||
<string name="write_permission_share_pic">Pro sdílení obrázků musíte udělit práva k zápisu!</string>
|
|
||||||
<string name="share_image">Sdílet obrázek</string>
|
<string name="share_image">Sdílet obrázek</string>
|
||||||
<string name="comment_error">Chyba komentáře!</string>
|
<string name="comment_error">Chyba komentáře!</string>
|
||||||
<string name="comment_posted">Komentář: %1$s zveřejněn!</string>
|
<string name="comment_posted">Komentář: %1$s zveřejněn!</string>
|
||||||
|
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