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-login:[5,6)'
implementation 'com.facebook.android:facebook-share:[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.AccessToken
import com.facebook.login.LoginManager import com.facebook.login.LoginManager
import com.facebook.share.widget.ShareDialog import com.facebook.share.widget.ShareDialog
import io.reactivex.Observable
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -86,8 +87,13 @@ class MainActivity : AppCompatActivity() {
previousPostsRecyclerView.apply { previousPostsRecyclerView.apply {
layoutManager = LinearLayoutManager(this@MainActivity) layoutManager = LinearLayoutManager(this@MainActivity)
adapter = PostSummaryRecycler(SocialPlatformsManager.getLoggedIn(this@MainActivity) adapter = PostSummaryRecycler(
.map { it to it.getPosts(this@MainActivity).size }) 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.R
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
import java.util.*
class NewPostActivity : AppCompatActivity() { class NewPostActivity : AppCompatActivity() {
@ -46,33 +45,20 @@ class NewPostActivity : AppCompatActivity() {
adapter = availablesRecycler 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) RxView.clicks(publishPost)
.filter { file != null }
.subscribe { .subscribe {
availablesRecycler.chips.entries availablesRecycler.chips.entries
.filter { file != null }
.filter { (_, chip) -> chip.isChecked } .filter { (_, chip) -> chip.isChecked }
.forEach { (social, _) -> .forEach { (social, _) ->
val bitmap = BitmapFactory.decodeFile(file!!) val bitmap = BitmapFactory.decodeFile(file!!)
social.addPost(this, postText.text?.toString() ?: "test", listOf(bitmap)) 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() .show()
} }
finish()
} }
RxView.clicks(imageView2) RxView.clicks(imageView2)
.subscribe { .subscribe {
val getIntent = Intent(Intent.ACTION_GET_CONTENT) 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 android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager 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.activity_post_history.*
import kotlinx.android.synthetic.main.content_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.utils.SocialPlatformsManager
import pl.edu.amu.wmi.socialaggregator.viewholders.PostDetailsRecycler import pl.edu.amu.wmi.socialaggregator.viewholders.PostDetailsRecycler
import androidx.recyclerview.widget.DividerItemDecoration
class PostHistoryActivity : AppCompatActivity() { class PostHistoryActivity : AppCompatActivity() {
@ -22,9 +25,16 @@ class PostHistoryActivity : AppCompatActivity() {
postHistoryRecycler.apply { postHistoryRecycler.apply {
layoutManager = LinearLayoutManager(this@PostHistoryActivity) layoutManager = LinearLayoutManager(this@PostHistoryActivity)
adapter = PostDetailsRecycler( adapter = PostDetailsRecycler(
SocialPlatformsManager.getLoggedIn(this@PostHistoryActivity) Observable.fromIterable(SocialPlatformsManager.getLoggedIn(this@PostHistoryActivity))
.flatMap { it.getPosts(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.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.icu.text.SimpleDateFormat
import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import com.facebook.CallbackManager import com.facebook.*
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.login.LoginResult import com.facebook.login.LoginResult
import com.facebook.login.widget.LoginButton import com.facebook.login.widget.LoginButton
import pl.edu.amu.wmi.socialaggregator.utils.Utils import pl.edu.amu.wmi.socialaggregator.utils.Utils
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
import com.facebook.AccessToken
import com.facebook.login.LoginManager import com.facebook.login.LoginManager
import com.facebook.share.Sharer import com.facebook.share.Sharer
import com.facebook.share.model.ShareMediaContent import com.facebook.share.model.ShareMediaContent
import com.facebook.share.model.SharePhoto import com.facebook.share.model.SharePhoto
import com.facebook.share.widget.ShareDialog import com.facebook.share.widget.ShareDialog
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject 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.R
import java.lang.Exception import java.lang.Exception
@ -78,8 +80,69 @@ class Facebook : SocialPlatform {
return publishSubject return publishSubject
} }
override fun getPosts(context: Context): List<Post> { override fun getPosts(context: Context): Single<List<Post>> {
return emptyList() 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 = 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.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.util.Log 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.R
import pl.edu.amu.wmi.socialaggregator.utils.InternalStorage import pl.edu.amu.wmi.socialaggregator.utils.InternalStorage
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -39,7 +41,7 @@ abstract class FacebookMock : SocialPlatform {
return InternalStorage.getFileOrDir(context, "logins/facebook")?.exists() ?: false 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") val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
if (postsDir != null) { if (postsDir != null) {
val postDir = File(postsDir, System.currentTimeMillis().toString()) val postDir = File(postsDir, System.currentTimeMillis().toString())
@ -62,15 +64,21 @@ abstract class FacebookMock : SocialPlatform {
} else { } else {
Log.e(TAG, "Could not create posts directory") 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") val postsDir = InternalStorage.getFileOrDir(context, "posts/facebook")
return postsDir?.listFiles()?.flatMap { return Single.just(postsDir?.listFiles()?.flatMap {
it.listFiles().map { it.listFiles().map {
val dt = Date(it.lastModified()) 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 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.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import io.reactivex.Single
import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.PublishSubject
import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler import pl.edu.amu.wmi.socialaggregator.viewholders.SocialWithButtonRecycler
@ -13,7 +14,7 @@ interface SocialPlatform {
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>): PublishSubject<Any> 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 getLogo(): Int
fun handleButtonView(context: Context): (SocialWithButtonRecycler.ViewHolder) -> Unit = {} fun handleButtonView(context: Context): (SocialWithButtonRecycler.ViewHolder) -> Unit = {}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {} fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}

View File

@ -1,16 +1,30 @@
package pl.edu.amu.wmi.socialaggregator.viewholders package pl.edu.amu.wmi.socialaggregator.viewholders
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView 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.R
import pl.edu.amu.wmi.socialaggregator.socialplatforms.Post import pl.edu.amu.wmi.socialaggregator.socialplatforms.Post
class PostDetailsRecycler( 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 { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layout = LayoutInflater.from(parent.context) val layout = LayoutInflater.from(parent.context)
@ -19,8 +33,9 @@ class PostDetailsRecycler(
val dateTime = layout.findViewById<TextView>(R.id.postDateTimeTextView) val dateTime = layout.findViewById<TextView>(R.id.postDateTimeTextView)
val postContent = layout.findViewById<TextView>(R.id.postContentTextView) val postContent = layout.findViewById<TextView>(R.id.postContentTextView)
val imageView = layout.findViewById<ImageView>(R.id.postSocialImage) 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 { override fun getItemCount(): Int {
@ -31,14 +46,40 @@ class PostDetailsRecycler(
val post = posts[position] val post = posts[position]
holder.dateTime.text = post.dateTime holder.dateTime.text = post.dateTime
holder.postContent.text = post.content 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( class ViewHolder(
root: ConstraintLayout, root: ConstraintLayout,
val dateTime: TextView, val dateTime: TextView,
val postContent: TextView, val postContent: TextView,
val imageView: ImageView val imageView: ImageView,
val imagesContainer: LinearLayout
) : RecyclerView.ViewHolder(root) ) : RecyclerView.ViewHolder(root)
} }

View File

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

View File

@ -29,7 +29,7 @@
<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="200dp" 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"

View File

@ -5,6 +5,7 @@
android:id="@+id/linearLayout2" android:id="@+id/linearLayout2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
@ -13,13 +14,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView" android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/postSocialImage" app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
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_toBottomOf="@+id/postDateTimeTextView" /> app:layout_constraintTop_toBottomOf="@+id/postDateTimeTextView"
tools:visibility="visible" />
<ImageView <ImageView
android:id="@+id/postSocialImage" android:id="@+id/postSocialImage"
@ -36,10 +36,22 @@
android:id="@+id/postDateTimeTextView" android:id="@+id/postDateTimeTextView"
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:text="TextView" android:text="TextView"
android:textSize="10sp" android:textSize="10sp"
app:layout_constraintEnd_toStartOf="@+id/postSocialImage" app:layout_constraintEnd_toStartOf="@+id/postSocialImage"
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="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> </androidx.constraintlayout.widget.ConstraintLayout>

View File

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