Implements swipe motion & fragmentalize (#41)

* 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

* refactor settings drawer tests

* added more tests and refactored some files

* added animations to the sliding of the swipe motion
This commit is contained in:
Ulysse Widmer 2020-03-13 06:23:25 +09:00 committed by GitHub
parent f124af3800
commit de314dc9de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 435 additions and 115 deletions

View File

@ -71,6 +71,7 @@ dependencies {
}
implementation "com.github.bumptech.glide:okhttp-integration:4.11.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation "com.github.tomakehurst:wiremock-jre8:2.26.3"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation 'junit:junit:4.13'

View File

@ -0,0 +1,25 @@
package com.h.pixeldroid
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BottomMenuTest {
@get:Rule
var activityRule: ActivityScenarioRule<MainActivity>
= ActivityScenarioRule(MainActivity::class.java)
@Test
fun testFollowersTextView() {
onView(withId(R.id.activity_main_account_btn)).perform(click())
onView(withId(R.id.profile_main_container)).check(matches(isDisplayed()))
}
}

View File

@ -2,12 +2,10 @@ 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
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*

View File

@ -12,11 +12,15 @@ import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
import androidx.test.espresso.intent.matcher.IntentMatchers.hasDataString
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.espresso.matcher.ViewMatchers.hasErrorText
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.rule.ActivityTestRule
import org.hamcrest.CoreMatchers.*
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anyOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Matcher
import org.junit.Before
import org.junit.Rule
@ -39,26 +43,27 @@ class LoginInstrumentedTest {
fun clickConnect() {
onView(withId(R.id.connect_instance_button)).check(matches(withText("Connect")))
}
@Test
fun invalidURL() {
onView(withId(R.id.editText)).perform(ViewActions.replaceText("/jdi"), ViewActions.closeSoftKeyboard())
onView(withId(R.id.connect_instance_button)).perform(click())
onView(withId(R.id.editText)).check(matches(hasErrorText("Invalid domain")))
}
@Test
fun notPixelfedInstance() {
onView(withId(R.id.editText)).perform(ViewActions.replaceText("localhost"), ViewActions.closeSoftKeyboard())
onView(withId(R.id.connect_instance_button)).perform(click())
onView(withId(R.id.editText)).check(matches(hasErrorText("Could not register the application with this server")))
}
}
@RunWith(AndroidJUnit4::class)
class LoginCheckIntent {
@get:Rule
val intentsTestRule = IntentsTestRule(LoginActivity::class.java)
@Test
fun launchesIntent() {
val expectedIntent: Matcher<Intent> = allOf(
@ -75,6 +80,7 @@ class LoginCheckIntent {
}
}
@RunWith(AndroidJUnit4::class)
class AfterIntent {
@ -87,6 +93,7 @@ class AfterIntent {
val intent = Intent(ACTION_VIEW, Uri.parse("oauth2redirect://com.h.pixeldroid?code=sdfdqsf"))
launchedActivity = rule.launchActivity(intent)
}
@Test
fun usesIntent() {

View File

@ -3,15 +3,16 @@ package com.h.pixeldroid
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
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 org.hamcrest.Matchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.lang.Thread.sleep
@RunWith(AndroidJUnit4::class)
class ProfileTest {
@ -19,10 +20,14 @@ class ProfileTest {
var activityRule: ActivityScenarioRule<MainActivity>
= ActivityScenarioRule(MainActivity::class.java)
@Test
fun testFollowersTextView() {
onView(withId(R.id.button)).perform(click())
onView(withId(R.id.followers)).check(matches(withText("Followers")))
@Before
fun openProfileFragment() {
onView(withId(R.id.activity_main_account_btn)).perform(click())
}
}
@Test
fun randomUsernameCorrectlyLoadedTest() {
Thread.sleep(5000) // wait for the username to load (to modify once we know how)
onView(withId(R.id.accountName)).check(matches(not(withText(R.string.no_username))))
}
}

View File

@ -1,16 +1,14 @@
package com.h.pixeldroid
import android.view.Gravity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
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.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 org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@ -22,47 +20,25 @@ class SettingsTest {
var activityRule: ActivityScenarioRule<MainActivity>
= ActivityScenarioRule(MainActivity::class.java)
@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 the 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()))
@Before
fun openDrawer() {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
}
@Test
fun testDrawerProfileButton() {
// 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 the activity.
fun myProfileButtonTest() {
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_account))
// Check that profile activity was opened.
onView(withId(R.id.posts)).check(matches(withText("Posts")))
onView(withId(R.id.profile_main_container)).check(matches(isDisplayed()))
}
@Test
fun testOnBackPressed() {
// 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.
fun settingsButtonTest() {
onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_settings))
onView(withId(R.id.settings)).check(matches(isDisplayed()))
}
onView(withId(R.id.settings)).perform(ViewActions.pressBack())
// Check that profile activity was opened.
onView(withId(R.id.button_start_login)).check(matches(withText("start login")))
@Test
fun accessibilityButtonTest() {
// TODO if some accessibility view is added
}
}

View File

@ -0,0 +1,33 @@
package com.h.pixeldroid
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.action.ViewActions.swipeRight
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
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)
@Test
fun swipingRightOnHomepageShowsSettings() {
onView(withId(R.id.main_linear_layout)).perform(swipeRight())
onView(withId(R.id.nav_view)).check(matches(isDisplayed()))
}
@Test
fun swipeLeftOnHomePageShowsProfile() {
// to modify when we add more tabs
onView(withId(R.id.main_linear_layout)).perform(swipeLeft())
onView(withId(R.id.profile_main_container)).check(matches(isDisplayed()))
}
}

