Skip to content

Commit 110833b

Browse files
authored
Merge pull request #4 from ukrainefield/feature/bookmarks
Feature/bookmarks
2 parents 9012777 + aa34878 commit 110833b

File tree

13 files changed

+288
-9
lines changed

13 files changed

+288
-9
lines changed

app/.idea/deploymentTargetDropDown.xml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/.idea/misc.xml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,6 @@ dependencies {
9595
implementation 'com.github.piasy:GlideImageViewFactory:1.8.1'
9696
implementation 'com.google.firebase:firebase-crashlytics:18.2.8'
9797
implementation 'androidx.preference:preference:1.1.0'
98+
implementation 'com.google.code.gson:gson:2.9.0'
9899

99100
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package nl.gardensnakes.ukrainefield
2+
3+
import android.os.Bundle
4+
import androidx.fragment.app.Fragment
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.TextView
9+
import androidx.recyclerview.widget.LinearLayoutManager
10+
import androidx.recyclerview.widget.RecyclerView
11+
import com.google.firebase.analytics.FirebaseAnalytics
12+
import nl.gardensnakes.ukrainefield.data.remote.SavedPreferences
13+
import nl.gardensnakes.ukrainefield.helper.BookmarkHelper
14+
import nl.gardensnakes.ukrainefield.view.adapter.FeedCardAdapter
15+
16+
class BookmarkFragment : Fragment() {
17+
18+
private lateinit var feedRecyclerView: RecyclerView
19+
private lateinit var noBookmarksText: TextView
20+
21+
private lateinit var feedCardAdapter: FeedCardAdapter
22+
private var useProxyServer: Boolean = false
23+
private lateinit var mFirebaseAnalytics: FirebaseAnalytics
24+
25+
override fun onCreate(savedInstanceState: Bundle?) {
26+
super.onCreate(savedInstanceState)
27+
}
28+
29+
override fun onCreateView(
30+
inflater: LayoutInflater, container: ViewGroup?,
31+
savedInstanceState: Bundle?
32+
): View? {
33+
// Inflate the layout for this fragment
34+
val view = inflater.inflate(R.layout.fragment_bookmark, container, false)
35+
36+
mFirebaseAnalytics = FirebaseAnalytics.getInstance(view.context);
37+
38+
useProxyServer = SavedPreferences.useProxyServer(requireContext())
39+
feedRecyclerView = view.findViewById(R.id.bookmark_recycler_view)
40+
noBookmarksText = view.findViewById(R.id.bookmark_no_bookmark_text)
41+
42+
val bookmarkedItems = BookmarkHelper().getAll(view.context)
43+
44+
if(bookmarkedItems == null || bookmarkedItems.isEmpty()){
45+
noBookmarksText.visibility = View.VISIBLE
46+
feedRecyclerView.visibility = View.GONE
47+
}
48+
else {
49+
noBookmarksText.visibility = View.GONE
50+
feedRecyclerView.visibility = View.VISIBLE
51+
feedCardAdapter = FeedCardAdapter(bookmarkedItems.sortedByDescending { it.epochTime}, true)
52+
feedRecyclerView.adapter = feedCardAdapter
53+
feedRecyclerView.layoutManager = LinearLayoutManager(view.context);
54+
}
55+
56+
return view
57+
}
58+
}

app/app/src/main/java/nl/gardensnakes/ukrainefield/MainActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class MainActivity : AppCompatActivity() {
3030
R.id.ukraine_map -> {
3131
fragment = MapFragment()
3232
}
33+
R.id.bookmarks -> {
34+
fragment = BookmarkFragment()
35+
}
3336
R.id.settings -> {
3437
fragment = SettingsFragment()
3538
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package nl.gardensnakes.ukrainefield.helper
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import com.google.gson.Gson
6+
import com.google.gson.reflect.TypeToken
7+
import nl.gardensnakes.ukrainefield.data.remote.dto.feed.FeedMessageResponse
8+
import java.io.*
9+
10+
class BookmarkHelper {
11+
private var bookmarkedFeed: ArrayList<FeedMessageResponse>? = null
12+
13+
fun bookmark(feedItem: FeedMessageResponse, context: Context) {
14+
val feedMessages = getAll(context) as ArrayList<FeedMessageResponse>
15+
16+
val found = feedMessages.firstOrNull {
17+
it.messageURL == feedItem.messageURL
18+
}
19+
20+
if(found == null) {
21+
feedMessages.add(feedItem)
22+
} else {
23+
feedMessages.remove(found)
24+
}
25+
26+
val json = extractToJson(feedMessages);
27+
28+
if(json != null) saveToJsonFile(json, context)
29+
}
30+
31+
32+
fun isFavorite(messageUrl: String, context: Context): Boolean {
33+
return getAll(context)?.any {
34+
it.messageURL == messageUrl
35+
} ?: false
36+
}
37+
38+
fun getAll(context: Context?): List<FeedMessageResponse>? {
39+
return if(bookmarkedFeed != null) {
40+
return bookmarkedFeed
41+
} else {
42+
context?.let {
43+
readJsonFile(it)
44+
}?.let {
45+
extractToBookmark(it)
46+
}
47+
}
48+
}
49+
50+
private fun extractToBookmark(videos: String): List<FeedMessageResponse>? {
51+
val listOfBookmarks = Gson().fromJson<List<FeedMessageResponse>>(
52+
videos,
53+
object : TypeToken<ArrayList<FeedMessageResponse>>() {}.type
54+
) ?: ArrayList(0)
55+
56+
this.bookmarkedFeed = listOfBookmarks as ArrayList<FeedMessageResponse>
57+
58+
return listOfBookmarks
59+
}
60+
61+
private fun extractToJson(bookmarks: List<FeedMessageResponse>): String? {
62+
return Gson().toJson(
63+
bookmarks,
64+
List::class.java
65+
)
66+
}
67+
68+
private fun saveToJsonFile(json: String, context: Context) {
69+
try {
70+
val outputStreamWriter = OutputStreamWriter(
71+
context.openFileOutput(
72+
"bookmarks.txt",
73+
Context.MODE_PRIVATE
74+
)
75+
)
76+
77+
outputStreamWriter.write(json)
78+
outputStreamWriter.close()
79+
} catch (e: IOException) {
80+
Log.e("Exception", "File write failed: $e")
81+
}
82+
}
83+
84+
private fun readJsonFile(context: Context): String {
85+
var ret = ""
86+
87+
try {
88+
val inputStream: InputStream? = context.openFileInput("bookmarks.txt")
89+
if (inputStream != null) {
90+
val inputStreamReader = InputStreamReader(inputStream)
91+
val bufferedReader = BufferedReader(inputStreamReader)
92+
var receiveString: String? = ""
93+
val stringBuilder = StringBuilder()
94+
while (bufferedReader.readLine().also { receiveString = it } != null) {
95+
stringBuilder.append("\n").append(receiveString)
96+
}
97+
inputStream.close()
98+
ret = stringBuilder.toString()
99+
}
100+
} catch (e: FileNotFoundException) {
101+
Log.e("BookmarkHelper", "File not found: $e")
102+
} catch (e: IOException) {
103+
Log.e("BookmarkHelper", "Can not read file: $e")
104+
}
105+
106+
return ret
107+
}
108+
}

app/app/src/main/java/nl/gardensnakes/ukrainefield/view/adapter/FeedCardAdapter.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ import nl.gardensnakes.ukrainefield.MediaDetailActivity
1818
import nl.gardensnakes.ukrainefield.R
1919
import nl.gardensnakes.ukrainefield.data.remote.HttpRoutes
2020
import nl.gardensnakes.ukrainefield.data.remote.dto.feed.FeedMessageResponse
21+
import nl.gardensnakes.ukrainefield.helper.BookmarkHelper
2122
import nl.gardensnakes.ukrainefield.helper.PreferenceHelper
2223
import nl.gardensnakes.ukrainefield.helper.TimeHelper
24+
import java.lang.Exception
2325

2426

25-
class FeedCardAdapter(private val mList: List<FeedMessageResponse>) :
27+
class FeedCardAdapter(private var mList: List<FeedMessageResponse>, private val deleteOnUnBookmark: Boolean = false) :
2628
RecyclerView.Adapter<FeedCardAdapter.ViewHolder>() {
2729

2830
lateinit var context: Context
@@ -42,6 +44,7 @@ class FeedCardAdapter(private val mList: List<FeedMessageResponse>) :
4244
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
4345

4446
val feedData = mList[position]
47+
val bookmarkHelper = BookmarkHelper()
4548

4649
resetView(holder)
4750

@@ -69,6 +72,8 @@ class FeedCardAdapter(private val mList: List<FeedMessageResponse>) :
6972
holder.postedAtText.text =
7073
"${context.getString(R.string.posted_at)} ${TimeHelper.epochToTimeString(feedData.epochTime.toLong())}"
7174

75+
updateBookmarkText(bookmarkHelper, feedData.messageURL ?: "", holder, position)
76+
7277
if (feedData.videos.isEmpty() && feedData.images.isEmpty()) {
7378
holder.imageSlide.visibility = View.GONE
7479
} else if (feedData.videos.isNotEmpty()) {
@@ -115,13 +120,36 @@ class FeedCardAdapter(private val mList: List<FeedMessageResponse>) :
115120
startActivity(context, browserIntent, null)
116121
}
117122

123+
holder.bookmarkButton.setOnClickListener {
124+
bookmarkHelper.bookmark(feedData, context)
125+
updateBookmarkText(bookmarkHelper, feedData.messageURL ?: "", holder, position)
126+
}
127+
118128
}
119129

120130
// return the number of the items in the list
121131
override fun getItemCount(): Int {
122132
return mList.size
123133
}
124134

135+
private fun updateBookmarkText(bookmarkHelper: BookmarkHelper, messageUrl: String, holder: ViewHolder, position: Int){
136+
var isBookmarked = bookmarkHelper.isFavorite(messageUrl, context)
137+
if(isBookmarked){
138+
holder.bookmarkButton.text = context.getString(R.string.remove_bookmark)
139+
}
140+
else{
141+
holder.bookmarkButton.text = context.getString(R.string.bookmark)
142+
if(deleteOnUnBookmark){
143+
try {
144+
notifyItemRemoved(position)
145+
notifyItemRangeChanged(position, mList.size)
146+
mList = BookmarkHelper().getAll(context) ?: emptyList()
147+
mList = mList.sortedByDescending { it.epochTime }
148+
} catch(e: Exception){}
149+
}
150+
}
151+
}
152+
125153
private fun resetView(holder: ViewHolder) {
126154
holder.videoView.stopPlayback()
127155
holder.videoView.visibility = View.GONE
@@ -147,5 +175,6 @@ class FeedCardAdapter(private val mList: List<FeedMessageResponse>) :
147175
val shareView: Button = itemView.findViewById(R.id.feed_card_share_button)
148176
val browserButtonView: Button = itemView.findViewById(R.id.feed_card_browser_button)
149177
val videoView: VideoView = itemView.findViewById(R.id.feed_card_video)
178+
var bookmarkButton: Button = ItemView.findViewById(R.id.feed_card_bookmark_button)
150179
}
151180
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24"
6+
android:tint="?attr/colorControlNormal">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
10+
</vector>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24"
6+
android:tint="?attr/colorControlNormal">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z"/>
10+
</vector>

app/app/src/main/res/layout/feed_card.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@
105105
android:id="@+id/feed_card_browser_button"
106106
/>
107107
</LinearLayout>
108+
<LinearLayout
109+
android:layout_width="wrap_content"
110+
android:layout_height="wrap_content"
111+
android:layout_margin="8dp"
112+
android:orientation="horizontal">
113+
<com.google.android.material.button.MaterialButton
114+
android:layout_width="wrap_content"
115+
android:layout_height="wrap_content"
116+
android:layout_marginEnd="8dp"
117+
android:text="@string/bookmark"
118+
app:icon="@drawable/ic_baseline_no_bookmark_24"
119+
style="?attr/borderlessButtonStyle"
120+
android:id="@+id/feed_card_bookmark_button"
121+
/>
122+
</LinearLayout>
108123

109124
</LinearLayout>
110125

0 commit comments

Comments
 (0)