WIP: Loading (#76)
* Add loading indications, refactor fragments into common parent class * Add cirrus * actually add cirrus * Add timeouts * Fix test rules * Add gitlab ci file to tes * move tests to MockedServer to not have infinite waits by Espresso * Update README for gitlab badge
This commit is contained in:
parent
bf808725db
commit
61cdb1118b
|
@ -0,0 +1,34 @@
|
|||
connected_check_task:
|
||||
name: Run Android instrumented tests
|
||||
env:
|
||||
API_LEVEL: 23
|
||||
TARGET: default
|
||||
ARCH: x86
|
||||
CC_TEST_REPORTER_ID: ENCRYPTED[!b71004b17b92e8fb7a3fecc3e2a9cc28c4e5f07f55e2f20cdfc641c57487cd21c7df6e7930318f8d87bc4675e63b260d!]
|
||||
container:
|
||||
image: reactivecircus/android-emulator-23:latest
|
||||
kvm: true
|
||||
cpu: 8
|
||||
memory: 16G
|
||||
create_device_script:
|
||||
echo no | avdmanager create avd --force --name "api-${API_LEVEL}" --abi "${TARGET}/${ARCH}" --package "system-images;android-${API_LEVEL};${TARGET};${ARCH}"
|
||||
start_emulator_background_script:
|
||||
$ANDROID_HOME/emulator/emulator -avd "api-${API_LEVEL}" -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
|
||||
wait_for_emulator_script:
|
||||
- chmod +x android-wait-for-emulator.sh
|
||||
- ./android-wait-for-emulator.sh
|
||||
disable_animations_script: |
|
||||
adb shell settings put global window_animation_scale 0.0
|
||||
adb shell settings put global transition_animation_scale 0.0
|
||||
adb shell settings put global animator_duration_scale 0.0
|
||||
prepare_codeclimate_script:
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
run_instrumented_tests_script:
|
||||
./gradlew build connectedCheck jacocoTestReport
|
||||
report_codeclimate_script:
|
||||
# Report test coverage to Code Climate
|
||||
- export JACOCO_SOURCE_PATH=app/src/main/java/
|
||||
- ./cc-test-reporter format-coverage ./app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml --input-type jacoco
|
||||
- ./cc-test-reporter upload-coverage
|
|
@ -0,0 +1,34 @@
|
|||
image: reactivecircus/android-emulator-23:latest
|
||||
|
||||
variables:
|
||||
API_LEVEL: "23"
|
||||
API_LEVEL: "23"
|
||||
ARCH: "x86"
|
||||
TARGET: "default"
|
||||
#CC_TEST_REPORTER_ID defined in GitLab CI settings
|
||||
|
||||
stages:
|
||||
- test
|
||||
- report
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- echo no | avdmanager create avd --force --name "api-${API_LEVEL}" --abi "${TARGET}/${ARCH}" --package "system-images;android-${API_LEVEL};${TARGET};${ARCH}"
|
||||
- $ANDROID_HOME/emulator/emulator -avd "api-${API_LEVEL}" -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none &
|
||||
- chmod +x android-wait-for-emulator.sh
|
||||
- ./android-wait-for-emulator.sh
|
||||
- adb shell settings put global window_animation_scale 0.0
|
||||
- adb shell settings put global transition_animation_scale 0.0
|
||||
- adb shell settings put global animator_duration_scale 0.0
|
||||
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
|
||||
- ./gradlew build connectedCheck jacocoTestReport
|
||||
|
||||
# Report test coverage to Code Climate
|
||||
- export JACOCO_SOURCE_PATH=app/src/main/java/
|
||||
- ./cc-test-reporter format-coverage ./app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml --input-type jacoco
|
||||
- ./cc-test-reporter upload-coverage
|
29
.travis.yml
29
.travis.yml
|
@ -1,29 +0,0 @@
|
|||
language: android
|
||||
android:
|
||||
components:
|
||||
# The BuildTools version used by your project (make sure it's exactly the same as in the build.gradle)
|
||||
- build-tools-29.0.3
|
||||
# The SDK version used to compile your project
|
||||
- android-29
|
||||
# The SDK version used by the system image
|
||||
- android-23
|
||||
# The system image, to run an emulator during the tests
|
||||
- sys-img-armeabi-v7a-android-23
|
||||
before_script:
|
||||
# Emulator Management: Create, Start and Wait
|
||||
- echo no | android create avd --force -n test -t android-23 --abi armeabi-v7a
|
||||
- export QEMU_AUDIO_DRV=none && emulator -avd test -no-window &
|
||||
- android-wait-for-emulator
|
||||
- adb shell input keyevent 82
|
||||
# This should be in the `before_script` entry
|
||||
# Set up Code Climate test reporter
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
script:
|
||||
- ./gradlew build connectedCheck jacocoTestReport
|
||||
after_script:
|
||||
# Report test coverage to Code Climate
|
||||
- export JACOCO_SOURCE_PATH=app/src/main/java/
|
||||
- ./cc-test-reporter format-coverage ./app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml --input-type jacoco
|
||||
- ./cc-test-reporter upload-coverage
|
|
@ -1,4 +1,4 @@
|
|||
# PixelDroid
|
||||
Software Development Project course, EPFL, Spring 2020
|
||||
|
||||
[![Build Status](https://travis-ci.org/H-PixelDroid/PixelDroid.svg?branch=master)](https://travis-ci.org/H-PixelDroid/PixelDroid) [![Maintainability](https://api.codeclimate.com/v1/badges/a4f1747dc60b96eb74df/maintainability)](https://codeclimate.com/github/H-PixelDroid/PixelDroid/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/a4f1747dc60b96eb74df/test_coverage)](https://codeclimate.com/github/H-PixelDroid/PixelDroid/test_coverage)
|
||||
[![Build Status](https://gitlab.com/Matttter/PixelDroid/badges/master/pipeline.svg)](https://gitlab.com/Matttter/PixelDroid/pipelines) [![Maintainability](https://api.codeclimate.com/v1/badges/a4f1747dc60b96eb74df/maintainability)](https://codeclimate.com/github/H-PixelDroid/PixelDroid/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/a4f1747dc60b96eb74df/test_coverage)](https://codeclimate.com/github/H-PixelDroid/PixelDroid/test_coverage)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Originally written by Ralf Kistner <ralf@embarkmobile.com>, but placed in the public domain
|
||||
|
||||
#set -x
|
||||
set +e
|
||||
|
||||
bootanim=""
|
||||
failcounter=0
|
||||
timeout_in_sec=600 # 10 minutes
|
||||
|
||||
until [[ "$bootanim" =~ "stopped" ]]; do
|
||||
bootanim=`adb -e shell getprop init.svc.bootanim 2>&1 &`
|
||||
if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline"
|
||||
|| "$bootanim" =~ "running" || "$bootanim" =~ "error: no emulators found" ]]; then
|
||||
let "failcounter += 1"
|
||||
echo "Waiting for emulator to start: $failcounter of $timeout_in_sec : status: $bootanim"
|
||||
if [[ $failcounter -ge timeout_in_sec ]]; then
|
||||
echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Emulator is ready"
|
|
@ -76,6 +76,11 @@ dependencies {
|
|||
exclude group: "com.android.support"
|
||||
}
|
||||
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
|
||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||
// Excludes the support library because it's already included by Glide.
|
||||
transitive = false
|
||||
}
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
testImplementation "com.github.tomakehurst:wiremock-jre8:2.26.3"
|
||||
|
|
|
@ -6,10 +6,8 @@ import com.h.pixeldroid.db.AppDatabase
|
|||
import com.h.pixeldroid.db.PostDao
|
||||
import com.h.pixeldroid.db.PostEntity
|
||||
import com.h.pixeldroid.utils.*
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.*
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.Calendar
|
||||
|
||||
|
@ -18,7 +16,8 @@ class AppDatabaseTest {
|
|||
private var postDao: PostDao? = null
|
||||
private var db: AppDatabase? = null
|
||||
private var postTest = PostEntity(1, "test", date= Calendar.getInstance().time)
|
||||
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
@Before
|
||||
fun setup() {
|
||||
AppDatabase.TEST_MODE = true
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.h.pixeldroid", appContext.packageName)
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.hamcrest.Matcher
|
|||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
|
@ -37,6 +38,8 @@ import org.junit.runner.RunWith
|
|||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LoginInstrumentedTest {
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
@get:Rule
|
||||
var activityRule: ActivityScenarioRule<LoginActivity>
|
||||
= ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
@ -63,6 +66,8 @@ class LoginInstrumentedTest {
|
|||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LoginCheckIntent {
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
@get:Rule
|
||||
val intentsTestRule = IntentsTestRule(LoginActivity::class.java)
|
||||
|
||||
|
@ -85,6 +90,8 @@ class LoginCheckIntent {
|
|||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AfterIntent {
|
||||
@get:Rule
|
||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
||||
|
||||
@get:Rule
|
||||
val rule = ActivityTestRule(LoginActivity::class.java)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,49 +0,0 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.contrib.DrawerMatchers.isClosed
|
||||
import androidx.test.espresso.contrib.NavigationViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SettingsTest {
|
||||
@get:Rule
|
||||
var activityRule: ActivityScenarioRule<MainActivity>
|
||||
= ActivityScenarioRule(MainActivity::class.java)
|
||||
|
||||
@Before
|
||||
fun before(){
|
||||
val preferences = InstrumentationRegistry.getInstrumentation()
|
||||
.targetContext.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
|
||||
preferences.edit().putString("accessToken", "azerty").apply()
|
||||
preferences.edit().putString("domain", "http://localhost").apply()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDrawerSettingsButton() {
|
||||
// Open Drawer to click on navigation.
|
||||
onView(withId(R.id.drawer_layout))
|
||||
.check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
|
||||
.perform(DrawerActions.open()); // Open Drawer
|
||||
|
||||
// Start the screen of your activity.
|
||||
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_settings))
|
||||
|
||||
// Check that settings activity was opened.
|
||||
onView(withText(R.string.signature_title)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.action.ViewActions.*
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.Matcher
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SwipeTest {
|
||||
@get:Rule
|
||||
var activityRule: ActivityScenarioRule<MainActivity>
|
||||
= ActivityScenarioRule(MainActivity::class.java)
|
||||
@Before
|
||||
fun before(){
|
||||
val preferences = getInstrumentation()
|
||||
.targetContext.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
|
||||
preferences.edit().putString("accessToken", "azerty").apply()
|
||||
preferences.edit().putString("domain", "http://localhost").apply()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun swipingLeftStopsAtProfile() {
|
||||
onView(withId(R.id.main_activity_main_linear_layout))
|
||||
.perform(swipeLeft()) // search
|
||||
.perform(swipeLeft()) // camera
|
||||
.perform(swipeLeft()) // notifications
|
||||
.perform(swipeLeft()) // profile
|
||||
.perform(swipeLeft()) // should stop at profile
|
||||
onView(withId(R.id.nbFollowersTextView)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun swipingRightStopsAtHomepage() {
|
||||
ActivityScenario.launch(MainActivity::class.java).onActivity {
|
||||
a -> a.findViewById<TabLayout>(R.id.tabs).getTabAt(4)?.select()
|
||||
} // go to the last tab
|
||||
onView(withId(R.id.main_activity_main_linear_layout))
|
||||
.perform(swipeRight()) // notifications
|
||||
.perform(swipeRight()) // camera
|
||||
.perform(swipeRight()) // search
|
||||
.perform(swipeRight()) // homepage
|
||||
.perform(swipeRight()) // should stop at homepage
|
||||
onView(withId(R.id.feedList)).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import android.content.Intent
|
|||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
|
@ -42,20 +43,26 @@ class LoginActivity : AppCompatActivity() {
|
|||
|
||||
val url = intent.data
|
||||
|
||||
//Check if the activity was started after the authentication
|
||||
if (url == null || !url.toString().startsWith("$OAUTH_SCHEME://$PACKAGE_ID")) return
|
||||
|
||||
loadingAnimation(true)
|
||||
|
||||
val code = url.getQueryParameter("code")
|
||||
authenticate(code)
|
||||
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
loadingAnimation(false)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
}
|
||||
|
||||
private fun onClickConnect() {
|
||||
|
||||
connect_instance_button.isEnabled = false
|
||||
|
||||
val normalizedDomain = normalizeDomain(editText.text.toString())
|
||||
|
||||
try{
|
||||
|
@ -64,6 +71,8 @@ class LoginActivity : AppCompatActivity() {
|
|||
return failedRegistration(getString(R.string.invalid_domain))
|
||||
}
|
||||
|
||||
loadingAnimation(true)
|
||||
|
||||
preferences.edit()
|
||||
.putString("domain", "https://$normalizedDomain")
|
||||
.apply()
|
||||
|
@ -125,7 +134,6 @@ class LoginActivity : AppCompatActivity() {
|
|||
return failedRegistration(getString(R.string.browser_launch_failed))
|
||||
}
|
||||
}
|
||||
connect_instance_button.isEnabled = true
|
||||
}
|
||||
|
||||
private fun authenticate(code: String?) {
|
||||
|
@ -169,8 +177,19 @@ class LoginActivity : AppCompatActivity() {
|
|||
|
||||
private fun failedRegistration(message: String =
|
||||
getString(R.string.registration_failed)){
|
||||
connect_instance_button.isEnabled = true
|
||||
loadingAnimation(false)
|
||||
editText.error = message
|
||||
}
|
||||
|
||||
private fun loadingAnimation(on: Boolean){
|
||||
if(on) {
|
||||
domainTextInputLayout.visibility = View.GONE
|
||||
progressLayout.visibility = View.VISIBLE
|
||||
}
|
||||
else {
|
||||
domainTextInputLayout.visibility = View.VISIBLE
|
||||
progressLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ import com.google.android.material.navigation.NavigationView
|
|||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.h.pixeldroid.fragments.CameraFragment
|
||||
import com.h.pixeldroid.fragments.HomeFragment
|
||||
import com.h.pixeldroid.fragments.feeds.HomeFragment
|
||||
import com.h.pixeldroid.fragments.MyProfileFragment
|
||||
import com.h.pixeldroid.fragments.NotificationsFragment
|
||||
import com.h.pixeldroid.fragments.feeds.NotificationsFragment
|
||||
|
||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
|
@ -44,10 +44,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||
val navigationView: NavigationView = findViewById(R.id.nav_view)
|
||||
navigationView.setNavigationItemSelectedListener(this)
|
||||
|
||||
val tabs = arrayOf(HomeFragment(),
|
||||
val tabs = arrayOf(
|
||||
HomeFragment(),
|
||||
Fragment(),
|
||||
CameraFragment(),
|
||||
NotificationsFragment(),
|
||||
NotificationsFragment(),
|
||||
MyProfileFragment())
|
||||
|
||||
setupTabs(tabs)
|
||||
|
|
|
@ -3,8 +3,8 @@ package com.h.pixeldroid
|
|||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.h.pixeldroid.fragments.PostFragment
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.models.Post.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status
|
||||
|
||||
|
||||
class PostActivity : AppCompatActivity() {
|
||||
|
@ -14,11 +14,11 @@ class PostActivity : AppCompatActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_post)
|
||||
|
||||
val post = intent.getSerializableExtra(POST_TAG) as Post
|
||||
val status = intent.getSerializableExtra(POST_TAG) as Status
|
||||
|
||||
postFragment = PostFragment()
|
||||
val arguments = Bundle()
|
||||
arguments.putSerializable(POST_TAG, post)
|
||||
arguments.putSerializable(POST_TAG, status)
|
||||
postFragment.arguments = arguments
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var feed : RecyclerView
|
||||
private lateinit var adapter : FeedRecyclerViewAdapter
|
||||
private lateinit var posts : List<Post>
|
||||
|
||||
fun setContent(newPosts : ArrayList<Post>) {
|
||||
posts = newPosts
|
||||
adapter.addPosts(posts)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
preferences = this.activity!!.getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
feed = view.findViewById(R.id.feedList)
|
||||
feed.layoutManager = LinearLayoutManager(context)
|
||||
adapter = FeedRecyclerViewAdapter(context!!)
|
||||
feed.adapter = adapter
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
val accessToken = preferences.getString("accessToken", "")
|
||||
var statuses: ArrayList<Status>? = null
|
||||
val newPosts = ArrayList<Post>()
|
||||
|
||||
pixelfedAPI.timelineHome("Bearer $accessToken")
|
||||
.enqueue(object : Callback<List<Status>> {
|
||||
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
|
||||
if (response.code() == 200) {
|
||||
statuses = response.body() as ArrayList<Status>?
|
||||
if(!statuses.isNullOrEmpty()) {
|
||||
for (status in statuses!!) {
|
||||
newPosts.add(Post(status))
|
||||
}
|
||||
setContent(newPosts)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Status>>, t: Throwable) {
|
||||
Log.e("HomeFragment", t.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.objects.Notification
|
||||
import kotlinx.android.synthetic.main.fragment_notifications_list.*
|
||||
import kotlinx.android.synthetic.main.fragment_notifications_list.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_notifications_list.view.swipeRefreshLayout
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
*/
|
||||
class NotificationsFragment : Fragment() {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
private lateinit var list : RecyclerView
|
||||
private lateinit var adapter : NotificationsRecyclerViewAdapter
|
||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
var notifications : List<Notification> = ArrayList()
|
||||
|
||||
private lateinit var pixelfedAPI: PixelfedAPI
|
||||
private var accessToken: String? = null
|
||||
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_notifications_list, container, false)
|
||||
preferences = activity!!.getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
|
||||
list = swipeRefreshLayout.list
|
||||
|
||||
// Set the adapter
|
||||
list.layoutManager = LinearLayoutManager(context)
|
||||
adapter = NotificationsRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
doRequest()
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
doRequest()
|
||||
}
|
||||
fun setContent(notifications : ArrayList<Notification>) {
|
||||
this.notifications = notifications
|
||||
adapter.initializeWith(notifications)
|
||||
}
|
||||
|
||||
private fun doRequest(){
|
||||
val call = pixelfedAPI.notifications("Bearer $accessToken", min_id="1")
|
||||
call.enqueue(object : Callback<List<Notification>> {
|
||||
override fun onResponse(call: Call<List<Notification>>, response: Response<List<Notification>>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!! as ArrayList<Notification>
|
||||
setContent(notifications)
|
||||
} else{
|
||||
Toast.makeText(context,"Something went wrong while refreshing", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Notification>>, t: Throwable) {
|
||||
Toast.makeText(context,"Could not get notifications", Toast.LENGTH_SHORT).show()
|
||||
Log.e("Notifications", t.toString())
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -6,8 +6,8 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.models.Post.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status
|
||||
|
||||
|
||||
class PostFragment : Fragment() {
|
||||
|
@ -16,9 +16,9 @@ class PostFragment : Fragment() {
|
|||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val post = arguments?.getSerializable(POST_TAG) as Post?
|
||||
val status = arguments?.getSerializable(POST_TAG) as Status?
|
||||
val root = inflater.inflate(R.layout.post_fragment, container, false)
|
||||
post?.setupPost(this, root)
|
||||
status?.setupPost(this, root)
|
||||
return root
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.h.pixeldroid.BuildConfig
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import kotlinx.android.synthetic.main.fragment_feed.*
|
||||
import kotlinx.android.synthetic.main.fragment_feed.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
open class FeedFragment<T, VH: RecyclerView.ViewHolder?>: Fragment() {
|
||||
|
||||
var content : List<T> = ArrayList()
|
||||
|
||||
protected var accessToken: String? = null
|
||||
protected lateinit var pixelfedAPI: PixelfedAPI
|
||||
protected lateinit var preferences: SharedPreferences
|
||||
|
||||
protected lateinit var list : RecyclerView
|
||||
protected lateinit var adapter : FeedsRecyclerViewAdapter<T, VH>
|
||||
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
|
||||
|
||||
protected fun doRequest(call: Call<List<T>>){
|
||||
call.enqueue(object : Callback<List<T>> {
|
||||
override fun onResponse(call: Call<List<T>>, response: Response<List<T>>) {
|
||||
if (response.code() == 200) {
|
||||
val notifications = response.body()!! as ArrayList<T>
|
||||
setContent(notifications)
|
||||
} else{
|
||||
Toast.makeText(context,"Something went wrong while loading", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
progressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<T>>, t: Throwable) {
|
||||
Toast.makeText(context,"Could not get feed", Toast.LENGTH_SHORT).show()
|
||||
Log.e("FeedFragment", t.toString())
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
protected fun setContent(content : ArrayList<T>) {
|
||||
this.content = content
|
||||
adapter.initializeWith(content)
|
||||
}
|
||||
|
||||
|
||||
protected fun onCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_feed, container, false)
|
||||
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout)
|
||||
list = swipeRefreshLayout.list
|
||||
// Set the adapter
|
||||
list.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
preferences = activity!!.getSharedPreferences(
|
||||
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
pixelfedAPI = PixelfedAPI.create("${preferences.getString("domain", "")}")
|
||||
accessToken = preferences.getString("accessToken", "")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FeedsRecyclerViewAdapter<T, VH : RecyclerView.ViewHolder?>: RecyclerView.Adapter<VH>() {
|
||||
|
||||
protected val feedContent: ArrayList<T> = arrayListOf()
|
||||
protected lateinit var context: Context
|
||||
|
||||
override fun getItemCount(): Int = feedContent.size
|
||||
|
||||
open fun initializeWith(content: List<T>){
|
||||
feedContent.clear()
|
||||
feedContent.addAll(content)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
|
||||
|
||||
class HomeFragment : FeedFragment<Status, HomeRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container)
|
||||
adapter = HomeRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
val call = pixelfedAPI.timelineHome("Bearer $accessToken")
|
||||
doRequest(call)
|
||||
}
|
||||
val call = pixelfedAPI.timelineHome("Bearer $accessToken")
|
||||
doRequest(call)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -10,29 +9,20 @@ import android.view.ViewGroup
|
|||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setDefaultImage
|
||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setImageViewFromURL
|
||||
import com.h.pixeldroid.utils.ImageConverter.Companion.setRoundImageFromURL
|
||||
import java.util.ArrayList
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a list of [Post]s
|
||||
* [RecyclerView.Adapter] that can display a list of Posts
|
||||
*/
|
||||
class FeedRecyclerViewAdapter(
|
||||
private val context : Context
|
||||
) : RecyclerView.Adapter<FeedRecyclerViewAdapter.ViewHolder>() {
|
||||
private val posts: ArrayList<Post> = ArrayList<Post>()
|
||||
|
||||
fun addPosts(newPosts : List<Post>) {
|
||||
val size = posts.size
|
||||
posts.addAll(newPosts)
|
||||
notifyItemRangeInserted(size, newPosts.size)
|
||||
}
|
||||
class HomeRecyclerViewAdapter() : FeedsRecyclerViewAdapter<Status, HomeRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.post_fragment, parent, false)
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.post_fragment, parent, false)
|
||||
context = view.context
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
|
@ -40,7 +30,7 @@ class FeedRecyclerViewAdapter(
|
|||
* Binds the different elements of the Post Model to the view holder
|
||||
*/
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val post = posts[position]
|
||||
val post = feedContent[position]
|
||||
val metrics = DisplayMetrics()
|
||||
|
||||
//Limit the height of the different images
|
||||
|
@ -72,7 +62,7 @@ class FeedRecyclerViewAdapter(
|
|||
holder.nshares.setTypeface(null, Typeface.BOLD)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = posts.size
|
||||
override fun getItemCount(): Int = feedContent.size
|
||||
|
||||
/**
|
||||
* Represents the posts that will be contained within the feed
|
|
@ -0,0 +1,44 @@
|
|||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.objects.Notification
|
||||
import kotlinx.android.synthetic.main.fragment_feed.*
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
*/
|
||||
class NotificationsFragment : FeedFragment<Notification, NotificationsRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
val view = super.onCreateView(inflater, container)
|
||||
|
||||
adapter = NotificationsRecyclerViewAdapter()
|
||||
list.adapter = adapter
|
||||
|
||||
|
||||
return view
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
val call = pixelfedAPI.notifications("Bearer $accessToken", min_id="1")
|
||||
doRequest(call)
|
||||
}
|
||||
val call = pixelfedAPI.notifications("Bearer $accessToken", min_id="1")
|
||||
doRequest(call)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.h.pixeldroid.fragments
|
||||
package com.h.pixeldroid.fragments.feeds
|
||||
|
||||
|
||||
import android.content.Context
|
||||
|
@ -16,17 +16,14 @@ import com.bumptech.glide.Glide
|
|||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.PostActivity
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.models.Post.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Status.Companion.POST_TAG
|
||||
import com.h.pixeldroid.objects.Notification
|
||||
import kotlinx.android.synthetic.main.fragment_notifications.view.*
|
||||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that can display a [Notification]
|
||||
*/
|
||||
class NotificationsRecyclerViewAdapter: RecyclerView.Adapter<NotificationsRecyclerViewAdapter.ViewHolder>() {
|
||||
private val notifications: ArrayList<Notification> = arrayListOf()
|
||||
private lateinit var context: Context
|
||||
class NotificationsRecyclerViewAdapter: FeedsRecyclerViewAdapter<Notification, NotificationsRecyclerViewAdapter.ViewHolder>() {
|
||||
|
||||
private val mOnClickListener: View.OnClickListener
|
||||
|
||||
|
@ -41,7 +38,7 @@ class NotificationsRecyclerViewAdapter: RecyclerView.Adapter<NotificationsRecycl
|
|||
when (notification.type){
|
||||
Notification.NotificationType.mention, Notification.NotificationType.favourite-> {
|
||||
intent = Intent(context, PostActivity::class.java)
|
||||
intent.putExtra(POST_TAG, Post(notification.status))
|
||||
intent.putExtra(POST_TAG, notification.status)
|
||||
}
|
||||
Notification.NotificationType.reblog-> {
|
||||
Toast.makeText(context,"Can't see shares yet, sorry!",Toast.LENGTH_SHORT).show()
|
||||
|
@ -55,14 +52,9 @@ class NotificationsRecyclerViewAdapter: RecyclerView.Adapter<NotificationsRecycl
|
|||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
fun initializeWith(newNotifications: List<Notification>){
|
||||
notifications.clear()
|
||||
notifications.addAll(newNotifications)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
fun addNotifications(newNotifications: List<Notification>){
|
||||
val oldSize = notifications.size
|
||||
notifications.addAll(newNotifications)
|
||||
val oldSize = feedContent.size
|
||||
feedContent.addAll(newNotifications)
|
||||
notifyItemRangeInserted(oldSize, newNotifications.size)
|
||||
}
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
|
@ -74,11 +66,11 @@ class NotificationsRecyclerViewAdapter: RecyclerView.Adapter<NotificationsRecycl
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val notification = notifications[position]
|
||||
val notification = feedContent[position]
|
||||
Glide.with(holder.mView).load(notification.account.avatar_static).apply(RequestOptions().circleCrop())
|
||||
.placeholder(R.drawable.ic_default_user).into(holder.avatar)
|
||||
|
||||
val previewUrl = notification.status?.media_attachments?.get(0)?.preview_url
|
||||
val previewUrl = notification.status?.media_attachments?.getOrNull(0)?.preview_url
|
||||
if(!previewUrl.isNullOrBlank()){
|
||||
Glide.with(holder.mView).load(previewUrl)
|
||||
.placeholder(R.drawable.ic_picture_fallback).into(holder.photoThumbnail)
|
||||
|
@ -125,7 +117,6 @@ class NotificationsRecyclerViewAdapter: RecyclerView.Adapter<NotificationsRecycl
|
|||
return Pair(context.getString(format), context.getDrawable(drawable))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = notifications.size
|
||||
|
||||
inner class ViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
|
||||
val notificationType: TextView = mView.notification_type
|
|
@ -1,86 +0,0 @@
|
|||
package com.h.pixeldroid.models
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import java.io.Serializable
|
||||
|
||||
class Post(private val status: Status?) : Serializable {
|
||||
companion object {
|
||||
const val POST_TAG = "postTag"
|
||||
const val POST_FRAG_TAG = "postFragTag"
|
||||
}
|
||||
|
||||
fun getPostUrl() : String? = status?.media_attachments?.get(0)?.url
|
||||
fun getProfilePicUrl() : String? = status?.account?.avatar
|
||||
|
||||
fun getDescription() : CharSequence {
|
||||
val description = status?.content as CharSequence
|
||||
if(description.isEmpty()) {
|
||||
return "No description"
|
||||
}
|
||||
return description
|
||||
}
|
||||
|
||||
fun getUsername() : CharSequence {
|
||||
var name = status?.account?.username
|
||||
if (name.isNullOrEmpty()) {
|
||||
name = status?.account?.display_name
|
||||
}
|
||||
return name!!
|
||||
}
|
||||
fun getUsernameDescription() : CharSequence {
|
||||
return status?.account?.display_name ?: ""
|
||||
}
|
||||
|
||||
fun getNLikes() : CharSequence {
|
||||
val nLikes : Int = status?.favourites_count ?: 0
|
||||
return "$nLikes Likes"
|
||||
}
|
||||
|
||||
fun getNShares() : CharSequence {
|
||||
val nShares : Int = status?.reblogs_count ?: 0
|
||||
return "$nShares Shares"
|
||||
}
|
||||
|
||||
fun setupPost(fragment: Fragment, rootView : View) {
|
||||
//Setup username as a button that opens the profile
|
||||
val username = rootView.findViewById<TextView>(R.id.username)
|
||||
username.text = this.getUsername()
|
||||
username.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val usernameDesc = rootView.findViewById<TextView>(R.id.usernameDesc)
|
||||
usernameDesc.text = this.getUsernameDescription()
|
||||
usernameDesc.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val description = rootView.findViewById<TextView>(R.id.description)
|
||||
description.text = this.getDescription()
|
||||
|
||||
val nlikes = rootView.findViewById<TextView>(R.id.nlikes)
|
||||
nlikes.text = this.getNLikes()
|
||||
nlikes.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val nshares = rootView.findViewById<TextView>(R.id.nshares)
|
||||
nshares.text = this.getNShares()
|
||||
nshares.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
//Setup post and profile images
|
||||
ImageConverter.setImageViewFromURL(
|
||||
fragment,
|
||||
getPostUrl(),
|
||||
rootView.findViewById(R.id.postPicture)
|
||||
)
|
||||
ImageConverter.setImageViewFromURL(
|
||||
fragment,
|
||||
getProfilePicUrl(),
|
||||
rootView.findViewById(R.id.profilePic)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,11 @@
|
|||
package com.h.pixeldroid.objects
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.utils.ImageConverter
|
||||
import java.io.Serializable
|
||||
|
||||
/*
|
||||
|
@ -43,6 +49,77 @@ data class Status(
|
|||
val pinned: Boolean
|
||||
) : Serializable
|
||||
{
|
||||
|
||||
companion object {
|
||||
const val POST_TAG = "postTag"
|
||||
const val POST_FRAG_TAG = "postFragTag"
|
||||
}
|
||||
|
||||
fun getPostUrl() : String? = media_attachments?.getOrNull(0)?.url
|
||||
fun getProfilePicUrl() : String? = account?.avatar
|
||||
|
||||
fun getDescription() : CharSequence {
|
||||
val description = content as CharSequence
|
||||
if(description.isEmpty()) {
|
||||
return "No description"
|
||||
}
|
||||
return description
|
||||
}
|
||||
|
||||
fun getUsername() : CharSequence {
|
||||
var name = account?.username
|
||||
if (name.isNullOrEmpty()) {
|
||||
name = account?.display_name
|
||||
}
|
||||
return name!!
|
||||
}
|
||||
fun getUsernameDescription() : CharSequence {
|
||||
return account?.display_name ?: ""
|
||||
}
|
||||
|
||||
fun getNLikes() : CharSequence {
|
||||
val nLikes : Int = favourites_count ?: 0
|
||||
return "$nLikes Likes"
|
||||
}
|
||||
|
||||
fun getNShares() : CharSequence {
|
||||
val nShares : Int = reblogs_count ?: 0
|
||||
return "$nShares Shares"
|
||||
}
|
||||
|
||||
fun setupPost(fragment: Fragment, rootView : View) {
|
||||
//Setup username as a button that opens the profile
|
||||
val username = rootView.findViewById<TextView>(R.id.username)
|
||||
username.text = this.getUsername()
|
||||
username.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val usernameDesc = rootView.findViewById<TextView>(R.id.usernameDesc)
|
||||
usernameDesc.text = this.getUsernameDescription()
|
||||
usernameDesc.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val description = rootView.findViewById<TextView>(R.id.description)
|
||||
description.text = this.getDescription()
|
||||
|
||||
val nlikes = rootView.findViewById<TextView>(R.id.nlikes)
|
||||
nlikes.text = this.getNLikes()
|
||||
nlikes.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
val nshares = rootView.findViewById<TextView>(R.id.nshares)
|
||||
nshares.text = this.getNShares()
|
||||
nshares.setTypeface(null, Typeface.BOLD)
|
||||
|
||||
//Setup post and profile images
|
||||
ImageConverter.setImageViewFromURL(
|
||||
fragment,
|
||||
getPostUrl(),
|
||||
rootView.findViewById(R.id.postPicture)
|
||||
)
|
||||
ImageConverter.setImageViewFromURL(
|
||||
fragment,
|
||||
getProfilePicUrl(),
|
||||
rootView.findViewById(R.id.profilePic)
|
||||
)
|
||||
}
|
||||
enum class Visibility : Serializable {
|
||||
public, unlisted, private, direct
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import androidx.fragment.app.FragmentActivity
|
|||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.h.pixeldroid.R
|
||||
import com.h.pixeldroid.models.Post
|
||||
|
||||
class ImageConverter {
|
||||
companion object {
|
||||
|
|
|
@ -6,34 +6,58 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".LoginActivity">
|
||||
|
||||
<Button
|
||||
android:id="@+id/connect_instance_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="36dp"
|
||||
android:text="Connect"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/domainTextInputLayout" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/domainTextInputLayout"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Domain of your instance"
|
||||
app:errorEnabled="true"
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.40">
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textUri" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/domainTextInputLayout"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:hint="Domain of your instance"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textUri" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/connect_instance_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="36dp"
|
||||
android:text="Connect" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/progressLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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_height="match_parent">
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layoutManager="LinearLayoutManager" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -4,7 +4,12 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".fragments.HomeFragment">
|
||||
tools:context=".fragments.feeds.HomeFragment">
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/feedList"
|
||||
|
@ -16,10 +21,11 @@
|
|||
app:layoutManager="LinearLayoutManager"
|
||||
tools:listitem="@layout/post_fragment" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"/>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</FrameLayout>
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/list"
|
||||
android:name="com.h.pixeldroid.fragments.NotificationsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".fragments.NotificationsFragment"
|
||||
tools:listitem="@layout/fragment_notifications" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
@ -1,6 +1,5 @@
|
|||
package com.h.pixeldroid
|
||||
|
||||
import com.h.pixeldroid.models.Post
|
||||
import com.h.pixeldroid.objects.*
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
@ -26,45 +25,42 @@ class PostUnitTest {
|
|||
tags= listOf(Tag(name="hiking", url="https://pixelfed.de/discover/tags/hiking", history=null), Tag(name="nature", url="https://pixelfed.de/discover/tags/nature", history=null), Tag(name="rotavicentina", url="https://pixelfed.de/discover/tags/rotavicentina", history=null)),
|
||||
emojis= emptyList(), reblogs_count=0, favourites_count=0, replies_count=0, url="https://pixelfed.de/p/Miike/140364967936397312",
|
||||
in_reply_to_id=null, in_reply_to_account=null, reblog=null, poll=null, card=null, language=null, text=null, favourited=false, reblogged=false, muted=false, bookmarked=false, pinned=false)
|
||||
private val post = Post(status)
|
||||
|
||||
@Test
|
||||
fun getPostUrlReturnsAValidURL() = Assert.assertNotNull(post.getPostUrl())
|
||||
fun getPostUrlReturnsAValidURL() = Assert.assertNotNull(status.getPostUrl())
|
||||
|
||||
@Test
|
||||
fun getProfilePicUrlReturnsAValidURL() = Assert.assertNotNull(post.getProfilePicUrl())
|
||||
fun getProfilePicUrlReturnsAValidURL() = Assert.assertNotNull(status.getProfilePicUrl())
|
||||
|
||||
@Test
|
||||
fun getDescriptionReturnsDefaultIfEmpty() {
|
||||
val emptyDescStatus = status.copy(content = "")
|
||||
val emptyDescPost = Post(emptyDescStatus)
|
||||
Assert.assertEquals( "No description", emptyDescPost.getDescription())
|
||||
Assert.assertEquals( "No description", emptyDescStatus.getDescription())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDescriptionReturnsAValidDesc() = Assert.assertNotNull(post.getDescription())
|
||||
fun getDescriptionReturnsAValidDesc() = Assert.assertNotNull(status.getDescription())
|
||||
|
||||
@Test
|
||||
fun getDescriptionReturnsACorrectDesc() = Assert.assertEquals(status.content, post.getDescription())
|
||||
fun getDescriptionReturnsACorrectDesc() = Assert.assertEquals(status.content, status.getDescription())
|
||||
|
||||
@Test
|
||||
fun getUsernameReturnsACorrectName() = Assert.assertEquals(status.account.username, post.getUsername())
|
||||
fun getUsernameReturnsACorrectName() = Assert.assertEquals(status.account.username, status.getUsername())
|
||||
|
||||
@Test
|
||||
fun getUsernameReturnsOtherNameIfUsernameIsNull() {
|
||||
val emptyDescStatus = status.copy(account = status.account.copy(username = ""))
|
||||
val spePost = Post(emptyDescStatus)
|
||||
Assert.assertEquals(status.account.display_name, spePost.getUsername())
|
||||
Assert.assertEquals(status.account.display_name, emptyDescStatus.getUsername())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getNLikesReturnsCorrectFormat() {
|
||||
Assert.assertEquals("${status.favourites_count} Likes", post.getNLikes())
|
||||
Assert.assertEquals("${status.favourites_count} Likes", status.getNLikes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getNSharesReturnsCorrectFormat() {
|
||||
Assert.assertEquals("${status.reblogs_count} Shares", post.getNShares())
|
||||
Assert.assertEquals("${status.reblogs_count} Shares", status.getNShares())
|
||||
}
|
||||
|
||||
}
|
|
@ -19,3 +19,6 @@ android.useAndroidX=true
|
|||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
org.gradle.daemon=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
|
Loading…
Reference in New Issue