View File

@ -41,11 +41,6 @@
android:scheme="@string/auth_scheme" />
</intent-filter>
</activity>
<activity android:name=".ProfileActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
</application>

View File

@ -6,7 +6,6 @@ import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import com.h.pixeldroid.api.PixelfedAPI

View File

@ -3,13 +3,18 @@ package com.h.pixeldroid
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.ImageButton
import android.widget.LinearLayout
import androidx.annotation.NonNull
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import com.google.android.material.navigation.NavigationView
import com.h.pixeldroid.fragments.HomeFragment
import com.h.pixeldroid.fragments.ProfileFragment
import com.h.pixeldroid.motions.OnSwipeListener
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
@ -19,20 +24,49 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mainLinearLayout : LinearLayout = findViewById(R.id.main_linear_layout)
val homepageButton : ImageButton = findViewById(R.id.activity_main_home_btn)
val accountButton : ImageButton = findViewById(R.id.activity_main_account_btn)
homepageButton.setOnClickListener {
launchFragment(HomeFragment())
}
accountButton.setOnClickListener {
launchFragment(ProfileFragment())
}
// Setup the drawer
drawerLayout = findViewById(R.id.drawer_layout)
val navigationView: NavigationView = findViewById(R.id.nav_view)
navigationView.setNavigationItemSelectedListener(this)
val buttonLogin = findViewById<Button>(R.id.button_start_login)
buttonLogin.setOnClickListener((View.OnClickListener {
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent) }))
val onSwipeListener = object: OnSwipeListener(this) {
override fun onSwipeRight() = swipeRight()
override fun onSwipeLeft() = swipeLeft()
}
mainLinearLayout.setOnTouchListener(onSwipeListener)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener((View.OnClickListener {
val intent = Intent(this, ProfileActivity::class.java)
startActivity(intent) }))
// default fragment that displays when we open the app
launchFragment(HomeFragment())
}
private fun swipeRight() {
// TODO: correctly switch between tabs
drawerLayout.openDrawer(GravityCompat.START)
}
private fun swipeLeft() {
// TODO: correctly switch between tabs
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left)
.replace(R.id.fragment_container, ProfileFragment()).commit()
}
/*
Launches the given fragment and put it as the current "activity"
*/
private fun launchFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment).commit()
}
/*
@ -41,7 +75,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
override fun onNavigationItemSelected(@NonNull item: MenuItem): Boolean {
when (item.itemId){
R.id.nav_settings -> launchActivity(SettingsActivity())
R.id.nav_account -> launchActivity(ProfileActivity())
R.id.nav_account -> launchFragment(ProfileFragment())
}
drawerLayout.closeDrawer(GravityCompat.START)

View File

@ -0,0 +1,29 @@
package com.h.pixeldroid.fragments
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import com.h.pixeldroid.LoginActivity
import com.h.pixeldroid.R
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
val loginButton: Button = view.findViewById(R.id.button_start_login)
loginButton.setOnClickListener {
startActivity(Intent(view.context, LoginActivity::class.java))
}
return view
}
}

View File

@ -1,12 +1,20 @@
package com.h.pixeldroid
package com.h.pixeldroid.fragments
import android.content.Intent
import android.graphics.Typeface
import androidx.appcompat.app.AppCompatActivity
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
import com.h.pixeldroid.LoginActivity
import com.h.pixeldroid.R
import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.objects.Account
import com.h.pixeldroid.objects.Status
@ -14,18 +22,19 @@ import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class ProfileActivity : AppCompatActivity() {
const val BASE_URL = "https://pixelfed.de/"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
class ProfileFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater.inflate(R.layout.fragment_profile, container, false)
var statuses: ArrayList<Status>? = null
val BASE_URL = "https://pixelfed.de/"
var statuses: ArrayList<Status>?
val pixelfedAPI = PixelfedAPI.create(BASE_URL)
pixelfedAPI.timelinePublic(null, null, null, null, null)
.enqueue(object : Callback<List<Status>> {
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
@ -36,7 +45,7 @@ class ProfileActivity : AppCompatActivity() {
val account = statuses!![0].account
setContent(account)
setContent(view, account)
}
}
@ -47,33 +56,34 @@ class ProfileActivity : AppCompatActivity() {
}
})
return view
}
private fun setContent(account: Account) {
private fun setContent(view: View, account: Account) {
// ImageView : profile picture
val profilePicture = findViewById<ImageView>(R.id.profilePicture)
Glide.with(applicationContext).load(account.avatar).into(profilePicture)
val profilePicture = view.findViewById<ImageView>(R.id.profilePicture)
Glide.with(view.context.applicationContext).load(account.avatar).into(profilePicture)
// TextView : description / bio
val description = findViewById<TextView>(R.id.description)
val description = view.findViewById<TextView>(R.id.description)
description.text = account.note
// TextView : account name
val accountName = findViewById<TextView>(R.id.accountName)
val accountName = view.findViewById<TextView>(R.id.accountName)
accountName.text = account.username
// TextView : number of posts
val nbPosts = findViewById<TextView>(R.id.nbPosts)
val nbPosts = view.findViewById<TextView>(R.id.nbPosts)
nbPosts.text = account.statuses_count.toString()
nbPosts.setTypeface(null, Typeface.BOLD)
// TextView : number of followers
val nbFollowers = findViewById<TextView>(R.id.nbFollowers)
val nbFollowers = view.findViewById<TextView>(R.id.nbFollowers)
nbFollowers.text = account.followers_count.toString()
nbFollowers.setTypeface(null, Typeface.BOLD)
// TextView : number of following
val nbFollowing = findViewById<TextView>(R.id.nbFollowing)
val nbFollowing = view.findViewById<TextView>(R.id.nbFollowing)
nbFollowing.text = account.following_count.toString()
nbFollowing.setTypeface(null, Typeface.BOLD)
}

View File

@ -0,0 +1,54 @@
package com.h.pixeldroid.motions
import android.content.Context
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import kotlin.math.abs
const val SWIPE_DISTANCE_THRESHOLD: Int = 100
const val SWIPE_VELOCITY_THRESHOLD: Int = 100
/**
* Detects left and right swipes across a view.
*
* inspired from https://stackoverflow.com/questions/4139288/android-how-to-handle-right-to-left-swipe-gestures
*/
open class OnSwipeListener(context: Context?) : OnTouchListener {
private val gestureDetector: GestureDetector = GestureDetector(context, GestureListener())
override fun onTouch(v: View?, event: MotionEvent?): Boolean =
gestureDetector.onTouchEvent(event)
// redefining gesture listener to call our custom functions
private inner class GestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean = true
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val distanceX = e2.x - e1.x
val distanceY = e2.y - e1.y
if (abs(distanceX) > abs(distanceY) // swipe on the side and not up or down
&& abs(distanceX) > SWIPE_DISTANCE_THRESHOLD
&& abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
) {
if (distanceX > 0) onSwipeRight() else onSwipeLeft()
return true
}
return false
}
}
open fun onSwipeLeft() {}
open fun onSwipeRight() {}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="@integer/swipe_animation_duration"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%p"
android:toXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="@integer/swipe_animation_duration"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="-100%p"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="@integer/swipe_animation_duration"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%p"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="@integer/swipe_animation_duration"/>
</set>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path android:fillColor="#FF000000" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

