Merge branch 'feature/performance-improvements' into develop
This commit is contained in:
commit
f58a22dadb
@ -20,8 +20,8 @@ android {
|
|||||||
applicationId "ch.dissem.apps." + appName.toLowerCase()
|
applicationId "ch.dissem.apps." + appName.toLowerCase()
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 16
|
versionCode 17
|
||||||
versionName "1.0-beta16"
|
versionName "1.0-beta17"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -39,7 +39,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ext.jabitVersion = '2.0.4'
|
//ext.jabitVersion = '2.0.4'
|
||||||
ext.jabitVersion = 'feature-exports-SNAPSHOT'
|
ext.jabitVersion = 'development-SNAPSHOT'
|
||||||
ext.supportVersion = '25.3.1'
|
ext.supportVersion = '25.3.1'
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
@ -29,7 +29,6 @@ import ch.dissem.apps.abit.drawer.ProfileImageListener
|
|||||||
import ch.dissem.apps.abit.drawer.ProfileSelectionListener
|
import ch.dissem.apps.abit.drawer.ProfileSelectionListener
|
||||||
import ch.dissem.apps.abit.listener.ListSelectionListener
|
import ch.dissem.apps.abit.listener.ListSelectionListener
|
||||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository.Companion.LABEL_ARCHIVE
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository.Companion.LABEL_ARCHIVE
|
||||||
import ch.dissem.apps.abit.service.BitmessageService.Companion.isRunning
|
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.synchronization.SyncAdapter
|
import ch.dissem.apps.abit.synchronization.SyncAdapter
|
||||||
import ch.dissem.apps.abit.util.Labels
|
import ch.dissem.apps.abit.util.Labels
|
||||||
@ -99,8 +98,8 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
private lateinit var drawer: Drawer
|
private lateinit var drawer: Drawer
|
||||||
private lateinit var nodeSwitch: SwitchDrawerItem
|
private lateinit var nodeSwitch: SwitchDrawerItem
|
||||||
|
|
||||||
val floatingActionButton: FabSpeedDial
|
val floatingActionButton: FabSpeedDial?
|
||||||
get() = fab
|
get() = findViewById(R.id.fab) as FabSpeedDial?
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -241,7 +240,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
.withIdentifier(ID_NODE_SWITCH)
|
.withIdentifier(ID_NODE_SWITCH)
|
||||||
.withName(R.string.full_node)
|
.withName(R.string.full_node)
|
||||||
.withIcon(CommunityMaterial.Icon.cmd_cloud_outline)
|
.withIcon(CommunityMaterial.Icon.cmd_cloud_outline)
|
||||||
.withChecked(isRunning)
|
.withChecked(Preferences.isFullNodeActive(this))
|
||||||
.withOnCheckedChangeListener { _, _, isChecked ->
|
.withOnCheckedChangeListener { _, _, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
NetworkUtils.enableNode(this@MainActivity)
|
NetworkUtils.enableNode(this@MainActivity)
|
||||||
@ -359,11 +358,12 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
selectedLabel = savedInstanceState.getSerializable("selectedLabel") as Label
|
selectedLabel = savedInstanceState.getSerializable("selectedLabel") as Label?
|
||||||
|
|
||||||
val selectedItem = drawer.getDrawerItem(selectedLabel)
|
selectedLabel?.let { selectedLabel ->
|
||||||
if (selectedItem != null) {
|
drawer.getDrawerItem(selectedLabel)?.let { selectedItem ->
|
||||||
drawer.setSelection(selectedItem)
|
drawer.setSelection(selectedItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.onRestoreInstanceState(savedInstanceState)
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
}
|
}
|
||||||
@ -373,7 +373,6 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
if (Preferences.isFullNodeActive(this) && Preferences.isConnectionAllowed(this@MainActivity)) {
|
if (Preferences.isFullNodeActive(this) && Preferences.isConnectionAllowed(this@MainActivity)) {
|
||||||
NetworkUtils.enableNode(this, false)
|
NetworkUtils.enableNode(this, false)
|
||||||
}
|
}
|
||||||
updateNodeSwitch()
|
|
||||||
Singleton.getMessageListener(this).resetNotification()
|
Singleton.getMessageListener(this).resetNotification()
|
||||||
active = true
|
active = true
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
@ -111,14 +111,7 @@ class MessageDetailFragment : Fragment() {
|
|||||||
text.linksClickable = true
|
text.linksClickable = true
|
||||||
text.setTextIsSelectable(true)
|
text.setTextIsSelectable(true)
|
||||||
|
|
||||||
var removed = false
|
val removed = item.labels.removeAll { it.type==Label.Type.UNREAD }
|
||||||
val labels = item.labels.iterator()
|
|
||||||
while (labels.hasNext()) {
|
|
||||||
if (labels.next().type == Label.Type.UNREAD) {
|
|
||||||
labels.remove()
|
|
||||||
removed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val messageRepo = Singleton.getMessageRepository(context)
|
val messageRepo = Singleton.getMessageRepository(context)
|
||||||
if (removed) {
|
if (removed) {
|
||||||
if (activity is MainActivity) {
|
if (activity is MainActivity) {
|
||||||
@ -167,29 +160,29 @@ class MessageDetailFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
R.id.delete -> {
|
R.id.delete -> {
|
||||||
if (isInTrash(item)) {
|
if (isInTrash(item)) {
|
||||||
|
Singleton.labeler.delete(item)
|
||||||
messageRepo.remove(item)
|
messageRepo.remove(item)
|
||||||
} else {
|
} else {
|
||||||
item.labels.clear()
|
Singleton.labeler.delete(item)
|
||||||
item.addLabels(messageRepo.getLabels(Label.Type.TRASH))
|
|
||||||
messageRepo.save(item)
|
messageRepo.save(item)
|
||||||
}
|
}
|
||||||
|
(activity as? MainActivity)?.updateUnread()
|
||||||
activity.onBackPressed()
|
activity.onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.mark_unread -> {
|
R.id.mark_unread -> {
|
||||||
item.addLabels(messageRepo.getLabels(Label.Type.UNREAD))
|
Singleton.labeler.markAsUnread(item)
|
||||||
messageRepo.save(item)
|
messageRepo.save(item)
|
||||||
if (activity is MainActivity) {
|
(activity as? MainActivity)?.updateUnread()
|
||||||
(activity as MainActivity).updateUnread()
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.archive -> {
|
R.id.archive -> {
|
||||||
if (item.isUnread() && activity is MainActivity) {
|
if (item.isUnread() && activity is MainActivity) {
|
||||||
(activity as MainActivity).updateUnread()
|
(activity as MainActivity).updateUnread()
|
||||||
}
|
}
|
||||||
item.labels.clear()
|
Singleton.labeler.archive(item)
|
||||||
messageRepo.save(item)
|
messageRepo.save(item)
|
||||||
|
(activity as? MainActivity)?.updateUnread()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package ch.dissem.apps.abit
|
package ch.dissem.apps.abit
|
||||||
|
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import android.support.v7.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.support.v7.widget.RecyclerView.OnScrollListener
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_BROADCAST
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_BROADCAST
|
||||||
@ -41,9 +43,12 @@ import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
|||||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
||||||
import kotlinx.android.synthetic.main.fragment_message_list.*
|
import kotlinx.android.synthetic.main.fragment_message_list.*
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.doAsync
|
||||||
|
import org.jetbrains.anko.support.v4.onUiThread
|
||||||
import org.jetbrains.anko.uiThread
|
import org.jetbrains.anko.uiThread
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
private const val PAGE_SIZE = 15
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list fragment representing a list of Messages. This fragment
|
* A list fragment representing a list of Messages. This fragment
|
||||||
* also supports tablet devices by allowing list items to be given an
|
* also supports tablet devices by allowing list items to be given an
|
||||||
@ -56,12 +61,32 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class MessageListFragment : Fragment(), ListHolder<Label> {
|
class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||||
|
|
||||||
private var layoutManager: RecyclerView.LayoutManager? = null
|
private var isLoading = false
|
||||||
|
private var isLastPage = false
|
||||||
|
|
||||||
|
private var layoutManager: LinearLayoutManager? = null
|
||||||
private var swipeableMessageAdapter: SwipeableMessageAdapter? = null
|
private var swipeableMessageAdapter: SwipeableMessageAdapter? = null
|
||||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||||
|
|
||||||
|
private val recyclerViewOnScrollListener = object : OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
|
||||||
|
layoutManager?.let { layoutManager ->
|
||||||
|
val visibleItemCount = layoutManager.childCount
|
||||||
|
val totalItemCount = layoutManager.itemCount
|
||||||
|
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||||
|
|
||||||
|
if (!isLoading && !isLastPage) {
|
||||||
|
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - 5
|
||||||
|
&& firstVisibleItemPosition >= 0) {
|
||||||
|
loadMoreItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override var currentLabel: Label? = null
|
override var currentLabel: Label? = null
|
||||||
|
|
||||||
private var emptyTrashMenuItem: MenuItem? = null
|
private var emptyTrashMenuItem: MenuItem? = null
|
||||||
@ -70,6 +95,20 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
|
|
||||||
private val backStack = Stack<Label>()
|
private val backStack = Stack<Label>()
|
||||||
|
|
||||||
|
fun loadMoreItems() {
|
||||||
|
isLoading = true
|
||||||
|
swipeableMessageAdapter?.let { messageAdapter ->
|
||||||
|
doAsync {
|
||||||
|
val messages = messageRepo.findMessages(currentLabel, messageAdapter.itemCount, PAGE_SIZE)
|
||||||
|
onUiThread {
|
||||||
|
messageAdapter.addAll(messages)
|
||||||
|
isLoading = false
|
||||||
|
isLastPage = messages.size < PAGE_SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@ -82,10 +121,8 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
initFab(activity)
|
initFab(activity)
|
||||||
messageRepo = Singleton.getMessageRepository(activity)
|
messageRepo = Singleton.getMessageRepository(activity)
|
||||||
|
|
||||||
if (backStack.isEmpty()) {
|
if (backStack.isEmpty() && currentLabel == null) {
|
||||||
doUpdateList(activity.selectedLabel)
|
doUpdateList(activity.selectedLabel)
|
||||||
} else {
|
|
||||||
doUpdateList(backStack.peek())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,13 +156,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doAsync {
|
loadMoreItems()
|
||||||
messageRepo.findMessageIds(label)
|
|
||||||
.map { messageRepo.getMessage(it) }
|
|
||||||
.forEach { message ->
|
|
||||||
uiThread { swipeableMessageAdapter?.add(message) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
@ -153,17 +184,18 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
adapter.eventListener = object : SwipeableMessageAdapter.EventListener {
|
adapter.eventListener = object : SwipeableMessageAdapter.EventListener {
|
||||||
override fun onItemDeleted(item: Plaintext) {
|
override fun onItemDeleted(item: Plaintext) {
|
||||||
if (MessageDetailFragment.isInTrash(item)) {
|
if (MessageDetailFragment.isInTrash(item)) {
|
||||||
|
Singleton.labeler.delete(item)
|
||||||
messageRepo.remove(item)
|
messageRepo.remove(item)
|
||||||
} else {
|
} else {
|
||||||
item.labels.clear()
|
Singleton.labeler.delete(item)
|
||||||
item.addLabels(messageRepo.getLabels(Label.Type.TRASH))
|
|
||||||
messageRepo.save(item)
|
messageRepo.save(item)
|
||||||
}
|
}
|
||||||
|
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemArchived(item: Plaintext) {
|
override fun onItemArchived(item: Plaintext) {
|
||||||
item.labels.clear()
|
Singleton.labeler.archive(item)
|
||||||
messageRepo.save(item)
|
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemViewClicked(v: View?) {
|
override fun onItemViewClicked(v: View?) {
|
||||||
@ -189,6 +221,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
recycler_view.layoutManager = layoutManager
|
recycler_view.layoutManager = layoutManager
|
||||||
recycler_view.adapter = wrappedAdapter // requires *wrapped* swipeableMessageAdapter
|
recycler_view.adapter = wrappedAdapter // requires *wrapped* swipeableMessageAdapter
|
||||||
recycler_view.itemAnimator = animator
|
recycler_view.itemAnimator = animator
|
||||||
|
recycler_view.addOnScrollListener(recyclerViewOnScrollListener)
|
||||||
|
|
||||||
recycler_view.addItemDecoration(SimpleListDividerDecorator(
|
recycler_view.addItemDecoration(SimpleListDividerDecorator(
|
||||||
ContextCompat.getDrawable(context, R.drawable.list_divider_h), true))
|
ContextCompat.getDrawable(context, R.drawable.list_divider_h), true))
|
||||||
@ -204,6 +237,33 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
|||||||
recyclerViewTouchActionGuardManager = touchActionGuardManager
|
recyclerViewTouchActionGuardManager = touchActionGuardManager
|
||||||
recyclerViewSwipeManager = swipeManager
|
recyclerViewSwipeManager = swipeManager
|
||||||
this.swipeableMessageAdapter = adapter
|
this.swipeableMessageAdapter = adapter
|
||||||
|
|
||||||
|
Singleton.labeler.listener = { message, added, removed ->
|
||||||
|
when {
|
||||||
|
currentLabel?.type == Label.Type.TRASH && added.all { it.type == Label.Type.TRASH } && removed.any { it.type == Label.Type.TRASH } -> {
|
||||||
|
// work-around for messages that are deleted from trash
|
||||||
|
swipeableMessageAdapter?.remove(message)
|
||||||
|
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||||
|
}
|
||||||
|
currentLabel?.type == Label.Type.UNREAD && added.all { it.type == Label.Type.TRASH } -> {
|
||||||
|
// work-around for messages that are deleted from unread, which already have the unread label removed
|
||||||
|
swipeableMessageAdapter?.remove(message)
|
||||||
|
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||||
|
}
|
||||||
|
added.contains(currentLabel) -> {
|
||||||
|
// in most cases, top should be the correct position, but time will show if
|
||||||
|
// the message should be properly sorted in
|
||||||
|
swipeableMessageAdapter?.addFirst(message)
|
||||||
|
}
|
||||||
|
removed.contains(currentLabel) -> {
|
||||||
|
swipeableMessageAdapter?.remove(message)
|
||||||
|
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||||
|
}
|
||||||
|
removed.any { it.type == Label.Type.UNREAD } || added.any { it.type == Label.Type.UNREAD } -> {
|
||||||
|
swipeableMessageAdapter?.update(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initFab(context: MainActivity) {
|
private fun initFab(context: MainActivity) {
|
||||||
|
@ -96,6 +96,35 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie
|
|||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addFirst(item: Plaintext) {
|
||||||
|
val index = data.size
|
||||||
|
data.addFirst(item)
|
||||||
|
notifyItemInserted(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAll(items: Collection<Plaintext>) {
|
||||||
|
val index = data.size
|
||||||
|
data.addAll(items)
|
||||||
|
notifyItemRangeInserted(index, items.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(item: Plaintext) {
|
||||||
|
val index = data.indexOf(item)
|
||||||
|
data.removeIf { it.id == item.id }
|
||||||
|
notifyItemRemoved(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(item: Plaintext) {
|
||||||
|
data.replaceAll {
|
||||||
|
if (it.id == item.id) {
|
||||||
|
item
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyItemChanged(data.indexOf(item))
|
||||||
|
}
|
||||||
|
|
||||||
fun clear(newLabel: Label?) {
|
fun clear(newLabel: Label?) {
|
||||||
label = newLabel
|
label = newLabel
|
||||||
data.clear()
|
data.clear()
|
||||||
|
@ -17,16 +17,12 @@
|
|||||||
package ch.dissem.apps.abit.listener
|
package ch.dissem.apps.abit.listener
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
||||||
import java.util.Deque
|
|
||||||
import java.util.LinkedList
|
|
||||||
import java.util.concurrent.ExecutorService
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
import ch.dissem.apps.abit.MainActivity
|
import ch.dissem.apps.abit.MainActivity
|
||||||
import ch.dissem.apps.abit.notification.NewMessageNotification
|
import ch.dissem.apps.abit.notification.NewMessageNotification
|
||||||
import ch.dissem.bitmessage.BitmessageContext
|
import ch.dissem.bitmessage.BitmessageContext
|
||||||
import ch.dissem.bitmessage.entity.Plaintext
|
import ch.dissem.bitmessage.entity.Plaintext
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for decrypted Bitmessage messages. Does show a notification.
|
* Listens for decrypted Bitmessage messages. Does show a notification.
|
||||||
|
@ -19,42 +19,18 @@ package ch.dissem.apps.abit.listener
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.NetworkInfo
|
|
||||||
import ch.dissem.apps.abit.service.BitmessageService
|
import ch.dissem.apps.abit.service.BitmessageService
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
import ch.dissem.apps.abit.service.Singleton
|
||||||
import ch.dissem.apps.abit.util.Preferences
|
import ch.dissem.apps.abit.util.Preferences
|
||||||
|
import org.jetbrains.anko.connectivityManager
|
||||||
|
|
||||||
class WifiReceiver : BroadcastReceiver() {
|
class WifiReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(ctx: Context, intent: Intent) {
|
override fun onReceive(ctx: Context, intent: Intent) {
|
||||||
if ("android.net.conn.CONNECTIVITY_CHANGE" == intent.action) {
|
if ("android.net.conn.CONNECTIVITY_CHANGE" == intent.action) {
|
||||||
val bmc = Singleton.getBitmessageContext(ctx)
|
val bmc = Singleton.getBitmessageContext(ctx)
|
||||||
if (Preferences.isWifiOnly(ctx) && isConnectedToMeteredNetwork(ctx) && bmc.isRunning()) {
|
if (Preferences.isFullNodeActive(ctx) && !bmc.isRunning() && !(Preferences.isWifiOnly(ctx) && ctx.connectivityManager.isActiveNetworkMetered)) {
|
||||||
bmc.shutdown()
|
|
||||||
}
|
|
||||||
if (Preferences.isFullNodeActive(ctx) && !bmc.isRunning() && !(Preferences.isWifiOnly(ctx) && isConnectedToMeteredNetwork(ctx))) {
|
|
||||||
ctx.startService(Intent(ctx, BitmessageService::class.java))
|
ctx.startService(Intent(ctx, BitmessageService::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun isConnectedToMeteredNetwork(ctx: Context): Boolean {
|
|
||||||
val netInfo = getNetworkInfo(ctx)
|
|
||||||
if (netInfo == null || !netInfo.isConnectedOrConnecting) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
when (netInfo.type) {
|
|
||||||
ConnectivityManager.TYPE_ETHERNET, ConnectivityManager.TYPE_WIFI -> return false
|
|
||||||
else -> return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNetworkInfo(ctx: Context): NetworkInfo? {
|
|
||||||
val conMan = ctx.getSystemService(Context
|
|
||||||
.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
return conMan.activeNetworkInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,10 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class AndroidMessageRepository(private val sql: SqlHelper, private val context: Context) : AbstractMessageRepository() {
|
class AndroidMessageRepository(private val sql: SqlHelper, private val context: Context) : AbstractMessageRepository() {
|
||||||
|
|
||||||
override fun findMessages(label: Label?) = if (label === LABEL_ARCHIVE) {
|
override fun findMessages(label: Label?, offset: Int, limit: Int) = if (label === LABEL_ARCHIVE) {
|
||||||
super.findMessages(null as Label?)
|
super.findMessages(null as Label?, offset, limit)
|
||||||
} else {
|
} else {
|
||||||
super.findMessages(label)
|
super.findMessages(label, offset, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findMessageIds(label: Label) = if (label === LABEL_ARCHIVE) {
|
fun findMessageIds(label: Label) = if (label === LABEL_ARCHIVE) {
|
||||||
@ -224,7 +224,7 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun find(where: String): List<Plaintext> {
|
override fun find(where: String, offset: Int, limit: Int): List<Plaintext> {
|
||||||
val result = LinkedList<Plaintext>()
|
val result = LinkedList<Plaintext>()
|
||||||
|
|
||||||
// Define a projection that specifies which columns from the database
|
// Define a projection that specifies which columns from the database
|
||||||
@ -235,7 +235,8 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
|
|||||||
db.query(
|
db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
where, null, null, null,
|
where, null, null, null,
|
||||||
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC"
|
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC",
|
||||||
|
if (limit == 0) null else "$offset, $limit"
|
||||||
).use { c ->
|
).use { c ->
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
val iv = c.getBlob(c.getColumnIndex(COLUMN_IV))
|
val iv = c.getBlob(c.getColumnIndex(COLUMN_IV))
|
||||||
|
@ -17,12 +17,17 @@
|
|||||||
package ch.dissem.apps.abit.service
|
package ch.dissem.apps.abit.service
|
||||||
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.ConnectivityManager
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import ch.dissem.apps.abit.notification.NetworkNotification
|
import ch.dissem.apps.abit.notification.NetworkNotification
|
||||||
import ch.dissem.apps.abit.notification.NetworkNotification.Companion.NETWORK_NOTIFICATION_ID
|
import ch.dissem.apps.abit.notification.NetworkNotification.Companion.NETWORK_NOTIFICATION_ID
|
||||||
import ch.dissem.bitmessage.BitmessageContext
|
import ch.dissem.bitmessage.BitmessageContext
|
||||||
import ch.dissem.bitmessage.utils.Property
|
import ch.dissem.bitmessage.utils.Property
|
||||||
|
import org.jetbrains.anko.connectivityManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a Service that returns an IBinder for the
|
* Define a Service that returns an IBinder for the
|
||||||
@ -34,6 +39,14 @@ class BitmessageService : Service() {
|
|||||||
private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) }
|
private val bmc: BitmessageContext by lazy { Singleton.getBitmessageContext(this) }
|
||||||
private lateinit var notification: NetworkNotification
|
private lateinit var notification: NetworkNotification
|
||||||
|
|
||||||
|
private val connectivityReceiver: BroadcastReceiver = object: BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
if (bmc.isRunning() && connectivityManager.isActiveNetworkMetered){
|
||||||
|
bmc.shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val cleanupHandler = Handler()
|
private val cleanupHandler = Handler()
|
||||||
private val cleanupTask: Runnable = object : Runnable {
|
private val cleanupTask: Runnable = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
@ -45,6 +58,7 @@ class BitmessageService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
registerReceiver(connectivityReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
|
||||||
notification = NetworkNotification(this)
|
notification = NetworkNotification(this)
|
||||||
running = false
|
running = false
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import ch.dissem.bitmessage.BitmessageContext
|
|||||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey
|
import ch.dissem.bitmessage.entity.payload.Pubkey
|
||||||
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler
|
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler
|
||||||
|
import ch.dissem.bitmessage.ports.DefaultLabeler
|
||||||
import ch.dissem.bitmessage.utils.ConversationService
|
import ch.dissem.bitmessage.utils.ConversationService
|
||||||
import ch.dissem.bitmessage.utils.TTL
|
import ch.dissem.bitmessage.utils.TTL
|
||||||
import ch.dissem.bitmessage.utils.UnixTime.DAY
|
import ch.dissem.bitmessage.utils.UnixTime.DAY
|
||||||
@ -40,6 +41,7 @@ import org.jetbrains.anko.uiThread
|
|||||||
* Provides singleton objects across the application.
|
* Provides singleton objects across the application.
|
||||||
*/
|
*/
|
||||||
object Singleton {
|
object Singleton {
|
||||||
|
val labeler = DefaultLabeler()
|
||||||
var bitmessageContext: BitmessageContext? = null
|
var bitmessageContext: BitmessageContext? = null
|
||||||
private set
|
private set
|
||||||
private var conversationService: ConversationService? = null
|
private var conversationService: ConversationService? = null
|
||||||
@ -69,6 +71,7 @@ object Singleton {
|
|||||||
.powRepo(powRepo)
|
.powRepo(powRepo)
|
||||||
.networkHandler(NioNetworkHandler())
|
.networkHandler(NioNetworkHandler())
|
||||||
.listener(getMessageListener(ctx))
|
.listener(getMessageListener(ctx))
|
||||||
|
.labeler(labeler)
|
||||||
.doNotSendPubkeyOnIdentityCreation()
|
.doNotSendPubkeyOnIdentityCreation()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ class StartupNodeOnWifiService : JobService() {
|
|||||||
|
|
||||||
override fun onStopJob(params: JobParameters?): Boolean {
|
override fun onStopJob(params: JobParameters?): Boolean {
|
||||||
if (Preferences.isWifiOnly(this)) {
|
if (Preferences.isWifiOnly(this)) {
|
||||||
Singleton.getBitmessageContext(this).shutdown()
|
// Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes
|
||||||
|
// depending on Android version.
|
||||||
return Preferences.isFullNodeActive(this)
|
return Preferences.isFullNodeActive(this)
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -11,7 +11,7 @@ import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
|||||||
*/
|
*/
|
||||||
object FabUtils {
|
object FabUtils {
|
||||||
fun initFab(activity: MainActivity, @DrawableRes drawableRes: Int, menu: FabSpeedDialMenu): FabSpeedDial {
|
fun initFab(activity: MainActivity, @DrawableRes drawableRes: Int, menu: FabSpeedDialMenu): FabSpeedDial {
|
||||||
val fab = activity.floatingActionButton
|
val fab = activity.floatingActionButton ?: throw IllegalStateException("Fab must not be null")
|
||||||
fab.removeAllOnMenuItemClickListeners()
|
fab.removeAllOnMenuItemClickListeners()
|
||||||
fab.show()
|
fab.show()
|
||||||
fab.closeMenu()
|
fab.closeMenu()
|
||||||
|
@ -26,6 +26,8 @@ import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
|
|||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE
|
||||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
|
import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY
|
||||||
|
import org.jetbrains.anko.connectivityManager
|
||||||
|
import org.jetbrains.anko.networkStatsManager
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -88,7 +90,7 @@ object Preferences {
|
|||||||
return preferences.getString(name, null)
|
return preferences.getString(name, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isConnectionAllowed(ctx: Context) = !isWifiOnly(ctx) || !WifiReceiver.isConnectedToMeteredNetwork(ctx)
|
fun isConnectionAllowed(ctx: Context) = !isWifiOnly(ctx) || ctx.connectivityManager.isActiveNetworkMetered
|
||||||
|
|
||||||
fun isWifiOnly(ctx: Context): Boolean {
|
fun isWifiOnly(ctx: Context): Boolean {
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingBottom="18dp"
|
android:paddingBottom="18dp"
|
||||||
@ -71,7 +70,7 @@
|
|||||||
android:text="@string/ok"
|
android:text="@string/ok"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
android:layout_alignRight="@+id/radioGroup"
|
android:layout_alignEnd="@+id/radioGroup"
|
||||||
android:layout_below="@+id/radioGroup"/>
|
android:layout_below="@+id/radioGroup"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -81,7 +80,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/cancel"
|
android:text="@string/cancel"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
android:layout_toLeftOf="@+id/ok"
|
android:layout_toStartOf="@+id/ok"
|
||||||
android:layout_below="@+id/radioGroup"/>
|
android:layout_below="@+id/radioGroup"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:text="CheckBox"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
tools:text="Name"/>
|
tools:text="Name"/>
|
||||||
|
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
android:id="@+id/select_encoding"
|
android:id="@+id/select_encoding"
|
||||||
app:showAsAction="never"
|
app:showAsAction="never"
|
||||||
android:icon="@drawable/ic_action_encoding"
|
android:icon="@drawable/ic_action_encoding"
|
||||||
android:title="Select encoding"
|
android:title="@string/select_encoding"
|
||||||
/>
|
/>
|
||||||
</menu>
|
</menu>
|
||||||
|
@ -124,4 +124,7 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
|
|||||||
<string name="request_acknowledgements">Bestätigungen verlangen</string>
|
<string name="request_acknowledgements">Bestätigungen verlangen</string>
|
||||||
<string name="request_acknowledgements_summary">Bestätigungen stellen sicher dass eine Nachricht empfangen wurde, benötigen aber zusätzliche Zeit zum versenden</string>
|
<string name="request_acknowledgements_summary">Bestätigungen stellen sicher dass eine Nachricht empfangen wurde, benötigen aber zusätzliche Zeit zum versenden</string>
|
||||||
<string name="got_it">Alles klar</string>
|
<string name="got_it">Alles klar</string>
|
||||||
|
<string name="import_data">Import</string>
|
||||||
|
<string name="import_data_summary">Nachrichten und Kontakte importieren (aber keine Identitäten)</string>
|
||||||
|
<string name="select_encoding">Kodierung auswählen</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -125,4 +125,5 @@ As an alternative you could configure a trusted node in the settings, but as of
|
|||||||
<string name="request_acknowledgements">Request acknowledgements</string>
|
<string name="request_acknowledgements">Request acknowledgements</string>
|
||||||
<string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string>
|
<string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string>
|
||||||
<string name="got_it">Got it</string>
|
<string name="got_it">Got it</string>
|
||||||
|
<string name="select_encoding">Select encoding</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -5,7 +5,7 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.1.4-2'
|
ext.kotlin_version = '1.1.4-3'
|
||||||
ext.anko_version = '0.10.1'
|
ext.anko_version = '0.10.1'
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
Loading…
Reference in New Issue
Block a user