This commit is contained in:
Mateusz Hinc 2020-01-09 20:59:24 +01:00
parent 4c00eaaa1b
commit 066400f700
13 changed files with 198 additions and 51 deletions

View File

@ -74,4 +74,6 @@ dependencies {
implementation 'com.facebook.android:facebook-login:[5,6)'
implementation 'com.facebook.android:facebook-share:[5,6)'
implementation 'com.squareup.picasso:picasso:2.5.2'
}

View File

@ -19,6 +19,7 @@ import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
import com.facebook.AccessToken
import com.facebook.login.LoginManager
import com.facebook.share.widget.ShareDialog
import io.reactivex.Observable
class MainActivity : AppCompatActivity() {
@ -86,8 +87,13 @@ class MainActivity : AppCompatActivity() {
previousPostsRecyclerView.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = PostSummaryRecycler(SocialPlatformsManager.getLoggedIn(this@MainActivity)
.map { it to it.getPosts(this@MainActivity).size })
adapter = PostSummaryRecycler(
Observable.fromIterable(SocialPlatformsManager.getLoggedIn(this@MainActivity))
.flatMap { social ->
return@flatMap social.getPosts(this@MainActivity)
.map { social to it }
.toObservable()
})
}
}

View File

@ -20,7 +20,6 @@ import kotlinx.android.synthetic.main.content_new_post.*
import pl.edu.amu.wmi.socialaggregator.R
import pl.edu.amu.wmi.socialaggregator.utils.SocialPlatformsManager
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithToggleRecycler
import java.util.*
class NewPostActivity : AppCompatActivity() {
@ -46,33 +45,20 @@ class NewPostActivity : AppCompatActivity() {
adapter = availablesRecycler
}
Observable.fromIterable(availablesRecycler.chips.entries)
.filter { file != null }
.filter { (_, chip) -> chip.isChecked }
.flatMap { (social, _) ->
val bitmap = BitmapFactory.decodeFile(file!!)
// Toast.makeText(this, "Posted to ${social.getName()}!", Toast.LENGTH_LONG)
// .show()
social.addPost(this, postText.text?.toString() ?: "test", listOf(bitmap))
}
.take(availablesRecycler.chips.entries.count { (_, chip) -> chip.isChecked }.toLong())
.subscribe {
finish()
}
RxView.clicks(publishPost)
.filter { file != null }
.subscribe {
availablesRecycler.chips.entries
.filter { file != null }
.filter { (_, chip) -> chip.isChecked }
.forEach { (social, _) ->
val bitmap = BitmapFactory.decodeFile(file!!)
social.addPost(this, postText.text?.toString() ?: "test", listOf(bitmap))
Toast.makeText(this, "Posted to ${social.getName()}!", Toast.LENGTH_LONG)
.blockingSubscribe()
Toast.makeText(this, "Posted to ${social.getName()}!", Toast.LENGTH_SHORT)
.show()
}
finish()
}
RxView.clicks(imageView2)
.subscribe {
val getIntent = Intent(Intent.ACTION_GET_CONTENT)

View File

@ -3,12 +3,15 @@ package pl.edu.amu.wmi.socialaggregator.activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import pl.edu.amu.wmi.socialaggregator.R
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() {
@ -22,9 +25,16 @@ class PostHistoryActivity : AppCompatActivity() {
postHistoryRecycler.apply {
layoutManager = LinearLayoutManager(this@PostHistoryActivity)
adapter = PostDetailsRecycler(
SocialPlatformsManager.getLoggedIn(this@PostHistoryActivity)
.flatMap { it.getPosts(this@PostHistoryActivity) }
)
Observable.fromIterable(SocialPlatformsManager.getLoggedIn(this@PostHistoryActivity))
.flatMap {
it.getPosts(this@PostHistoryActivity)
.toObservable()
})
addItemDecoration(
DividerItemDecoration(
context,
(layoutManager as LinearLayoutManager).orientation
))
}
}

View File

@ -4,27 +4,29 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.icu.text.SimpleDateFormat
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.facebook.CallbackManager
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.*
import com.facebook.login.LoginResult
import com.facebook.login.widget.LoginButton
import pl.edu.amu.wmi.socialaggregator.utils.Utils
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
import com.facebook.AccessToken
import com.facebook.login.LoginManager
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 java.lang.Exception
@ -78,8 +80,69 @@ class Facebook : SocialPlatform {
return publishSubject
}
override fun getPosts(context: Context): List<Post> {
return emptyList()
override fun getPosts(context: Context): Single<List<Post>> {
return Single.just(AccessToken.getCurrentAccessToken())
.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))
}
}
}
.subscribeOn(Schedulers.io())
.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 =

View File