View File

@ -7,28 +7,72 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"
tools:context=".MainActivity">
tools:context="com.h.pixeldroid.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:layout_editor_absoluteX="67dp"
tools:layout_editor_absoluteY="299dp">
android:id="@+id/main_linear_layout">
<Button
android:id="@+id/button_start_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:text="start login" />
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Show a profile" />
android:background="@color/colorPrimary"
android:orientation="horizontal">
<ImageButton
android:id="@+id/activity_main_home_btn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:padding="10dp"
app:srcCompat="@drawable/ic_home_white_24dp" />
<ImageButton
android:id="@+id/activity_main_search_btn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="10dp"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_search_white_24dp" />
<ImageButton
android:id="@+id/activity_main_camera_btn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="10dp"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_photo_camera_white_24dp" />
<ImageButton
android:id="@+id/activity_main_favorite_btn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="10dp"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_star_white_24dp" />
<ImageButton
android:id="@+id/activity_main_account_btn"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
app:srcCompat="@drawable/ic_person_white_24dp" />
</LinearLayout>
</LinearLayout>
<com.google.android.material.navigation.NavigationView
@ -40,6 +84,5 @@
app:headerLayout="@layout/nav_header"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.HomeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@+id/button_start_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/start_login" />
</LinearLayout>
</FrameLayout>

