My profile (#46)

* Layout of my-profile page

* implements swipe motion

add a new class to implement swipe motion
add the swipe right from home page to display settings
passed the homepage in a fragment

* transform profile activity into fragment

transformed profile activity and layout into fragment
linked it with a swipe motion

* Implement swipeable tabs

* Ask for login on first start, add API endpoints, change profile to show the user's profile

* Added constraint view

* Layout of my-profile page

* Added constraint view

* Rebase myProfile with login-flow

* Add tests

* delete test for now

* Adapt test to changes (no more profile from drawer)

* Add unit test for api

* Add test for profile, refactor to allow testing, add exception to security policy to allow tests

* Adapt test to new situation

* Fix typo due to change

* refactor somewhat

* Added myProfile fragment to main activity + edit link

* Layout of my-profile page

* Added constraint view

* Layout of my-profile page

* Added constraint view

* Rebase myProfile with login-flow

* Added myProfile fragment to main activity + edit link

* Working tests for MyProfile

Co-authored-by: Ulysse Widmer <ulysse.widmer@epfl.ch>
This commit is contained in:
mjaillot 2020-03-13 12:17:17 +01:00 committed by GitHub
parent 20c5ff4ee0
commit 8802bf9905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 171 additions and 162 deletions

View File

@ -1,6 +1,7 @@
package com.h.pixeldroid
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri
@ -17,6 +18,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anyOf
@ -90,6 +92,9 @@ class AfterIntent {
@Before
fun setup() {
val preferences = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences("com.h.pixeldroid.pref", Context.MODE_PRIVATE)
preferences.edit().putString("domain", "http://localhost").apply()
val intent = Intent(ACTION_VIEW, Uri.parse("oauth2redirect://com.h.pixeldroid?code=sdfdqsf"))
launchedActivity = rule.launchActivity(intent)
}

View File

@ -1,22 +1,17 @@
package com.h.pixeldroid
import android.content.Context
import androidx.core.view.get
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.h.pixeldroid.objects.Account
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -24,7 +19,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ProfileTest {
class MyProfileTest {
private val accountJson = "{\n" +
" \"id\": \"1450\",\n" +
" \"username\": \"deerbard_photo\",\n" +
@ -52,7 +47,6 @@ class ProfileTest {
var activityRule: ActivityScenarioRule<MainActivity>
= ActivityScenarioRule(MainActivity::class.java)
@Before
fun before(){
val server = MockWebServer()
@ -71,9 +65,7 @@ class ProfileTest {
ViewActions.swipeLeft()
).perform(ViewActions.swipeLeft())
Thread.sleep(1000)
onView(withId(R.id.followers)).check(matches(withText("Followers")))
onView(withId(R.id.accountName)).check(matches(withText("deerbard_photo")))
onView(withId(R.id.nbFollowersTextView)).check(matches(withText("68\nFollowers")))
onView(withId(R.id.accountNameTextView)).check(matches(withText("deerbard_photo")))
}
}
}

View File

@ -34,7 +34,6 @@ class SwipeTest {
@Test
fun swipingRightOnHomepageShowsSettings() {
onView(withId(R.id.view_pager)).perform(swipeLeft()).perform(swipeLeft()).perform(swipeLeft()).perform(swipeLeft())
onView(withId(R.id.nbFollowers)).check(matches(isDisplayed()))
onView(withId(R.id.nbFollowersTextView)).check(matches(isDisplayed()))
}
}

View File

@ -17,7 +17,8 @@ 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.HomeFragment
import com.h.pixeldroid.fragments.ProfileFragment
import com.h.pixeldroid.fragments.MyProfileFragment
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
@ -26,7 +27,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private lateinit var tabLayout: TabLayout
private lateinit var preferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
@ -45,7 +45,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val navigationView: NavigationView = findViewById(R.id.nav_view)
navigationView.setNavigationItemSelectedListener(this)
val tabs = arrayOf(HomeFragment(), Fragment(), Fragment(), Fragment(), ProfileFragment())
val tabs = arrayOf(HomeFragment(), Fragment(), Fragment(), Fragment(), MyProfileFragment())
setupTabs(tabs)
}

View File

@ -1,15 +1,17 @@
package com.h.pixeldroid.fragments
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
@ -17,12 +19,12 @@ import com.h.pixeldroid.BuildConfig
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Status
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class ProfileFragment : Fragment() {
class MyProfileFragment : Fragment() {
private lateinit var preferences: SharedPreferences
override fun onCreateView(
@ -32,7 +34,12 @@ class ProfileFragment : Fragment() {
preferences = this.activity!!.getSharedPreferences(
"${BuildConfig.APPLICATION_ID}.pref", Context.MODE_PRIVATE
)
return inflater.inflate(R.layout.fragment_profile, container, false)
val view = inflater.inflate(R.layout.fragment_my_profile, container, false)
val editButton: Button = view.findViewById(R.id.editButton)
editButton.setOnClickListener((View.OnClickListener { onClickEditButton() }))
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -44,11 +51,10 @@ class ProfileFragment : Fragment() {
pixelfedAPI.verifyCredentials("Bearer $accessToken")
.enqueue(object : Callback<Account> {
override fun onResponse(call: Call<Account>, response: Response<Account>) {
if (response.code() == 200) {
if(response.code() == 200) {
val account = response.body()!!
setContent(view, account)
}
}
@ -60,31 +66,42 @@ class ProfileFragment : Fragment() {
private fun setContent(view: View, account: Account) {
// ImageView : profile picture
val profilePicture = view.findViewById<ImageView>(R.id.profilePicture)
val profilePicture = view.findViewById<ImageView>(R.id.profilePictureImageView)
Glide.with(view.context).load(account.avatar).into(profilePicture)
// TextView : description / bio
val description = view.findViewById<TextView>(R.id.description)
val description = view.findViewById<TextView>(R.id.descriptionTextView)
description.text = account.note
// TextView : account name
val accountName = view.findViewById<TextView>(R.id.accountName)
val accountName = view.findViewById<TextView>(R.id.accountNameTextView)
accountName.text = account.username
// TextView : number of posts
val nbPosts = view.findViewById<TextView>(R.id.nbPosts)
nbPosts.text = account.statuses_count.toString()
val nbPosts = view.findViewById<TextView>(R.id.nbPostsTextView)
nbPosts.text = account.statuses_count.toString() + "\nPosts"
nbPosts.setTypeface(null, Typeface.BOLD)
// TextView : number of followers
val nbFollowers = view.findViewById<TextView>(R.id.nbFollowers)
nbFollowers.text = account.followers_count.toString()
val nbFollowers = view.findViewById<TextView>(R.id.nbFollowersTextView)
nbFollowers.text = account.followers_count.toString() + "\nFollowers"
nbFollowers.setTypeface(null, Typeface.BOLD)
// TextView : number of following
val nbFollowing = view.findViewById<TextView>(R.id.nbFollowing)
nbFollowing.text = account.following_count.toString()
val nbFollowing = view.findViewById<TextView>(R.id.nbFollowingTextView)
nbFollowing.text = account.following_count.toString() + "\nFollowing"
nbFollowing.setTypeface(null, Typeface.BOLD)
}
private fun onClickEditButton() {
val url = "${preferences.getString("domain", "")}/settings/home"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if(activity != null && browserIntent.resolveActivity(activity!!.packageManager) != null) {
startActivity(browserIntent)
} else {
val text = "Cannot open this link"
Log.e("ProfileFragment", text)
}
}
}

View File

@ -9,6 +9,7 @@
tools:openDrawer="start"
tools:context="com.h.pixeldroid.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.MyProfileFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/profilePictureImageView"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="230dp"
android:layout_height="120dp"
android:layout_marginTop="24dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/profilePictureImageView"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/nbPostsTextView"
android:layout_width="15dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="-\nPosts" />
<TextView
android:id="@+id/nbFollowersTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowers" />
<TextView
android:id="@+id/nbFollowingTextView"
android:layout_width="15dp"
android:layout_height="120dp"
android:layout_weight="1"
android:gravity="center"
android:text="-\nFollowing" />
</LinearLayout>
<TextView
android:id="@+id/accountNameTextView"
android:layout_width="156dp"
android:layout_height="22dp"
android:layout_marginTop="15dp"
android:text="No Username"
app:layout_constraintStart_toStartOf="@+id/profilePictureImageView"
app:layout_constraintTop_toBottomOf="@+id/profilePictureImageView" />
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="348dp"
android:layout_height="85dp"
android:layout_marginTop="14dp"
android:autoSizeMaxTextSize="300sp"
android:autoSizeMinTextSize="2sp"
android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="@+id/accountNameTextView"
app:layout_constraintTop_toBottomOf="@+id/accountNameTextView" />
<Button
android:id="@+id/editButton"
android:layout_width="350dp"
android:layout_height="37dp"
android:layout_marginTop="14dp"
android:gravity="center"
android:text="Edit profile"
android:background="@color/colorPrimary"
android:textColor="@color/cardview_light_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/descriptionTextView" />
<LinearLayout
android:layout_width="409dp"
android:layout_height="50dp"
android:layout_marginTop="340dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/postsButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@color/cardview_shadow_end_color"
android:gravity="right"
app:srcCompat="@android:drawable/ic_dialog_dialer" />
<ImageButton
android:id="@+id/collectionButton"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@color/cardview_shadow_end_color"
android:gravity="left"
app:srcCompat="@android:drawable/ic_menu_gallery" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/profile_main_container"
tools:context=".fragments.ProfileFragment">
<ImageView
android:id="@+id/profilePicture"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/nbFollowing"
android:layout_width="38dp"
android:layout_height="26dp"
android:layout_marginTop="52dp"
android:layout_marginEnd="36dp"
android:text="N/A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/nbPosts"
android:layout_width="39dp"
android:layout_height="27dp"
android:layout_marginStart="152dp"
android:layout_marginTop="52dp"
android:text="N/A"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/posts"
android:layout_width="40dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:text="Posts"
app:layout_constraintEnd_toEndOf="@+id/nbPosts"
app:layout_constraintStart_toStartOf="@+id/nbPosts"
app:layout_constraintTop_toBottomOf="@+id/nbPosts" />
<TextView
android:id="@+id/followers"
android:layout_width="65dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:text="Followers"
app:layout_constraintEnd_toEndOf="@+id/nbFollowers"
app:layout_constraintStart_toStartOf="@+id/nbFollowers"
app:layout_constraintTop_toBottomOf="@+id/nbFollowers" />
<TextView
android:id="@+id/following"
android:layout_width="65dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:text="Following"
app:layout_constraintEnd_toEndOf="@+id/nbFollowing"
app:layout_constraintStart_toStartOf="@+id/nbFollowing"
app:layout_constraintTop_toBottomOf="@+id/nbFollowing" />
<TextView
android:id="@+id/nbFollowers"
android:layout_width="39dp"
android:layout_height="27dp"
android:layout_marginTop="52dp"
android:text="N/A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.669"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/accountName"
android:layout_width="156dp"
android:layout_height="22dp"
android:layout_marginTop="15dp"
android:text="@string/no_username"
app:layout_constraintStart_toStartOf="@+id/profilePicture"
app:layout_constraintTop_toBottomOf="@+id/profilePicture" />
<TextView
android:id="@+id/description"
android:layout_width="348dp"
android:layout_height="85dp"
android:layout_marginTop="14dp"
app:layout_constraintStart_toStartOf="@+id/accountName"
app:layout_constraintTop_toBottomOf="@+id/accountName" />
<Button
android:id="@+id/followButton"
android:layout_width="150dp"
android:layout_height="38dp"
android:layout_marginTop="14dp"
android:text="Follow"
app:layout_constraintStart_toStartOf="@+id/description"
app:layout_constraintTop_toBottomOf="@+id/description" />
<ImageButton
android:id="@+id/postsButton"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginStart="152dp"
android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/followButton"
app:srcCompat="@android:drawable/ic_dialog_dialer" />
<ImageButton
android:id="@+id/collectionButton"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/postsButton"
app:layout_constraintTop_toTopOf="@+id/postsButton"
app:srcCompat="@android:drawable/ic_menu_gallery" />
</androidx.constraintlayout.widget.ConstraintLayout>