Compare commits
8 Commits
feature/SA
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
962c5df78e | ||
|
bd164ff832 | ||
|
61f2af2cb9 | ||
|
066400f700 | ||
|
4c00eaaa1b | ||
|
0c8ec7fd28 | ||
|
9fc3c124bf | ||
8130012710 |
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
687
Test Results - MainActivityTest.html
Normal file
1010
Test Results - Tests_in_'pl_edu_amu_wmi_socialaggregator'.html
Normal file
@ -25,6 +25,16 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
includeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -39,9 +49,36 @@ dependencies {
|
|||||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
|
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"
|
||||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.2.0'
|
implementation 'com.jakewharton.rxbinding2:rxbinding:2.2.0'
|
||||||
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.2.0'
|
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.2.0'
|
||||||
testImplementation 'junit:junit:4.12'
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
|
|
||||||
|
androidTestImplementation 'androidx.test:core-ktx:1.2.0'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion
|
||||||
|
androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion
|
||||||
|
androidTestImplementation 'androidx.test:rules:' + rootProject.rulesVersion
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-contrib:' + rootProject.espressoVersion
|
||||||
|
androidTestImplementation 'androidx.test.ext:truth:' + rootProject.extTruthVersion
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion
|
||||||
|
androidTestImplementation 'org.robolectric:annotations:' + rootProject.robolectricVersion
|
||||||
|
|
||||||
|
testImplementation 'androidx.test:core:' + rootProject.coreVersion
|
||||||
|
testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion
|
||||||
|
testImplementation 'androidx.test.espresso:espresso-core:' + rootProject.espressoVersion
|
||||||
|
testImplementation 'androidx.test.espresso:espresso-intents:' + rootProject.espressoVersion
|
||||||
|
testImplementation 'androidx.test.espresso:espresso-contrib:' + rootProject.espressoVersion
|
||||||
|
testImplementation 'androidx.test.ext:truth:' + rootProject.extTruthVersion
|
||||||
|
testImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion
|
||||||
|
testImplementation 'org.robolectric:robolectric:' + rootProject.robolectricVersion
|
||||||
|
|
||||||
|
implementation 'com.facebook.android:facebook-login:[5,6)'
|
||||||
|
implementation 'com.facebook.android:facebook-share:[5,6)'
|
||||||
|
|
||||||
|
implementation 'com.squareup.picasso:picasso:2.5.2'
|
||||||
|
|
||||||
|
implementation([
|
||||||
|
'com.snapchat.kit.sdk:creative:1.1.4',
|
||||||
|
'com.snapchat.kit.sdk:core:1.1.4'
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.test.core.app.launchActivity
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
|
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import junit.framework.Assert.assertEquals
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.TestUtils.actionOnChild
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.TestUtils.cleanLocalStorage
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.activity.AddSocialActivity
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AddSocialActivityTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val activity = IntentsTestRule(AddSocialActivity::class.java)
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun initialState_noAddedSocials_oneAvailableSocial() {
|
||||||
|
cleanLocalStorage(activity)
|
||||||
|
|
||||||
|
val newActivity = launchActivity<AddSocialActivity>()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
0, activity.activity.findViewById<RecyclerView>(R.id.availableSocialsRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
1, activity.activity.findViewById<RecyclerView>(R.id.addSocialRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clickingSocials_movesThemFormAvailableToAdded_etViceVersa() {
|
||||||
|
cleanLocalStorage(activity)
|
||||||
|
|
||||||
|
onView(withId(R.id.addSocialRecyclerView))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
|
||||||
|
0,
|
||||||
|
actionOnChild(click(), R.id.socialPlatformImage)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
1, activity.activity.findViewById<RecyclerView>(R.id.availableSocialsRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
0, activity.activity.findViewById<RecyclerView>(R.id.addSocialRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
|
||||||
|
onView(withId(R.id.availableSocialsRecyclerView))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
|
||||||
|
0,
|
||||||
|
actionOnChild(click(), R.id.socialPlatformImage)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
0, activity.activity.findViewById<RecyclerView>(R.id.availableSocialsRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
1, activity.activity.findViewById<RecyclerView>(R.id.addSocialRecyclerView)
|
||||||
|
.adapter?.itemCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,10 +15,12 @@ import org.junit.Assert.*
|
|||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ExampleInstrumentedTest {
|
class ExampleInstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
// Context of the app under test.
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
appContext.filesDir.delete()
|
||||||
assertEquals("pl.edu.amu.wmi.socialaggregator", appContext.packageName)
|
assertEquals("pl.edu.amu.wmi.socialaggregator", appContext.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator
|
||||||
|
|
||||||
|
import androidx.test.espresso.Espresso.onView
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
|
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.activity.AddSocialActivity
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.activity.MainActivity
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.activity.NewPostActivity
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MainActivityTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val activity = IntentsTestRule(MainActivity::class.java)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clickAddNewSocial_shouldOpenAddNewSocialActivity() {
|
||||||
|
onView(withId(R.id.connectedSocialsButton)).perform(click())
|
||||||
|
|
||||||
|
intended(hasComponent(AddSocialActivity::class.java.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clickCreatePost_shouldOpenAddNewNewPostActivity() {
|
||||||
|
onView(withId(R.id.createPostButton)).perform(click())
|
||||||
|
|
||||||
|
intended(hasComponent(NewPostActivity::class.java.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.test.espresso.Espresso.*
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
|
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.TestUtils.atPosition
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.TestUtils.cleanLocalStorage
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.TestUtils.getCurrentActivity
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.activity.MainActivity
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class NewPostActivityTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val activity = IntentsTestRule(MainActivity::class.java)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clickPublishPost_shouldEndActivity() {
|
||||||
|
onView(withId(R.id.createPostButton)).perform(click())
|
||||||
|
|
||||||
|
closeSoftKeyboard()
|
||||||
|
|
||||||
|
onView(withId(R.id.publishPost)).perform(click())
|
||||||
|
|
||||||
|
assertTrue(getCurrentActivity()!!::class.java.simpleName == "MainActivity")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun clickPublishPost_shouldIncreasePostCount() {
|
||||||
|
cleanLocalStorage(activity)
|
||||||
|
|
||||||
|
onView(withId(R.id.connectedSocialsButton)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.addSocialRecyclerView))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
|
||||||
|
0,
|
||||||
|
TestUtils.actionOnChild(click(), R.id.socialPlatformImage)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
pressBack()
|
||||||
|
|
||||||
|
onView(withId(R.id.createPostButton)).perform(click())
|
||||||
|
|
||||||
|
closeSoftKeyboard()
|
||||||
|
|
||||||
|
onView(withId(R.id.availableSocials))
|
||||||
|
.perform(
|
||||||
|
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
|
||||||
|
0,
|
||||||
|
TestUtils.actionOnChild(click(), R.id.chip)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
onView(withId(R.id.publishPost)).perform(click())
|
||||||
|
|
||||||
|
onView(withId(R.id.previousPostsRecyclerView)).check(
|
||||||
|
matches(atPosition(0, hasDescendant(withText("Facebook"))))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.test.espresso.NoMatchingViewException
|
||||||
|
import androidx.test.espresso.UiController
|
||||||
|
import androidx.test.espresso.ViewAction
|
||||||
|
import androidx.test.espresso.ViewAssertion
|
||||||
|
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||||
|
import androidx.test.espresso.matcher.BoundedMatcher
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
|
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
|
||||||
|
import androidx.test.runner.lifecycle.Stage
|
||||||
|
import org.hamcrest.CoreMatchers
|
||||||
|
import org.hamcrest.Description
|
||||||
|
import org.hamcrest.Matcher
|
||||||
|
import org.hamcrest.core.AllOf
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object TestUtils {
|
||||||
|
|
||||||
|
fun cleanLocalStorage(activity: IntentsTestRule<out Activity>) {
|
||||||
|
deleteRecursive(activity.activity.filesDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteRecursive(fileOrDirectory: File) {
|
||||||
|
if (fileOrDirectory.isDirectory())
|
||||||
|
for (child in fileOrDirectory.listFiles())
|
||||||
|
deleteRecursive(child)
|
||||||
|
|
||||||
|
fileOrDirectory.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun actionOnChild(action: ViewAction, childId: Int): ViewAction? {
|
||||||
|
return object : ViewAction {
|
||||||
|
override fun getDescription(): String {
|
||||||
|
return "Action on child"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getConstraints(): Matcher<View> {
|
||||||
|
return AllOf.allOf(
|
||||||
|
ViewMatchers.isDisplayed(),
|
||||||
|
ViewMatchers.isAssignableFrom(View::class.java)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun perform(uiController: UiController?, view: View?) {
|
||||||
|
view?.let {
|
||||||
|
val child = it.findViewById<View>(childId)
|
||||||
|
action.perform(uiController, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atPosition(position: Int, itemMatcher: Matcher<View>): Matcher<View> {
|
||||||
|
checkNotNull(itemMatcher)
|
||||||
|
return object : BoundedMatcher<View, RecyclerView>(RecyclerView::class.java) {
|
||||||
|
override fun describeTo(description: Description) {
|
||||||
|
description.appendText("has item at position $position: ")
|
||||||
|
itemMatcher.describeTo(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun matchesSafely(view: RecyclerView): Boolean {
|
||||||
|
val viewHolder = view.findViewHolderForAdapterPosition(position)
|
||||||
|
?: // has no item on such position
|
||||||
|
return false
|
||||||
|
return itemMatcher.matches(viewHolder.itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun atPosition(position: Int, id: Int, itemMatcher: Matcher<View>): Matcher<View> {
|
||||||
|
checkNotNull(itemMatcher)
|
||||||
|
return object : BoundedMatcher<View, RecyclerView>(RecyclerView::class.java) {
|
||||||
|
override fun describeTo(description: Description) {
|
||||||
|
description.appendText("has item at position $position: ")
|
||||||
|
itemMatcher.describeTo(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun matchesSafely(view: RecyclerView): Boolean {
|
||||||
|
val viewHolder = view.findViewHolderForAdapterPosition(position)
|
||||||
|
?: // has no item on such position
|
||||||
|
return false
|
||||||
|
val view = viewHolder.itemView.findViewById<View>(id)
|
||||||
|
return itemMatcher.matches(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCurrentActivity(): Activity? {
|
||||||
|
var currentActivity: Activity? = null
|
||||||
|
getInstrumentation().runOnMainSync {
|
||||||
|
run {
|
||||||
|
currentActivity =
|
||||||
|
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
|
||||||
|
Stage.RESUMED
|
||||||
|
).elementAtOrNull(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,13 +2,25 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="pl.edu.amu.wmi.socialaggregator">
|
package="pl.edu.amu.wmi.socialaggregator">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
<activity
|
||||||
|
android:name=".activity.PostHistoryActivity"
|
||||||
|
android:label="@string/title_activity_post_history"
|
||||||
|
android:parentActivityName=".activity.MainActivity"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="pl.edu.amu.wmi.socialaggregator.activity.MainActivity" />
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.AddSocialActivity"
|
android:name=".activity.AddSocialActivity"
|
||||||
android:label="@string/title_activity_add_social"
|
android:label="@string/title_activity_add_social"
|
||||||
@ -34,6 +46,50 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<meta-data android:name="com.facebook.sdk.ApplicationId"
|
||||||
|
android:value="@string/facebook_app_id"/>
|
||||||
|
|
||||||
|
<activity android:name="com.facebook.FacebookActivity"
|
||||||
|
android:configChanges=
|
||||||
|
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
|
||||||
|
android:label="@string/app_name" />
|
||||||
|
<activity
|
||||||
|
android:name="com.facebook.CustomTabActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="@string/fb_login_protocol_scheme" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider android:authorities="com.facebook.app.FacebookContentProvider2437098899888167"
|
||||||
|
android:name="com.facebook.FacebookContentProvider"
|
||||||
|
android:exported="true"/>
|
||||||
|
|
||||||
|
|
||||||
|
<meta-data android:name="com.snapchat.kit.sdk.clientId" android:value="240836fb-b762-4be8-a42f-5472a22f2114" />
|
||||||
|
|
||||||
|
<!-- <meta-data android:name="com.snapchat.kit.sdk.clientId" android:value="your app’s client id" />-->
|
||||||
|
<!-- <meta-data android:name="com.snapchat.kit.sdk.redirectUrl" android:value="the url that will handle login completion" />-->
|
||||||
|
<!-- <meta-data android:name="com.snapchat.kit.sdk.scopes" android:resource="@array/snap_connect_scopes" />-->
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths"
|
||||||
|
/>
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -8,7 +8,7 @@ import kotlinx.android.synthetic.main.activity_add_social.*
|
|||||||
import kotlinx.android.synthetic.main.content_add_social.*
|
import kotlinx.android.synthetic.main.content_add_social.*
|
||||||
import pl.edu.amu.wmi.socialaggregator.R
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
||||||
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithImageRecycler
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
|
||||||
|
|
||||||
class AddSocialActivity : AppCompatActivity() {
|
class AddSocialActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -23,33 +23,33 @@ class AddSocialActivity : AppCompatActivity() {
|
|||||||
val loggedInSocials = SocialPlatformsManager.getLoggedIn(this)
|
val loggedInSocials = SocialPlatformsManager.getLoggedIn(this)
|
||||||
val recyclers = mutableListOf(availableSocialsRecyclerView, addSocialRecyclerView)
|
val recyclers = mutableListOf(availableSocialsRecyclerView, addSocialRecyclerView)
|
||||||
|
|
||||||
availableSocialsRecyclerView.apply {
|
// availableSocialsRecyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(this@AddSocialActivity)
|
// layoutManager = LinearLayoutManager(this@AddSocialActivity)
|
||||||
adapter = SocialWithImageRecycler(
|
// adapter = SocialWithButtonRecycler(
|
||||||
loggedInSocials,
|
// loggedInSocials,
|
||||||
R.drawable.ic_remove_circle
|
// R.mipmap.ic_opaque_remove
|
||||||
) {
|
// ) {
|
||||||
it.logout(context)
|
// it.logout(context)
|
||||||
loggedInSocials.remove(it)
|
// loggedInSocials.remove(it)
|
||||||
notLoggedInSocials.add(it)
|
// notLoggedInSocials.add(it)
|
||||||
recyclers.forEach { it.adapter?.notifyDataSetChanged() }
|
// recyclers.forEach { it.adapter?.notifyDataSetChanged() }
|
||||||
Toast.makeText(context, "Logged out from ${it.getName()}!", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(context, "Logged out from ${it.getName()}!", Toast.LENGTH_SHORT).show()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
addSocialRecyclerView.apply {
|
// addSocialRecyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(this@AddSocialActivity)
|
// layoutManager = LinearLayoutManager(this@AddSocialActivity)
|
||||||
adapter = SocialWithImageRecycler(
|
// adapter = SocialWithButtonRecycler(
|
||||||
notLoggedInSocials,
|
// notLoggedInSocials,
|
||||||
R.drawable.ic_add_circle
|
// R.mipmap.ic_opaque_add
|
||||||
) {
|
// ) {
|
||||||
it.login(context)
|
// it.login(context)
|
||||||
notLoggedInSocials.remove(it)
|
// notLoggedInSocials.remove(it)
|
||||||
loggedInSocials.add(it)
|
// loggedInSocials.add(it)
|
||||||
recyclers.forEach { it.adapter?.notifyDataSetChanged() }
|
// recyclers.forEach { it.adapter?.notifyDataSetChanged() }
|
||||||
Toast.makeText(context, "Logged in to ${it.getName()}!", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(context, "Logged in to ${it.getName()}!", Toast.LENGTH_SHORT).show()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,17 +5,21 @@ import android.os.Bundle
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.jakewharton.rxbinding2.view.RxView
|
import com.jakewharton.rxbinding2.view.RxView
|
||||||
|
import io.reactivex.Observable
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import pl.edu.amu.wmi.socialaggregator.R
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
||||||
import pl.edu.amu.wmi.socialaggregator.viewholders.PostTextRecycler
|
import pl.edu.amu.wmi.socialaggregator.viewholders.PostSummaryRecycler
|
||||||
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithImageRecycler
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val subs = CompositeDisposable()
|
private val subs = CompositeDisposable()
|
||||||
|
|
||||||
|
private val TAG = MainActivity::class.java.simpleName
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
@ -26,13 +30,18 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val intent = Intent(this, NewPostActivity::class.java)
|
val intent = Intent(this, NewPostActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
},
|
},
|
||||||
RxView.clicks(connectedSocialsButton)
|
RxView.clicks(previousPostsButton)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
val intent = Intent(this, AddSocialActivity::class.java)
|
val intent = Intent(this, PostHistoryActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
SocialPlatformsManager.getAll()
|
||||||
|
.forEach { it.onActivityResult(requestCode, resultCode, data) }
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@ -40,14 +49,20 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
connectedSocialsRecyclerView.apply {
|
connectedSocialsRecyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(this@MainActivity)
|
layoutManager = LinearLayoutManager(this@MainActivity)
|
||||||
adapter = SocialWithImageRecycler(SocialPlatformsManager.getLoggedIn(this@MainActivity),
|
adapter =
|
||||||
R.drawable.ic_launcher_background) {}
|
SocialWithButtonRecycler(SocialPlatformsManager.getAll(), this@MainActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
previousPostsRecyclerView.apply {
|
previousPostsRecyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(this@MainActivity)
|
layoutManager = LinearLayoutManager(this@MainActivity)
|
||||||
adapter = PostTextRecycler(SocialPlatformsManager.getLoggedIn(this@MainActivity)
|
adapter = PostSummaryRecycler(
|
||||||
.map { it to it.getPosts(this@MainActivity).size })
|
Observable.merge(SocialPlatformsManager.getLoggedIn(this@MainActivity)
|
||||||
|
.map { social ->
|
||||||
|
social.getPosts(this@MainActivity)
|
||||||
|
.map { social to it }
|
||||||
|
.toObservable()
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,41 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.activity
|
package pl.edu.amu.wmi.socialaggregator.activity
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import android.provider.MediaStore
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.jakewharton.rxbinding2.view.RxView
|
import com.jakewharton.rxbinding2.view.RxView
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.functions.BiFunction
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import io.reactivex.subjects.PublishSubject
|
||||||
import kotlinx.android.synthetic.main.activity_new_post.*
|
import kotlinx.android.synthetic.main.activity_new_post.*
|
||||||
import kotlinx.android.synthetic.main.content_new_post.*
|
import kotlinx.android.synthetic.main.content_new_post.*
|
||||||
import pl.edu.amu.wmi.socialaggregator.R
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
||||||
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
||||||
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithToggleRecycler
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithToggleRecycler
|
||||||
|
|
||||||
|
|
||||||
class NewPostActivity : AppCompatActivity() {
|
class NewPostActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
var file: String? = null
|
||||||
|
|
||||||
|
val postedSubject = PublishSubject.create<Any>()
|
||||||
|
|
||||||
|
val resumedSubject = PublishSubject.create<Any>()
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -36,16 +57,115 @@ class NewPostActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RxView.clicks(publishPost)
|
RxView.clicks(publishPost)
|
||||||
.subscribe {
|
.filter { file != null }
|
||||||
availablesRecycler.chips.entries.forEach { (social, chip) ->
|
.doOnNext {
|
||||||
if (chip.isChecked) {
|
val sdk = android.os.Build.VERSION.SDK_INT
|
||||||
social.addPost(this, postText.text?.toString() ?: "test", emptyList())
|
if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||||
Toast.makeText(this, "Posted to ${social.getName()}!", Toast.LENGTH_LONG)
|
val clipboard =
|
||||||
.show()
|
getSystemService(Context.CLIPBOARD_SERVICE) as android.text.ClipboardManager
|
||||||
}
|
clipboard.text = postText.text?.toString() ?: ""
|
||||||
|
} else {
|
||||||
|
val clipboard =
|
||||||
|
getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||||
|
val clip = android.content.ClipData.newPlainText(
|
||||||
|
"text label",
|
||||||
|
postText.text?.toString() ?: ""
|
||||||
|
)
|
||||||
|
clipboard.primaryClip = clip
|
||||||
}
|
}
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.doOnNext {
|
||||||
|
resumedSubject
|
||||||
|
.zipWith(
|
||||||
|
Observable.fromIterable(availablesRecycler.chips.entries
|
||||||
|
.filter { (_, chip) -> chip.isChecked }
|
||||||
|
.map { it.key }),
|
||||||
|
BiFunction<Any, SocialPlatform, SocialPlatform> { _, t2 -> t2 }
|
||||||
|
)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.doOnNext {
|
||||||
|
val bitmap = BitmapFactory.decodeFile(file!!)
|
||||||
|
it.addPost(
|
||||||
|
postedSubject, this,
|
||||||
|
postText.text?.toString() ?: "",
|
||||||
|
listOf(bitmap),
|
||||||
|
listOf(file!!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.take(availablesRecycler.chips.count { it.value.isChecked }.toLong() + 1)
|
||||||
|
.toList()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { _ ->
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.subscribe {
|
||||||
|
resumedSubject.onNext(Any())
|
||||||
|
}
|
||||||
|
|
||||||
|
RxView.clicks(imageView2)
|
||||||
|
.subscribe {
|
||||||
|
val getIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
getIntent.type = "image/*"
|
||||||
|
|
||||||
|
val pickIntent = Intent(
|
||||||
|
Intent.ACTION_PICK,
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||||
|
)
|
||||||
|
pickIntent.type = "image/*"
|
||||||
|
|
||||||
|
val chooserIntent = Intent.createChooser(getIntent, "Select Image")
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(pickIntent))
|
||||||
|
|
||||||
|
startActivityForResult(chooserIntent, PICK_IMAGE_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (canAccessGallery()) {
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
this,
|
||||||
|
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
SocialPlatformsManager.getAll()
|
||||||
|
.forEach { it.onActivityResult(requestCode, resultCode, data) }
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (requestCode == PICK_IMAGE_REQUEST_CODE) {
|
||||||
|
data?.data?.let { uri ->
|
||||||
|
file = getPath(uri)
|
||||||
|
imageView2.setImageURI(uri)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPath(uri: Uri): String? {
|
||||||
|
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
|
val cursor = contentResolver.query(uri, projection, null, null, null) ?: return null
|
||||||
|
val column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||||
|
cursor.moveToFirst()
|
||||||
|
val s = cursor.getString(column_index)
|
||||||
|
cursor.close()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
resumedSubject.onNext(Any())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canAccessGallery() =
|
||||||
|
(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PICK_IMAGE_REQUEST_CODE = 69
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.activity
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import kotlinx.android.synthetic.main.activity_post_history.*
|
||||||
|
import kotlinx.android.synthetic.main.content_post_history.*
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.viewholders.PostDetailsRecycler
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PostHistoryActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_post_history)
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
postHistoryRecycler.apply {
|
||||||
|
layoutManager = LinearLayoutManager(this@PostHistoryActivity)
|
||||||
|
adapter = PostDetailsRecycler(
|
||||||
|
Observable.fromIterable(SocialPlatformsManager.getLoggedIn(this@PostHistoryActivity))
|
||||||
|
.flatMap {
|
||||||
|
it.getPosts(this@PostHistoryActivity)
|
||||||
|
.toObservable()
|
||||||
|
})
|
||||||
|
addItemDecoration(
|
||||||
|
DividerItemDecoration(
|
||||||
|
context,
|
||||||
|
(layoutManager as LinearLayoutManager).orientation
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,234 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.constraintlayout.widget.ConstraintSet
|
||||||
|
import com.facebook.*
|
||||||
|
import com.facebook.login.LoginManager
|
||||||
|
import com.facebook.login.LoginResult
|
||||||
|
import com.facebook.login.widget.LoginButton
|
||||||
|
import com.facebook.share.Sharer
|
||||||
|
import com.facebook.share.model.ShareMediaContent
|
||||||
|
import com.facebook.share.model.SharePhoto
|
||||||
|
import com.facebook.share.widget.ShareDialog
|
||||||
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import io.reactivex.subjects.PublishSubject
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.utils.Utils
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
|
||||||
|
|
||||||
|
|
||||||
|
class Facebook : SocialPlatform {
|
||||||
|
|
||||||
|
private var callbackManager: CallbackManager = CallbackManager.Factory.create()
|
||||||
|
|
||||||
|
override fun getName(): String = "Facebook"
|
||||||
|
|
||||||
|
override fun getLogo(): Int = R.mipmap.ic_logo_facebook
|
||||||
|
|
||||||
|
override fun login(context: Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun logout(context: Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLoggedIn(context: Context): Boolean {
|
||||||
|
val accessToken = AccessToken.getCurrentAccessToken()
|
||||||
|
return accessToken != null && !accessToken.isExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addPost(
|
||||||
|
publishSubject: PublishSubject<Any>,
|
||||||
|
context: Context,
|
||||||
|
text: String,
|
||||||
|
images: List<Bitmap>,
|
||||||
|
imagePaths: List<String>
|
||||||
|
) {
|
||||||
|
val content = ShareMediaContent.Builder().apply {
|
||||||
|
images.forEach {
|
||||||
|
addMedium(
|
||||||
|
SharePhoto.Builder().setBitmap(it).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val shareDialog = ShareDialog(context as Activity)
|
||||||
|
shareDialog.registerCallback(callbackManager, object : FacebookCallback<Sharer.Result?> {
|
||||||
|
override fun onSuccess(result: Sharer.Result?) {
|
||||||
|
publishSubject.onNext(Any())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
publishSubject.onNext(Any())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(error: FacebookException?) {
|
||||||
|
publishSubject.onError(Exception(error?.localizedMessage))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
shareDialog.show(content, ShareDialog.Mode.AUTOMATIC)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPosts(context: Context): Single<List<Post>> {
|
||||||
|
return Single.just(AccessToken.getCurrentAccessToken())
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.map { token ->
|
||||||
|
if (token.isExpired) {
|
||||||
|
return@map emptyList<Post>()
|
||||||
|
} else {
|
||||||
|
val request = GraphRequest.newGraphPathRequest(
|
||||||
|
AccessToken.getCurrentAccessToken(),
|
||||||
|
"/me/posts"
|
||||||
|
) {
|
||||||
|
Log.i(getName(), it.toString())
|
||||||
|
}
|
||||||
|
val parameters = Bundle()
|
||||||
|
parameters.putString(
|
||||||
|
"fields",
|
||||||
|
"likes.summary(true),created_time,message,attachments{url,unshimmed_url,media,subattachments}"
|
||||||
|
)
|
||||||
|
request.parameters = parameters
|
||||||
|
val res = request.executeAndWait()
|
||||||
|
|
||||||
|
val data = res.jsonObject["data"] as JSONArray
|
||||||
|
val size = data.length()
|
||||||
|
return@map (0 until size).map {
|
||||||
|
val obj = data[it] as JSONObject
|
||||||
|
val msg = if (obj.has("message")) obj.getString("message") else ""
|
||||||
|
val date =
|
||||||
|
if (obj.has("created_time")) obj.getString("created_time") else ""
|
||||||
|
// SimpleDateFormat("YYYY-MM-DD\'T\'hh:mm:ssZ").parse(date)
|
||||||
|
Post(this, msg, date, getImages(obj))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImages(obj: JSONObject): List<String> {
|
||||||
|
val list = emptyList<String>().toMutableList()
|
||||||
|
if (obj.has("attachments")) {
|
||||||
|
val attachments = obj.getJSONObject("attachments").getJSONArray("data")
|
||||||
|
try {
|
||||||
|
list.addAll((0 until attachments.length())
|
||||||
|
.map { attachments[it] as JSONObject }
|
||||||
|
.flatMap {
|
||||||
|
val urls = mutableListOf<String>()
|
||||||
|
if (it.has("media")) {
|
||||||
|
urls.add(
|
||||||
|
it.getJSONObject("media")
|
||||||
|
.getJSONObject("image")
|
||||||
|
.getString("src")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (it.has("subattachments")) {
|
||||||
|
val subattachments =
|
||||||
|
it.getJSONObject("subattachments").getJSONArray("data")
|
||||||
|
urls.addAll((0 until subattachments.length())
|
||||||
|
.map { subattachments[it] as JSONObject }
|
||||||
|
.map {
|
||||||
|
it.getJSONObject("media")
|
||||||
|
.getJSONObject("image")
|
||||||
|
.getString("src")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return@flatMap urls
|
||||||
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(getName(), e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleButtonView(context: Context): (SocialWithButtonRecycler.ViewHolder) -> Unit =
|
||||||
|
{ viewHolder ->
|
||||||
|
viewHolder.textView.text = this.getName()
|
||||||
|
|
||||||
|
val constraintSet = ConstraintSet()
|
||||||
|
val button = LoginButton(context)
|
||||||
|
button.id = View.generateViewId()
|
||||||
|
|
||||||
|
val parent = viewHolder.image.parent as ViewGroup
|
||||||
|
parent.removeView(viewHolder.image)
|
||||||
|
parent.addView(button)
|
||||||
|
|
||||||
|
constraintSet.clone(parent as ConstraintLayout)
|
||||||
|
constraintSet.connect(
|
||||||
|
button.id, ConstraintSet.TOP,
|
||||||
|
parent.id, ConstraintSet.TOP, 8.toPx(context)
|
||||||
|
)
|
||||||
|
constraintSet.connect(
|
||||||
|
button.id, ConstraintSet.END,
|
||||||
|
parent.id, ConstraintSet.END
|
||||||
|
)
|
||||||
|
constraintSet.connect(
|
||||||
|
button.id, ConstraintSet.BOTTOM,
|
||||||
|
parent.id, ConstraintSet.BOTTOM, 8.toPx(context)
|
||||||
|
)
|
||||||
|
|
||||||
|
constraintSet.connect(
|
||||||
|
viewHolder.textView.id, ConstraintSet.BOTTOM,
|
||||||
|
button.id, ConstraintSet.BOTTOM
|
||||||
|
)
|
||||||
|
constraintSet.connect(
|
||||||
|
viewHolder.textView.id, ConstraintSet.END,
|
||||||
|
button.id, ConstraintSet.START, 8.toPx(context)
|
||||||
|
)
|
||||||
|
constraintSet.connect(
|
||||||
|
viewHolder.textView.id, ConstraintSet.START,
|
||||||
|
parent.id, ConstraintSet.START
|
||||||
|
)
|
||||||
|
constraintSet.connect(
|
||||||
|
viewHolder.textView.id, ConstraintSet.TOP,
|
||||||
|
button.id, ConstraintSet.TOP
|
||||||
|
)
|
||||||
|
constraintSet.setHorizontalBias(viewHolder.textView.id, 0.toFloat())
|
||||||
|
constraintSet.applyTo(parent)
|
||||||
|
|
||||||
|
button.setPermissions(listOf("email"))
|
||||||
|
val callback = object : FacebookCallback<LoginResult> {
|
||||||
|
override fun onSuccess(result: LoginResult?) {
|
||||||
|
Log.i(getName(), "SUCCESS!")
|
||||||
|
LoginManager.getInstance().logInWithReadPermissions(
|
||||||
|
context as Activity,
|
||||||
|
listOf("email")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
Log.i(getName(), "CANCEL!")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(error: FacebookException?) {
|
||||||
|
Log.i(getName(), "FAIL!")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
button.registerCallback(callbackManager, callback)
|
||||||
|
LoginManager.getInstance().registerCallback(callbackManager, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
callbackManager.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Int.toPx(context: Context): Int {
|
||||||
|
return Utils.convertDpToPixel(this.toFloat(), context).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,65 +0,0 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.util.Log
|
|
||||||
import pl.edu.amu.wmi.socialaggregator.utils.InternalStorage
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class FacebookMock : SocialPlatform {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val TAG = FacebookMock::class.java.canonicalName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName(): String = "Facebook"
|
|
||||||
|
|
||||||
override fun login(context: Context) {
|
|
||||||
val loginsDir = InternalStorage.getFileOrDir(context, "logins")
|
|
||||||
if (loginsDir != null) {
|
|
||||||
val loginFile = File(loginsDir, "facebook")
|
|
||||||
loginFile.createNewFile()
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Could not create logins directory")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun logout(context: Context) {
|
|
||||||
InternalStorage.getFileOrDir(context, "logins/facebook")?.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isLoggedIn(context: Context): Boolean {
|
|
||||||
return InternalStorage.getFileOrDir(context, "logins/facebook")?.exists() ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addPost(context: Context, text: String, images: List<Bitmap>) {
|
|
||||||
val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
|
|
||||||
if (postsDir != null) {
|
|
||||||
val postDir = File(postsDir, System.currentTimeMillis().toString())
|
|
||||||
postDir.mkdirs()
|
|
||||||
|
|
||||||
val textFile = File(postDir, "content")
|
|
||||||
textFile.createNewFile()
|
|
||||||
textFile.writeText(text)
|
|
||||||
|
|
||||||
images.forEachIndexed { index, image ->
|
|
||||||
val imageFile = File(postDir, "image$index")
|
|
||||||
imageFile.createNewFile()
|
|
||||||
|
|
||||||
ByteArrayOutputStream().use { stream ->
|
|
||||||
image.compress(Bitmap.CompressFormat.JPEG, 100, stream)
|
|
||||||
imageFile.writeBytes(stream.toByteArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Could not create posts directory")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPosts(context: Context): List<String> {
|
|
||||||
val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
|
|
||||||
return postsDir?.listFiles()?.map { it.name }?.toList() ?: emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,5 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
||||||
|
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
class NotApplicableException(val socialPlatform: SocialPlatform) : Exception()
|
@ -0,0 +1,8 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
||||||
|
|
||||||
|
data class Post(
|
||||||
|
val social: SocialPlatform,
|
||||||
|
val content: String,
|
||||||
|
val dateTime: String,
|
||||||
|
val images: List<String>
|
||||||
|
)
|
@ -0,0 +1,83 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.util.Log
|
||||||
|
import com.snapchat.kit.sdk.SnapCreative
|
||||||
|
import com.snapchat.kit.sdk.creative.api.SnapCreativeKitCompletionCallback
|
||||||
|
import com.snapchat.kit.sdk.creative.api.SnapCreativeKitSendError
|
||||||
|
import com.snapchat.kit.sdk.creative.exceptions.SnapMediaSizeException
|
||||||
|
import com.snapchat.kit.sdk.creative.models.SnapPhotoContent
|
||||||
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.subjects.PublishSubject
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
class Snapchat : SocialPlatform {
|
||||||
|
override fun getName(): String = "Snapchat"
|
||||||
|
|
||||||
|
override fun login(context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun logout(context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLoggedIn(context: Context): Boolean = true
|
||||||
|
|
||||||
|
override fun addPost(
|
||||||
|
publishSubject: PublishSubject<Any>,
|
||||||
|
context: Context,
|
||||||
|
text: String,
|
||||||
|
images: List<Bitmap>,
|
||||||
|
imagePaths: List<String>
|
||||||
|
) {
|
||||||
|
val snapCreativeKitApi = SnapCreative.getApi(context)
|
||||||
|
|
||||||
|
val content = imagePaths.map {
|
||||||
|
val snapMediaFactory = SnapCreative.getMediaFactory(context)
|
||||||
|
val photoFile = try {
|
||||||
|
snapMediaFactory.getSnapPhotoFromFile(File(it))
|
||||||
|
} catch (e: SnapMediaSizeException) {
|
||||||
|
Log.e(getName(), e.toString())
|
||||||
|
null
|
||||||
|
}
|
||||||
|
SnapPhotoContent(photoFile!!)
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
if (text.isNotBlank()) {
|
||||||
|
content.captionText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
snapCreativeKitApi.sendWithCompletionHandler(
|
||||||
|
content,
|
||||||
|
object : SnapCreativeKitCompletionCallback {
|
||||||
|
override fun onSendSuccess() {
|
||||||
|
publishSubject.onNext(Any())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSendFailed(p0: SnapCreativeKitSendError?) {
|
||||||
|
publishSubject.onError(Exception("Snapchat error"))
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPosts(context: Context): Single<List<Post>> {
|
||||||
|
return Single.just(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLogo(): Int = R.drawable.ic_icon_snapchat
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleButtonView(context: Context): (SocialWithButtonRecycler.ViewHolder) -> Unit =
|
||||||
|
{
|
||||||
|
it.image.setImageResource(getLogo())
|
||||||
|
it.textView.text = getName()
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
package pl.edu.amu.wmi.socialaggregator.socialplatforms
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.subjects.PublishSubject
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
|
||||||
|
|
||||||
interface SocialPlatform {
|
interface SocialPlatform {
|
||||||
|
|
||||||
@ -9,6 +13,12 @@ interface SocialPlatform {
|
|||||||
fun login(context: Context)
|
fun login(context: Context)
|
||||||
fun logout(context: Context)
|
fun logout(context: Context)
|
||||||
fun isLoggedIn(context: Context): Boolean
|
fun isLoggedIn(context: Context): Boolean
|
||||||
fun addPost(context: Context, text: String, images: List<Bitmap>)
|
fun addPost(publishSubject: PublishSubject<Any>,
|
||||||
fun getPosts(context: Context): List<String>
|
context: Context, text: String, images: List<Bitmap>,
|
||||||
|
imagePaths: List<String>)
|
||||||
|
|
||||||
|
fun getPosts(context: Context): Single<List<Post>>
|
||||||
|
fun getLogo(): Int
|
||||||
|
fun handleButtonView(context: Context): (SocialWithButtonRecycler.ViewHolder) -> Unit = {}
|
||||||
|
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
||||||
}
|
}
|
@ -1,14 +1,19 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.utils
|
package pl.edu.amu.wmi.socialaggregator.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import pl.edu.amu.wmi.socialaggregator.socialplatforms.FacebookMock
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Facebook
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Snapchat
|
||||||
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
||||||
|
|
||||||
object SocialPlatformsManager {
|
object SocialPlatformsManager {
|
||||||
private val IMPLEMENTED_PLATFORMS = listOf<SocialPlatform>(
|
private val IMPLEMENTED_PLATFORMS = listOf(
|
||||||
FacebookMock()
|
// FacebookMock()
|
||||||
|
Facebook(),
|
||||||
|
Snapchat()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun getAll() = IMPLEMENTED_PLATFORMS
|
||||||
|
|
||||||
fun getLoggedIn(context: Context) =
|
fun getLoggedIn(context: Context) =
|
||||||
IMPLEMENTED_PLATFORMS.filter { it.isLoggedIn(context) }.toMutableList()
|
IMPLEMENTED_PLATFORMS.filter { it.isLoggedIn(context) }.toMutableList()
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
|
||||||
|
|
||||||
|
object Utils {
|
||||||
|
|
||||||
|
fun convertDpToPixel(dp: Float, context: Context): Float {
|
||||||
|
return dp * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.viewholders
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.NotApplicableException
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Post
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
class PostDetailsRecycler(
|
||||||
|
postsObservable: Observable<List<Post>>
|
||||||
|
) : RecyclerView.Adapter<PostDetailsRecycler.ViewHolder>() {
|
||||||
|
|
||||||
|
val posts = emptyList<Post>().toMutableList()
|
||||||
|
|
||||||
|
init {
|
||||||
|
postsObservable
|
||||||
|
.subscribe {
|
||||||
|
posts.addAll(it)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val layout = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.item_post, parent, false) as ConstraintLayout
|
||||||
|
|
||||||
|
val dateTime = layout.findViewById<TextView>(R.id.postDateTimeTextView)
|
||||||
|
val postContent = layout.findViewById<TextView>(R.id.postContentTextView)
|
||||||
|
val imageView = layout.findViewById<ImageView>(R.id.postSocialImage)
|
||||||
|
val imagesContainer = layout.findViewById<LinearLayout>(R.id.imagesContainer)
|
||||||
|
|
||||||
|
return ViewHolder(layout, dateTime, postContent, imageView, imagesContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return posts.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val post = posts[position]
|
||||||
|
holder.dateTime.text = formatDate(post.dateTime)
|
||||||
|
holder.postContent.text = post.content
|
||||||
|
holder.postContent.visibility = if (post.content.isBlank()) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
|
holder.imageView.setImageResource(post.social.getLogo())
|
||||||
|
|
||||||
|
holder.imagesContainer.apply {
|
||||||
|
removeAllViews()
|
||||||
|
|
||||||
|
|
||||||
|
post {
|
||||||
|
|
||||||
|
val imgWidth = (parent as ConstraintLayout).width / post.images.size - 10
|
||||||
|
post.images.forEach {
|
||||||
|
val imageView = ImageView(context)
|
||||||
|
addView(imageView)
|
||||||
|
|
||||||
|
imageView.apply {
|
||||||
|
(layoutParams as ViewGroup.MarginLayoutParams)
|
||||||
|
.apply {
|
||||||
|
marginEnd = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Picasso.with(context)
|
||||||
|
.load(it)
|
||||||
|
.placeholder(android.R.drawable.ic_menu_gallery)
|
||||||
|
.resize(imgWidth, imgWidth)
|
||||||
|
.centerCrop()
|
||||||
|
.into(imageView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatDate(str: String): String {
|
||||||
|
val regex = """(\d+-\d+-\d+)T(\d+:\d+:\d+)""".toRegex()
|
||||||
|
val matchResult = regex.find(str)
|
||||||
|
val (date, time) = matchResult!!.destructured
|
||||||
|
|
||||||
|
return "$date $time"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(
|
||||||
|
root: ConstraintLayout,
|
||||||
|
val dateTime: TextView,
|
||||||
|
val postContent: TextView,
|
||||||
|
val imageView: ImageView,
|
||||||
|
val imagesContainer: LinearLayout
|
||||||
|
|
||||||
|
) : RecyclerView.ViewHolder(root)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package pl.edu.amu.wmi.socialaggregator.viewholders
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.Single
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.NotApplicableException
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Post
|
||||||
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
class PostSummaryRecycler(
|
||||||
|
socialsObservable: Observable<Pair<SocialPlatform, List<Post>?>>
|
||||||
|
) : RecyclerView.Adapter<PostSummaryRecycler.ViewHolder>() {
|
||||||
|
|
||||||
|
private val socials = mutableListOf<Pair<SocialPlatform, List<Post>?>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
socialsObservable
|
||||||
|
.doOnNext {
|
||||||
|
socials.add(it)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val layout = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.previous_posts, parent, false) as ConstraintLayout
|
||||||
|
|
||||||
|
val socialName = layout.findViewById<TextView>(R.id.socialTextView)
|
||||||
|
val postCount = layout.findViewById<TextView>(R.id.post)
|
||||||
|
val imageView = layout.findViewById<ImageView>(R.id.postSocialImage)
|
||||||
|
|
||||||
|
return ViewHolder(layout, socialName, postCount, imageView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return socials.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val (social, posts) = socials[position]
|
||||||
|
val count = posts?.size
|
||||||
|
holder.socialName.text = social.getName()
|
||||||
|
holder.postCount.text = count?.let {
|
||||||
|
count.toString() + if (count > 1) " posts" else " post"
|
||||||
|
} ?: "Not applicable"
|
||||||
|
holder.imageView.setImageResource(social.getLogo())
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(
|
||||||
|
root: ConstraintLayout,
|
||||||
|
val socialName: TextView,
|
||||||
|
val postCount: TextView,
|
||||||
|
val imageView: ImageView
|
||||||
|
) : RecyclerView.ViewHolder(root)
|
||||||
|
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.viewholders
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.jakewharton.rxbinding2.view.RxView
|
|
||||||
import io.reactivex.disposables.Disposable
|
|
||||||
import pl.edu.amu.wmi.socialaggregator.R
|
|
||||||
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
|
||||||
|
|
||||||
class PostTextRecycler(
|
|
||||||
val socials: List<Pair<SocialPlatform, Int>>) : RecyclerView.Adapter<PostTextRecycler.ViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
val layout = LayoutInflater.from(parent.context)
|
|
||||||
.inflate(R.layout.previous_posts, parent, false) as ConstraintLayout
|
|
||||||
|
|
||||||
val socialName = layout.findViewById<TextView>(R.id.socialTextView)
|
|
||||||
val postCount = layout.findViewById<TextView>(R.id.postCountTextView)
|
|
||||||
|
|
||||||
return ViewHolder(layout, socialName, postCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return socials.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
val pair = socials[position]
|
|
||||||
holder.socialName.text = pair.first.getName()
|
|
||||||
val count = pair.second
|
|
||||||
holder.postCount.text = count.toString() + if (count > 1) " posts" else " post"
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder(
|
|
||||||
root: ConstraintLayout,
|
|
||||||
val socialName: TextView,
|
|
||||||
val postCount: TextView
|
|
||||||
) : RecyclerView.ViewHolder(root)
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
package pl.edu.amu.wmi.socialaggregator.viewholders
|
package pl.edu.amu.wmi.socialaggregator.viewholders
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
@ -11,11 +12,10 @@ import io.reactivex.disposables.Disposable
|
|||||||
import pl.edu.amu.wmi.socialaggregator.R
|
import pl.edu.amu.wmi.socialaggregator.R
|
||||||
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
|
||||||
|
|
||||||
class SocialWithImageRecycler(
|
class SocialWithButtonRecycler(
|
||||||
val availableSocials: List<SocialPlatform>,
|
val availableSocials: List<SocialPlatform>,
|
||||||
private val imageResource: Int,
|
val context: Context
|
||||||
private val action: (SocialPlatform) -> Unit
|
) : RecyclerView.Adapter<SocialWithButtonRecycler.ViewHolder>() {
|
||||||
) : RecyclerView.Adapter<SocialWithImageRecycler.ViewHolder>() {
|
|
||||||
|
|
||||||
private val disposables = HashMap<SocialPlatform, Disposable>()
|
private val disposables = HashMap<SocialPlatform, Disposable>()
|
||||||
|
|
||||||
@ -34,18 +34,18 @@ class SocialWithImageRecycler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val social = availableSocials[position]
|
availableSocials[position].handleButtonView(context)(holder)
|
||||||
holder.text.text = social.getName()
|
// holder.textView.text = social.getName()
|
||||||
holder.image.setImageResource(imageResource)
|
// holder.image.setImageResource(imageResource ?: social.getLogo())
|
||||||
|
//
|
||||||
disposables[social] = RxView.clicks(holder.image)
|
// disposables[social] = RxView.clicks(holder.image)
|
||||||
.map { social }
|
// .map { social }
|
||||||
.subscribe(action)
|
// .subscribe(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(
|
class ViewHolder(
|
||||||
root: ConstraintLayout,
|
root: ConstraintLayout,
|
||||||
val text: TextView,
|
val textView: TextView,
|
||||||
val image: ImageView
|
val image: ImageView
|
||||||
) : RecyclerView.ViewHolder(root)
|
) : RecyclerView.ViewHolder(root)
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_icon_snapchat.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-hdpi/logo.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
app/src/main/res/drawable-ldpi/logo.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_icon_snapchat.png
Normal file
After Width: | Height: | Size: 725 B |
BIN
app/src/main/res/drawable-mdpi/logo.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_icon_snapchat.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/drawable-xhdpi/logo.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_icon_snapchat.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/drawable-xxhdpi/logo.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_icon_snapchat.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/logo.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
@ -14,7 +14,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@mipmap/ic_launcher" />
|
app:srcCompat="@drawable/logo" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/connectedSocialsTextView"
|
android:id="@+id/connectedSocialsTextView"
|
||||||
@ -22,14 +22,14 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="Connected Socials"
|
android:text="Available Socials"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/connectedSocialsRecyclerView"
|
android:id="@+id/connectedSocialsRecyclerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="150dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
@ -41,17 +41,6 @@
|
|||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/connectedSocialsButton"
|
|
||||||
style="@style/Widget.AppCompat.Button.Colored"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="Connect new"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/connectedSocialsRecyclerView" />
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -63,7 +52,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/connectedSocialsButton" />
|
app:layout_constraintTop_toBottomOf="@+id/connectedSocialsRecyclerView" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView2"
|
android:id="@+id/textView2"
|
||||||
@ -129,11 +118,12 @@
|
|||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:tint="#00FFFFFF"
|
android:tint="@android:color/background_light"
|
||||||
app:fabSize="auto"
|
app:fabSize="auto"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/previousPostsButton"
|
app:layout_constraintTop_toBottomOf="@+id/previousPostsButton"
|
||||||
app:srcCompat="@android:drawable/ic_input_add" />
|
app:layout_constraintVertical_bias="1.0"
|
||||||
|
app:srcCompat="@mipmap/ic_opaque_add" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
25
app/src/main/res/layout/activity_post_history.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".activity.PostHistoryActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/AppTheme.AppBarOverlay">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/content_post_history" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -61,10 +61,11 @@
|
|||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:tint="@android:color/background_light"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/availableSocials"
|
app:layout_constraintTop_toBottomOf="@+id/availableSocials"
|
||||||
app:srcCompat="@drawable/ic_add_circle" />
|
app:srcCompat="@mipmap/ic_opaque_send" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
23
app/src/main/res/layout/content_post_history.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?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"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:context=".activity.PostHistoryActivity"
|
||||||
|
tools:showIn="@layout/activity_post_history">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/postHistoryRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
58
app/src/main/res/layout/item_post.xml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?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:id="@+id/linearLayout2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/postContentTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="TextView"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/postDateTimeTextView"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/postSocialImage"
|
||||||
|
android:layout_width="@dimen/social_logo_size"
|
||||||
|
android:layout_height="@dimen/social_logo_size"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.0"
|
||||||
|
app:srcCompat="@mipmap/ic_logo_facebook" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/postDateTimeTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="TextView"
|
||||||
|
android:textSize="10sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/imagesContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/postContentTextView" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -13,32 +13,32 @@
|
|||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:text="Bla bla bla"
|
android:text="Bla bla bla"
|
||||||
android:textColor="@android:color/primary_text_light"
|
android:textColor="@android:color/primary_text_light"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/postCountTextView"
|
app:layout_constraintBottom_toTopOf="@+id/post"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/imageView3"
|
app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/imageView3"
|
app:layout_constraintTop_toTopOf="@+id/postSocialImage"
|
||||||
app:layout_constraintVertical_chainStyle="packed" />
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/postCountTextView"
|
android:id="@+id/post"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:text="Bla bla bla"
|
android:text="Bla bla bla"
|
||||||
android:textColor="@android:color/secondary_text_light"
|
android:textColor="@android:color/secondary_text_light"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/imageView3"
|
app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/socialTextView" />
|
app:layout_constraintTop_toBottomOf="@+id/socialTextView" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imageView3"
|
android:id="@+id/postSocialImage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/social_logo_size"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/social_logo_size"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@mipmap/ic_launcher_round" />
|
app:srcCompat="@mipmap/ic_logo_facebook" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -9,23 +9,23 @@
|
|||||||
android:id="@+id/socialPlatformName"
|
android:id="@+id/socialPlatformName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/socialPlatformImage"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/socialPlatformImage"
|
app:layout_constraintEnd_toStartOf="@+id/socialPlatformImage"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="@+id/socialPlatformImage"
|
||||||
tools:text="Some Sample Text" />
|
tools:text="Some Sample Text" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/socialPlatformImage"
|
android:id="@+id/socialPlatformImage"
|
||||||
android:layout_width="0dp"
|
android:layout_width="@dimen/social_logo_size"
|
||||||
android:layout_height="0dp"
|
android:layout_height="@dimen/social_logo_size"
|
||||||
android:layout_marginTop="-4dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="-4dp"
|
android:layout_marginBottom="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/socialPlatformName"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1:1"
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/socialPlatformName"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:srcCompat="@mipmap/ic_launcher_round" />
|
tools:srcCompat="@mipmap/ic_logo_facebook" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_logo_facebook.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_opaque_add.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_opaque_remove.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
app/src/main/res/mipmap-hdpi/ic_opaque_send.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_logo_facebook.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_opaque_add.png
Normal file
After Width: | Height: | Size: 742 B |
BIN
app/src/main/res/mipmap-mdpi/ic_opaque_remove.png
Normal file
After Width: | Height: | Size: 459 B |
BIN
app/src/main/res/mipmap-mdpi/ic_opaque_send.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 6.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_logo_facebook.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_opaque_add.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_opaque_remove.png
Normal file
After Width: | Height: | Size: 907 B |
BIN
app/src/main/res/mipmap-xhdpi/ic_opaque_send.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_logo_facebook.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_opaque_add.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_opaque_remove.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_opaque_send.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_logo_facebook.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_opaque_add.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_opaque_remove.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_opaque_send.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#4267b2</color>
|
<color name="colorPrimary">#512DA8</color>
|
||||||
<color name="colorPrimaryDark">#4267b2</color>
|
<color name="colorAccent">#5E35B1</color>
|
||||||
<color name="colorAccent">#4267b2</color>
|
<color name="colorPrimaryDark">#4A148C</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<dimen name="fab_margin">16dp</dimen>
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
<dimen name="social_logo_size">32dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Social Aggregator</string>
|
<string name="app_name">Social Aggregator</string>
|
||||||
<string name="title_activity_new_post">NewPostActivity</string>
|
<string name="title_activity_new_post">Create new post!</string>
|
||||||
<string name="title_activity_add_social">AddSocialActivity</string>
|
<string name="title_activity_add_social">AddSocialActivity</string>
|
||||||
|
<string name="title_activity_post_history">Posts history</string>
|
||||||
|
|
||||||
|
<string name="facebook_app_id">2437098899888167</string>
|
||||||
|
<string name="fb_login_protocol_scheme">fb2437098899888167</string>
|
||||||
|
|
||||||
|
<!-- <key>SCSDKScopes</key>-->
|
||||||
|
<!-- <array name="snap_connect_scopes">-->
|
||||||
|
<!-- <string>https://auth.snapchat.com/oauth2/api/user.bitmoji.avatar</string>-->
|
||||||
|
<!-- <!– other scopes you might have... –>-->
|
||||||
|
<!-- </array>-->
|
||||||
</resources>
|
</resources>
|
||||||
|
7
app/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<root-path name="root" path="." />
|
||||||
|
|
||||||
|
<external-cache-path name="external_files" path="."/>
|
||||||
|
<external-path name="external_files" path="."/>
|
||||||
|
</paths>
|
20
build.gradle
@ -19,10 +19,28 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
||||||
|
maven {
|
||||||
|
url "https://storage.googleapis.com/snap-kit-build/maven"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ext {
|
||||||
|
buildToolsVersion = "28.0.3"
|
||||||
|
androidxLibVersion = "1.0.0"
|
||||||
|
coreVersion = "1.3.0-alpha03"
|
||||||
|
extJUnitVersion = "1.1.2-alpha03"
|
||||||
|
runnerVersion = "1.3.0-alpha03"
|
||||||
|
rulesVersion = "1.3.0-alpha03"
|
||||||
|
espressoVersion = "3.3.0-alpha03"
|
||||||
|
extJUnitVersion = "1.1.2-alpha03"
|
||||||
|
extTruthVersion = "1.3.0-alpha03"
|
||||||
|
robolectricVersion = "4.3.1"
|
||||||
|
}
|
@ -19,3 +19,4 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
android.enableUnitTestBinaryResources=true
|
||||||
|