@ -3,6 +3,8 @@ package pl.edu.amu.wmi.socialaggregator.socialplatforms
import android.content.Context
import android.graphics.Bitmap
import android.util.Log
import io.reactivex.Single
import io.reactivex.subjects.PublishSubject
import pl.edu.amu.wmi.socialaggregator.R
import pl.edu.amu.wmi.socialaggregator.utils.InternalStorage
import java.io.ByteArrayOutputStream
@ -39,7 +41,7 @@ abstract class FacebookMock : SocialPlatform {
return InternalStorage.getFileOrDir(context, "logins/facebook")?.exists() ?: false
}
override fun addPost(context: Context, text: String, images: List<Bitmap>) {
override fun addPost(context: Context, text: String, images: List<Bitmap>): PublishSubject<Any> {
val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
if (postsDir != null) {
val postDir = File(postsDir, System.currentTimeMillis().toString())
@ -62,15 +64,21 @@ abstract class FacebookMock : SocialPlatform {
} else {
Log.e(TAG, "Could not create posts directory")
}
return PublishSubject.create()
}
override fun getPosts(context: Context): List<Post> {
override fun getPosts(context: Context): Single<List<Post>> {
val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
return postsDir?.listFiles()?.flatMap {
return Single.just(postsDir?.listFiles()?.flatMap {
it.listFiles().map {
val dt = Date(it.lastModified())
Post(this, it.readText(), DateFormat.getDateTimeInstance().format(dt))
Post(
this,
it.readText(),
DateFormat.getDateTimeInstance().format(dt),
emptyList()
)
}
}?.toList() ?: emptyList()
}?.toList() ?: emptyList())
}
}

View File

@ -1,3 +1,8 @@
package pl.edu.amu.wmi.socialaggregator.socialplatforms
data class Post(val social: SocialPlatform, val content: String, val dateTime: String)
data class Post(
val social: SocialPlatform,
val content: String,
val dateTime: String,
val images: List<String>
)

View File

@ -3,6 +3,7 @@ package pl.edu.amu.wmi.socialaggregator.socialplatforms
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import io.reactivex.Single
import io.reactivex.subjects.PublishSubject
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
@ -13,7 +14,7 @@ interface SocialPlatform {
fun logout(context: Context)
fun isLoggedIn(context: Context): Boolean
fun addPost(context: Context, text: String, images: List<Bitmap>): PublishSubject<Any>
fun getPosts(context: Context): List<Post>
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?) {}

View File

@ -1,16 +1,30 @@
package pl.edu.amu.wmi.socialaggregator.viewholders
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.Post
class PostDetailsRecycler(
val posts: List<Post>) : RecyclerView.Adapter<PostDetailsRecycler.ViewHolder>() {
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)
@ -19,8 +33,9 @@ class PostDetailsRecycler(
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)
return ViewHolder(layout, dateTime, postContent, imageView, imagesContainer)
}
override fun getItemCount(): Int {
@ -31,14 +46,40 @@ class PostDetailsRecycler(
val post = posts[position]
holder.dateTime.text = 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.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(250, 250)
.centerCrop()
.into(imageView)
}
}
}
class ViewHolder(
root: ConstraintLayout,
val dateTime: TextView,
val postContent: TextView,
val imageView: ImageView
val imageView: ImageView,
val imagesContainer: LinearLayout
) : RecyclerView.ViewHolder(root)
}

View File

@ -6,11 +6,23 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import pl.edu.amu.wmi.socialaggregator.R
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Post
import pl.edu.amu.wmi.socialaggregator.socialplatforms.SocialPlatform
class PostSummaryRecycler(
val socials: List<Pair<SocialPlatform, Int>>) : RecyclerView.Adapter<PostSummaryRecycler.ViewHolder>() {
socialsObservable: Observable<Pair<SocialPlatform, List<Post>>>
) : RecyclerView.Adapter<PostSummaryRecycler.ViewHolder>() {
init {
socialsObservable.subscribe {
socials.add(it)
notifyDataSetChanged()
}
}
val socials = emptyList<Pair<SocialPlatform, List<Post>>>().toMutableList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layout = LayoutInflater.from(parent.context)
@ -28,7 +40,8 @@ class PostSummaryRecycler(
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val (social, count) = socials[position]
val (social, posts) = socials[position]
val count = posts.size
holder.socialName.text = social.getName()
holder.postCount.text = count.toString() + if (count > 1) " posts" else " post"
holder.imageView.setImageResource(social.getLogo())

View File

@ -29,7 +29,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/connectedSocialsRecyclerView"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_height="150dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"

View File

@ -5,6 +5,7 @@
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<TextView
@ -13,13 +14,12 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/postDateTimeTextView" />
app:layout_constraintTop_toBottomOf="@+id/postDateTimeTextView"
tools:visibility="visible" />
<ImageView
android:id="@+id/postSocialImage"
@ -36,10 +36,22 @@
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="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
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>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#845EC2</color>
<color name="colorAccent">#4E8397</color>
<color name="colorPrimaryDark">#D5CABD</color>
<color name="colorPrimary">#512DA8</color>
<color name="colorAccent">#5E35B1</color>
<color name="colorPrimaryDark">#4A148C</color>
</resources>