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:
Wv5twkFEKh54vo4tta9yu7dHa3 2020-04-01 19:38:21 +02:00 committed by GitHub
parent bf808725db
commit 61cdb1118b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 563 additions and 551 deletions

34
.cirrus.yml Normal file
View File

@ -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

34
.gitlab-ci.yml Normal file
View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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()))
}
}

View File

@ -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()))
}
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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()

View File

@ -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())
}
})
}
}

View File

@ -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())
}
})
}
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
)
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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())
}
}

View File

@ -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