View File

@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProfileActivity">
android:id="@+id/profile_main_container"
tools:context=".fragments.ProfileFragment">
<ImageView
@ -27,16 +28,6 @@
app:layout_constraintEnd_toEndOf="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/nbPosts"
android:layout_width="39dp"
@ -47,6 +38,16 @@
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"
@ -83,7 +84,7 @@
android:layout_width="156dp"
android:layout_height="22dp"
android:layout_marginTop="15dp"
android:text="No Username"
android:text="@string/no_username"
app:layout_constraintStart_toStartOf="@+id/profilePicture"
app:layout_constraintTop_toBottomOf="@+id/profilePicture" />

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="swipe_animation_duration">300</integer>
</resources>

View File

@ -53,4 +53,9 @@
<string name="attachment_summary_on">Automatically download attachments for incoming emails
</string>
<string name="attachment_summary_off">Only download attachments when manually requested</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="start_login">Start Login</string>
<string name="no_username">No Username</string>
</resources>

View File

@ -0,0 +1,20 @@
package com.h.pixeldroid
import com.h.pixeldroid.motions.OnSwipeListener
import org.junit.Assert.assertEquals
import org.junit.Test
class OnSwipeListenerUnitTest {
@Test
fun undefinedSwipeRightDoesNothingTest() {
val swipeListener = OnSwipeListener(null)
assertEquals(Unit, swipeListener.onSwipeRight())
}
@Test
fun undefinedSwipeLeftDoesNothingTest() {
val swipeListener = OnSwipeListener(null)
assertEquals(Unit, swipeListener.onSwipeLeft())
}
}