Switch to API level 27 and updated libraries
This commit is contained in:
parent
f58a22dadb
commit
072f732924
100
app/build.gradle
100
app/build.gradle
@ -8,18 +8,18 @@ ext {
|
||||
}
|
||||
if (project.hasProperty("project.configs")
|
||||
&& new File(project.property("project.configs") + appName + ".gradle").exists()) {
|
||||
apply from: project.property("project.configs") + appName + ".gradle";
|
||||
apply from: project.property("project.configs") + appName + ".gradle"
|
||||
}
|
||||
|
||||
//noinspection GroovyMissingReturnStatement
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.2"
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "ch.dissem.apps." + appName.toLowerCase()
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 25
|
||||
targetSdkVersion 27
|
||||
versionCode 17
|
||||
versionName "1.0-beta17"
|
||||
multiDexEnabled true
|
||||
@ -28,6 +28,9 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
@ -36,69 +39,74 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ext.jabitVersion = '2.0.4'
|
||||
ext.jabitVersion = 'development-SNAPSHOT'
|
||||
ext.supportVersion = '25.3.1'
|
||||
ext.supportVersion = '27.0.0'
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
compile "org.jetbrains.anko:anko:$anko_version"
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation "org.jetbrains.anko:anko:$anko_version"
|
||||
|
||||
compile "com.android.support:appcompat-v7:$supportVersion"
|
||||
compile "com.android.support:preference-v7:$supportVersion"
|
||||
compile "com.android.support:support-v4:$supportVersion"
|
||||
compile "com.android.support:design:$supportVersion"
|
||||
compile "com.android.support:multidex:1.0.1"
|
||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||
implementation "com.android.support:preference-v7:$supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation "com.android.support:design:$supportVersion"
|
||||
implementation "com.android.support:multidex:1.0.2"
|
||||
|
||||
compile "ch.dissem.jabit:jabit-core:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-exports:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-core:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-exports:$jabitVersion"
|
||||
|
||||
compile 'org.slf4j:slf4j-android:1.7.25'
|
||||
implementation 'org.slf4j:slf4j-android:1.7.25'
|
||||
|
||||
compile 'com.mikepenz:materialize:1.0.1@aar'
|
||||
compile('com.mikepenz:materialdrawer:5.9.0@aar') {
|
||||
implementation 'com.mikepenz:materialize:1.1.0@aar'
|
||||
implementation('com.mikepenz:materialdrawer:5.9.5@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile('com.mikepenz:aboutlibraries:5.9.5@aar') {
|
||||
implementation('com.mikepenz:aboutlibraries:5.9.7@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile "com.mikepenz:iconics-core:2.8.3@aar"
|
||||
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
|
||||
compile 'com.mikepenz:community-material-typeface:1.9.32.1@aar'
|
||||
implementation "com.mikepenz:iconics-core:2.9.3@aar"
|
||||
implementation "com.mikepenz:iconics-views:2.9.3@aar"
|
||||
implementation 'com.mikepenz:google-material-typeface:3.0.1.1.original@aar'
|
||||
implementation 'com.mikepenz:community-material-typeface:1.9.32.2@aar'
|
||||
|
||||
compile 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
|
||||
compile 'com.google.zxing:core:3.3.0'
|
||||
implementation 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
|
||||
implementation 'com.google.zxing:core:3.3.1'
|
||||
|
||||
compile 'com.github.kobakei:MaterialFabSpeedDial:1.1.5'
|
||||
compile 'com.github.amlcurran.showcaseview:library:5.4.3'
|
||||
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.4@aar') {
|
||||
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.1.7'
|
||||
implementation 'com.github.amlcurran.showcaseview:library:5.4.3'
|
||||
// FIXME: switch back to com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.+ a.s.a.p.
|
||||
implementation('com.github.h6ah4i:android-advancedrecyclerview:develop-SNAPSHOT@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile 'com.github.angads25:filepicker:1.1.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.github.angads25:filepicker:1.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:2.8.9'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
testCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testCompile 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'
|
||||
testCompile 'org.robolectric:robolectric:3.4.2'
|
||||
testCompile "org.robolectric:shadows-multidex:3.4-rc2"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:2.11.0'
|
||||
testImplementation 'org.hamcrest:hamcrest-library:1.3'
|
||||
testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testImplementation 'org.robolectric:robolectric:3.5'
|
||||
testImplementation "org.robolectric:shadows-multidex:3.5"
|
||||
|
||||
androidTestImplementation "com.android.support:multidex:1.0.2"
|
||||
}
|
||||
|
||||
idea.module {
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> {
|
||||
private var activatedPosition = ListView.INVALID_POSITION
|
||||
private var activateOnItemClick: Boolean = false
|
||||
|
||||
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Restore the previously serialized activated item position.
|
||||
@ -92,9 +92,9 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
if (outState != null && activatedPosition != ListView.INVALID_POSITION) {
|
||||
if (activatedPosition != ListView.INVALID_POSITION) {
|
||||
// Serialize and persist the activated item position.
|
||||
outState.putInt(STATE_ACTIVATED_POSITION, activatedPosition)
|
||||
}
|
||||
@ -136,9 +136,7 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> {
|
||||
* nothing. Used only when this fragment is not attached to an activity.
|
||||
*/
|
||||
internal object DummyCallback : ListSelectionListener<Any> {
|
||||
override fun onItemSelected(item: Any) {
|
||||
// NO OP
|
||||
}
|
||||
override fun onItemSelected(item: Any) = Unit // NO OP
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -49,16 +49,18 @@ class AddressDetailFragment : Fragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
arguments?.let { arguments ->
|
||||
if (arguments.containsKey(ARG_ITEM)) {
|
||||
item = arguments.getSerializable(ARG_ITEM) as BitmessageAddress
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.address, menu)
|
||||
|
||||
val ctx = activity
|
||||
val ctx = activity!!
|
||||
Drawables.addIcon(ctx, menu, R.id.write_message, GoogleMaterial.Icon.gmd_mail)
|
||||
Drawables.addIcon(ctx, menu, R.id.share, GoogleMaterial.Icon.gmd_share)
|
||||
Drawables.addIcon(ctx, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete)
|
||||
@ -69,7 +71,7 @@ class AddressDetailFragment : Fragment() {
|
||||
|
||||
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
|
||||
val item = item ?: return false
|
||||
val ctx = activity
|
||||
val ctx = activity ?: return false
|
||||
when (menuItem.itemId) {
|
||||
R.id.write_message -> {
|
||||
val identity = Singleton.getIdentity(ctx)
|
||||
@ -138,24 +140,21 @@ class AddressDetailFragment : Fragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// Show the dummy content as text in a TextView.
|
||||
item?.let { item ->
|
||||
val activity = activity
|
||||
activity?.let { activity ->
|
||||
when {
|
||||
item.isChan -> activity.setTitle(R.string.title_chan_detail)
|
||||
item.privateKey != null -> activity.setTitle(R.string.title_identity_detail)
|
||||
item.isSubscribed -> activity.setTitle(R.string.title_subscription_detail)
|
||||
else -> activity.setTitle(R.string.title_contact_detail)
|
||||
}
|
||||
}
|
||||
|
||||
avatar.setImageDrawable(Identicon(item))
|
||||
name.setText(item.toString())
|
||||
name.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// Nothing to do
|
||||
}
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit // Nothing to do
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
// Nothing to do
|
||||
}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) = Unit // Nothing to do
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
item.alias = s.toString()
|
||||
@ -185,7 +184,7 @@ class AddressDetailFragment : Fragment() {
|
||||
|
||||
override fun onPause() {
|
||||
item?.let { item ->
|
||||
Singleton.getAddressRepository(context).save(item)
|
||||
Singleton.getAddressRepository(context!!).save(item)
|
||||
if (item.privateKey != null) {
|
||||
MainActivity.getInstance()?.updateIdentityEntry(item)
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
||||
val view = inflater.inflate(R.layout.subscription_row, parent, false)
|
||||
v = ViewHolder(
|
||||
ctx = context,
|
||||
avatar = view.findViewById(R.id.avatar) as ImageView,
|
||||
name = view.findViewById(R.id.name) as TextView,
|
||||
streamNumber = view.findViewById(R.id.stream_number) as TextView,
|
||||
avatar = view.findViewById(R.id.avatar),
|
||||
name = view.findViewById(R.id.name),
|
||||
streamNumber = view.findViewById(R.id.stream_number),
|
||||
subscribed = view.findViewById(R.id.subscribed)
|
||||
)
|
||||
view.tag = v
|
||||
@ -89,6 +89,7 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
||||
|
||||
fun updateList() {
|
||||
adapter.clear()
|
||||
context?.let { context ->
|
||||
val addressRepo = Singleton.getAddressRepository(context)
|
||||
doAsync {
|
||||
addressRepo.getContactIds()
|
||||
@ -97,6 +98,7 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFab(activity: MainActivity) {
|
||||
activity.updateTitle(getString(R.string.contacts_and_subscriptions))
|
||||
@ -131,9 +133,7 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateList(label: Void) {
|
||||
updateList()
|
||||
}
|
||||
override fun updateList(label: Void) = updateList()
|
||||
|
||||
private data class ViewHolder(
|
||||
val ctx: Context,
|
||||
|
@ -61,18 +61,19 @@ class ComposeMessageActivity : AppCompatActivity() {
|
||||
const val EXTRA_ENCODING = "ch.dissem.abit.Message.ENCODING"
|
||||
const val EXTRA_PARENT = "ch.dissem.abit.Message.PARENT"
|
||||
|
||||
fun launchReplyTo(fragment: Fragment, item: Plaintext) {
|
||||
fragment.startActivity(getReplyIntent(fragment.activity, item))
|
||||
}
|
||||
fun launchReplyTo(fragment: Fragment, item: Plaintext) =
|
||||
fragment.startActivity(getReplyIntent(
|
||||
ctx = fragment.activity ?: throw IllegalStateException("Fragment not attached to an activity"),
|
||||
item = item
|
||||
))
|
||||
|
||||
fun launchReplyTo(activity: Activity, item: Plaintext) {
|
||||
fun launchReplyTo(activity: Activity, item: Plaintext) =
|
||||
activity.startActivity(getReplyIntent(activity, item))
|
||||
}
|
||||
|
||||
private fun getReplyIntent(ctx: Context, item: Plaintext): Intent {
|
||||
val replyIntent = Intent(ctx, ComposeMessageActivity::class.java)
|
||||
val receivingIdentity = item.to
|
||||
if (receivingIdentity?.isChan ?: false) {
|
||||
if (receivingIdentity?.isChan == true) {
|
||||
// reply to chan, not to the sender of the message
|
||||
replyIntent.putExtra(EXTRA_RECIPIENT, receivingIdentity)
|
||||
// I hate when people send as chan, so it won't be the default behaviour.
|
||||
|
@ -59,7 +59,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
arguments?.let { arguments ->
|
||||
var id = arguments.getSerializable(EXTRA_IDENTITY) as? BitmessageAddress
|
||||
if (context != null && (id == null || id.privateKey == null)) {
|
||||
id = Singleton.getIdentity(context)
|
||||
id = Singleton.getIdentity(context!!)
|
||||
}
|
||||
if (id?.privateKey != null) {
|
||||
identity = id
|
||||
@ -97,7 +97,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
if (broadcast) {
|
||||
recipient_input.visibility = View.GONE
|
||||
} else {
|
||||
val adapter = ContactAdapter(context)
|
||||
val adapter = ContactAdapter(context!!)
|
||||
recipient_input.setAdapter(adapter)
|
||||
recipient_input.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) }
|
||||
recipient_input.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
@ -105,9 +105,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
recipient = adapter.getItem(position)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// leave current selection
|
||||
}
|
||||
override fun onNothingSelected(parent: AdapterView<*>) = Unit // leave current selection
|
||||
}
|
||||
recipient?.let { recipient_input.setText(it.toString()) }
|
||||
}
|
||||
@ -148,17 +146,16 @@ class ComposeMessageFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == 0 && data != null && resultCode == RESULT_OK) {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = if (requestCode == 0 && data != null && resultCode == RESULT_OK) {
|
||||
encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun send() {
|
||||
val builder: Plaintext.Builder
|
||||
val bmc = Singleton.getBitmessageContext(context)
|
||||
val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity")
|
||||
val bmc = Singleton.getBitmessageContext(ctx)
|
||||
if (broadcast) {
|
||||
builder = Plaintext.Builder(BROADCAST).from(identity)
|
||||
} else {
|
||||
@ -167,7 +164,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
try {
|
||||
recipient = BitmessageAddress(inputString)
|
||||
} catch (e: Exception) {
|
||||
val contacts = Singleton.getAddressRepository(context).getContacts()
|
||||
val contacts = Singleton.getAddressRepository(ctx).getContacts()
|
||||
for (contact in contacts) {
|
||||
if (inputString.equals(contact.alias, ignoreCase = true)) {
|
||||
recipient = contact
|
||||
@ -186,7 +183,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
.from(identity)
|
||||
.to(recipient)
|
||||
}
|
||||
if (!Preferences.requestAcknowledgements(context)) {
|
||||
if (!Preferences.requestAcknowledgements(ctx)) {
|
||||
builder.preventAck()
|
||||
}
|
||||
when (encoding) {
|
||||
@ -203,8 +200,8 @@ class ComposeMessageFragment : Fragment() {
|
||||
)
|
||||
else -> {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_unsupported_encoding, encoding),
|
||||
ctx,
|
||||
ctx.getString(R.string.error_unsupported_encoding, encoding),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
builder.message(
|
||||
@ -214,7 +211,7 @@ class ComposeMessageFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
bmc.send(builder.build())
|
||||
activity.finish()
|
||||
ctx.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,9 @@ class CreateAddressActivity : AppCompatActivity() {
|
||||
else
|
||||
setContentView(R.layout.activity_create_bitmessage_address)
|
||||
|
||||
val address = findViewById(R.id.address) as TextView
|
||||
val label = findViewById(R.id.label) as EditText
|
||||
val subscribe = findViewById(R.id.subscribe) as Switch
|
||||
val address = findViewById<TextView>(R.id.address)
|
||||
val label = findViewById<EditText>(R.id.label)
|
||||
val subscribe = findViewById<Switch>(R.id.subscribe)
|
||||
|
||||
if (uri != null) {
|
||||
val addressText = getAddress(uri)
|
||||
@ -70,12 +70,12 @@ class CreateAddressActivity : AppCompatActivity() {
|
||||
address.text = addressText
|
||||
}
|
||||
|
||||
val cancel = findViewById(R.id.cancel) as Button
|
||||
val cancel = findViewById<Button>(R.id.cancel)
|
||||
cancel.setOnClickListener {
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
findViewById(R.id.do_import).setOnClickListener { onOK(address, label, subscribe) }
|
||||
findViewById<Button>(R.id.do_import).setOnClickListener { onOK(address, label, subscribe) }
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@ import android.support.v7.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
|
||||
import com.h6ah4i.android.widget.advrecyclerview.decoration.SimpleListDividerDecorator
|
||||
|
||||
@ -38,7 +39,7 @@ class ImportIdentitiesFragment : Fragment() {
|
||||
private lateinit var adapter: AddressSelectorAdapter
|
||||
private lateinit var importer: WifImporter
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle): View =
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||
inflater.inflate(R.layout.fragment_import_select_identities, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -52,14 +53,14 @@ class ImportIdentitiesFragment : Fragment() {
|
||||
val layoutManager = LinearLayoutManager(activity,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false)
|
||||
val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
recyclerView.addItemDecoration(SimpleListDividerDecorator(
|
||||
ContextCompat.getDrawable(activity, R.drawable.list_divider_h), true))
|
||||
|
||||
view.findViewById(R.id.finish).setOnClickListener {
|
||||
view.findViewById<Button>(R.id.finish).setOnClickListener {
|
||||
importer.importAll(adapter.selected)
|
||||
val mainActivity = MainActivity.getInstance()
|
||||
if (mainActivity != null) {
|
||||
|
@ -39,7 +39,7 @@ class InputWifFragment : Fragment() {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle): View =
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||
inflater.inflate(R.layout.fragment_import_input, container, false)
|
||||
|
||||
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||
@ -58,9 +58,8 @@ class InputWifFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) =
|
||||
inflater.inflate(R.menu.import_input_data, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val properties = DialogProperties()
|
||||
|
@ -99,7 +99,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
||||
private lateinit var nodeSwitch: SwitchDrawerItem
|
||||
|
||||
val floatingActionButton: FabSpeedDial?
|
||||
get() = findViewById(R.id.fab) as FabSpeedDial?
|
||||
get() = findViewById(R.id.fab)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -109,7 +109,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
||||
setContentView(R.layout.activity_main)
|
||||
fab.hide()
|
||||
|
||||
val toolbar = findViewById(R.id.toolbar) as Toolbar
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val listFragment = MessageListFragment()
|
||||
@ -118,7 +118,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
||||
.replace(R.id.item_list, listFragment)
|
||||
.commit()
|
||||
|
||||
if (findViewById(R.id.message_detail_container) != null) {
|
||||
if (findViewById<View>(R.id.message_detail_container) != null) {
|
||||
// The detail container view will be present only in the
|
||||
// large-screen layouts (res/values-large and
|
||||
// res/values-sw600dp). If this view is present, then the
|
||||
@ -358,7 +358,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
selectedLabel = savedInstanceState.getSerializable("selectedLabel") as Label?
|
||||
selectedLabel = savedInstanceState.getSerializable("selectedLabel") as? Label
|
||||
|
||||
selectedLabel?.let { selectedLabel ->
|
||||
drawer.getDrawerItem(selectedLabel)?.let { selectedItem ->
|
||||
|
@ -25,32 +25,23 @@ import android.support.v7.widget.GridLayoutManager
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.text.util.Linkify
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.text.util.Linkify.WEB_URLS
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.view.IconicsImageView
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.util.Assets
|
||||
import ch.dissem.apps.abit.util.Drawables
|
||||
import ch.dissem.apps.abit.util.Labels
|
||||
import ch.dissem.bitmessage.entity.Plaintext
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||
|
||||
import android.text.util.Linkify.WEB_URLS
|
||||
import ch.dissem.apps.abit.util.Constants.BITMESSAGE_ADDRESS_PATTERN
|
||||
import ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA
|
||||
import ch.dissem.apps.abit.util.Drawables
|
||||
import ch.dissem.apps.abit.util.Labels
|
||||
import ch.dissem.apps.abit.util.Strings.prepareMessageExtract
|
||||
import ch.dissem.bitmessage.entity.Plaintext
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.view.IconicsImageView
|
||||
import kotlinx.android.synthetic.main.fragment_message_detail.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* A fragment representing a single Message detail screen.
|
||||
@ -68,12 +59,14 @@ class MessageDetailFragment : Fragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
arguments?.let { arguments ->
|
||||
if (arguments.containsKey(ARG_ITEM)) {
|
||||
// Load the dummy content specified by the fragment
|
||||
// arguments. In a real-world scenario, use a Loader
|
||||
// to load content from a content provider.
|
||||
item = arguments.getSerializable(ARG_ITEM) as Plaintext
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
@ -83,6 +76,8 @@ class MessageDetailFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity")
|
||||
|
||||
// Show the dummy content as text in a TextView.
|
||||
item?.let { item ->
|
||||
subject.text = item.subject
|
||||
@ -97,7 +92,7 @@ class MessageDetailFragment : Fragment() {
|
||||
recipient.setText(R.string.broadcast)
|
||||
}
|
||||
}.invoke()
|
||||
val labelAdapter = LabelAdapter(activity, item.labels)
|
||||
val labelAdapter = LabelAdapter(ctx, item.labels)
|
||||
labels.adapter = labelAdapter
|
||||
labels.layoutManager = GridLayoutManager(activity, 2)
|
||||
|
||||
@ -112,11 +107,9 @@ class MessageDetailFragment : Fragment() {
|
||||
text.setTextIsSelectable(true)
|
||||
|
||||
val removed = item.labels.removeAll { it.type == Label.Type.UNREAD }
|
||||
val messageRepo = Singleton.getMessageRepository(context)
|
||||
val messageRepo = Singleton.getMessageRepository(ctx)
|
||||
if (removed) {
|
||||
if (activity is MainActivity) {
|
||||
(activity as MainActivity).updateUnread()
|
||||
}
|
||||
(activity as? MainActivity)?.updateUnread()
|
||||
messageRepo.save(item)
|
||||
}
|
||||
val parents = ArrayList<Plaintext>(item.parents.size)
|
||||
@ -126,32 +119,35 @@ class MessageDetailFragment : Fragment() {
|
||||
parents.add(parent)
|
||||
}
|
||||
}
|
||||
showRelatedMessages(view, R.id.parents, parents)
|
||||
showRelatedMessages(view, R.id.responses, messageRepo.findResponses(item))
|
||||
showRelatedMessages(ctx, view, R.id.parents, parents)
|
||||
showRelatedMessages(ctx, view, R.id.responses, messageRepo.findResponses(item))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRelatedMessages(rootView: View, @IdRes id: Int, messages: List<Plaintext>) {
|
||||
val recyclerView = rootView.findViewById(id) as RecyclerView
|
||||
val adapter = RelatedMessageAdapter(activity, messages)
|
||||
private fun showRelatedMessages(ctx: Context, rootView: View, @IdRes id: Int, messages: List<Plaintext>) {
|
||||
val recyclerView = rootView.findViewById<RecyclerView>(id)
|
||||
val adapter = RelatedMessageAdapter(ctx, messages)
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.layoutManager = LinearLayoutManager(activity)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.message, menu)
|
||||
|
||||
activity?.let { activity ->
|
||||
Drawables.addIcon(activity, menu, R.id.reply, GoogleMaterial.Icon.gmd_reply)
|
||||
Drawables.addIcon(activity, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete)
|
||||
Drawables.addIcon(activity, menu, R.id.mark_unread, GoogleMaterial.Icon
|
||||
.gmd_markunread)
|
||||
Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive)
|
||||
}
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
|
||||
val messageRepo = Singleton.getMessageRepository(context)
|
||||
val messageRepo = Singleton.getMessageRepository(
|
||||
context ?: throw IllegalStateException("No context available")
|
||||
)
|
||||
item?.let { item ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.reply -> {
|
||||
@ -167,7 +163,7 @@ class MessageDetailFragment : Fragment() {
|
||||
messageRepo.save(item)
|
||||
}
|
||||
(activity as? MainActivity)?.updateUnread()
|
||||
activity.onBackPressed()
|
||||
activity?.onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.mark_unread -> {
|
||||
@ -220,10 +216,10 @@ class MessageDetailFragment : Fragment() {
|
||||
override fun getItemCount() = messages.size
|
||||
|
||||
internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
internal val avatar = itemView.findViewById(R.id.avatar) as ImageView
|
||||
internal val status = itemView.findViewById(R.id.status) as ImageView
|
||||
internal val sender = itemView.findViewById(R.id.sender) as TextView
|
||||
internal val extract = itemView.findViewById(R.id.text) as TextView
|
||||
internal val avatar = itemView.findViewById<ImageView>(R.id.avatar)
|
||||
internal val status = itemView.findViewById<ImageView>(R.id.status)
|
||||
internal val sender = itemView.findViewById<TextView>(R.id.sender)
|
||||
internal val extract = itemView.findViewById<TextView>(R.id.text)
|
||||
internal var item: Plaintext? = null
|
||||
|
||||
init {
|
||||
@ -260,16 +256,16 @@ class MessageDetailFragment : Fragment() {
|
||||
// Get the data model based on position
|
||||
val label = labels[position]
|
||||
|
||||
viewHolder.icon.setColor(Labels.getColor(label))
|
||||
viewHolder.icon.setIcon(Labels.getIcon(label))
|
||||
viewHolder.icon.icon?.color(Labels.getColor(label))
|
||||
viewHolder.icon.icon?.icon(Labels.getIcon(label))
|
||||
viewHolder.label.text = Labels.getText(label, ctx)
|
||||
}
|
||||
|
||||
override fun getItemCount() = labels.size
|
||||
|
||||
internal class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
var icon = itemView.findViewById(R.id.icon) as IconicsImageView
|
||||
var label = itemView.findViewById(R.id.label) as TextView
|
||||
var icon = itemView.findViewById<IconicsImageView>(R.id.icon)!!
|
||||
var label = itemView.findViewById<TextView>(R.id.label)!!
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,6 +276,6 @@ class MessageDetailFragment : Fragment() {
|
||||
*/
|
||||
val ARG_ITEM = "item"
|
||||
|
||||
fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } ?: false
|
||||
fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } == true
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,8 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val context = context ?: throw IllegalStateException("No context available")
|
||||
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
// touch guard manager (this class is required to suppress scrolling while swipe-dismiss
|
||||
@ -190,12 +192,10 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||
Singleton.labeler.delete(item)
|
||||
messageRepo.save(item)
|
||||
}
|
||||
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||
}
|
||||
|
||||
override fun onItemArchived(item: Plaintext) {
|
||||
Singleton.labeler.archive(item)
|
||||
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||
}
|
||||
|
||||
override fun onItemViewClicked(v: View?) {
|
||||
@ -239,28 +239,27 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||
this.swipeableMessageAdapter = adapter
|
||||
|
||||
Singleton.labeler.listener = { message, added, removed ->
|
||||
swipeableMessageAdapter?.let { swipeableMessageAdapter ->
|
||||
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)
|
||||
swipeableMessageAdapter.remove(message)
|
||||
}
|
||||
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)
|
||||
swipeableMessageAdapter.remove(message)
|
||||
}
|
||||
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)
|
||||
swipeableMessageAdapter.addFirst(message)
|
||||
}
|
||||
removed.contains(currentLabel) -> {
|
||||
swipeableMessageAdapter?.remove(message)
|
||||
recyclerViewOnScrollListener.onScrolled(null, 0, 0)
|
||||
swipeableMessageAdapter.remove(message)
|
||||
}
|
||||
removed.any { it.type == Label.Type.UNREAD } || added.any { it.type == Label.Type.UNREAD } -> {
|
||||
swipeableMessageAdapter?.update(message)
|
||||
swipeableMessageAdapter.update(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,7 +271,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||
menu.add(R.string.personal_message).setIcon(R.drawable.ic_action_personal)
|
||||
FabUtils.initFab(context, R.drawable.ic_action_compose_message, menu)
|
||||
.addOnMenuItemClickListener { _, _, itemId ->
|
||||
val identity = Singleton.getIdentity(activity)
|
||||
val identity = Singleton.getIdentity(context)
|
||||
if (identity == null) {
|
||||
Toast.makeText(activity, R.string.no_identity_warning,
|
||||
Toast.LENGTH_LONG).show()
|
||||
@ -347,12 +346,10 @@ class MessageListFragment : Fragment(), ListHolder<Label> {
|
||||
this.activateOnItemClick = activateOnItemClick
|
||||
}
|
||||
|
||||
override fun showPreviousList(): Boolean {
|
||||
return if (backStack.isEmpty()) {
|
||||
override fun showPreviousList() = if (backStack.isEmpty()) {
|
||||
false
|
||||
} else {
|
||||
doUpdateList(backStack.pop())
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,26 +59,26 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
addPreferencesFromResource(R.xml.preferences)
|
||||
|
||||
findPreference("about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
(activity as? MainActivity)?.let { activity ->
|
||||
val libsBuilder = LibsBuilder()
|
||||
.withActivityTitle(activity.getString(R.string.about))
|
||||
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.withAboutDescription(getString(R.string.about_app))
|
||||
val activity = activity as MainActivity
|
||||
if (activity.hasDetailPane) {
|
||||
activity.setDetailView(libsBuilder.supportFragment())
|
||||
} else {
|
||||
libsBuilder.start(getActivity())
|
||||
libsBuilder.start(activity)
|
||||
}
|
||||
}
|
||||
return@OnPreferenceClickListener true
|
||||
}
|
||||
val cleanup = findPreference("cleanup")
|
||||
cleanup?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val ctx = activity.applicationContext
|
||||
val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available")
|
||||
cleanup.isEnabled = false
|
||||
Toast.makeText(ctx, R.string.cleanup_notification_start, Toast
|
||||
.LENGTH_SHORT).show()
|
||||
Toast.makeText(ctx, R.string.cleanup_notification_start, Toast.LENGTH_SHORT).show()
|
||||
|
||||
doAsync {
|
||||
val bmc = Singleton.getBitmessageContext(ctx)
|
||||
@ -99,21 +99,23 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
}
|
||||
|
||||
findPreference("export")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val ctx = context ?: throw IllegalStateException("No context available")
|
||||
|
||||
val dialog = indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data)
|
||||
doAsync {
|
||||
val exportDirectory = Preferences.getExportDirectory(context)
|
||||
val exportDirectory = Preferences.getExportDirectory(ctx)
|
||||
exportDirectory.mkdirs()
|
||||
val temp = File(exportDirectory, "export-${UnixTime.now}.zip")
|
||||
ZipOutputStream(FileOutputStream(temp)).use { zip ->
|
||||
zip.putNextEntry(ZipEntry("contacts.json"))
|
||||
val addressRepo = Singleton.getAddressRepository(context)
|
||||
val addressRepo = Singleton.getAddressRepository(ctx)
|
||||
val exportContacts = ContactExport.exportContacts(addressRepo.getContacts())
|
||||
zip.write(
|
||||
exportContacts.toJsonString(true).toByteArray()
|
||||
)
|
||||
zip.closeEntry()
|
||||
|
||||
val messageRepo = Singleton.getMessageRepository(context)
|
||||
val messageRepo = Singleton.getMessageRepository(ctx)
|
||||
zip.putNextEntry(ZipEntry("labels.json"))
|
||||
val exportLabels = MessageExport.exportLabels(messageRepo.getLabels())
|
||||
zip.write(
|
||||
@ -128,7 +130,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
zip.closeEntry()
|
||||
}
|
||||
|
||||
val contentUri = getUriForFile(context, "ch.dissem.apps.abit.fileprovider", temp)
|
||||
val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", temp)
|
||||
val intent = Intent(android.content.Intent.ACTION_SEND)
|
||||
intent.type = "application/zip"
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "abit-export.zip")
|
||||
@ -161,8 +163,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
}
|
||||
}
|
||||
|
||||
private fun processEntry(zipFile: Uri, entry: String, processor: (JsonArray<*>) -> Unit) {
|
||||
ZipInputStream(context.contentResolver.openInputStream(zipFile)).use { zip ->
|
||||
private fun processEntry(ctx: Context, zipFile: Uri, entry: String, processor: (JsonArray<*>) -> Unit) =
|
||||
ZipInputStream(ctx.contentResolver.openInputStream(zipFile)).use { zip ->
|
||||
var nextEntry = zip.nextEntry
|
||||
while (nextEntry != null) {
|
||||
if (nextEntry.name == entry) {
|
||||
@ -171,38 +173,38 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
nextEntry = zip.nextEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
val ctx = context ?: throw IllegalStateException("No context available")
|
||||
when (requestCode) {
|
||||
WRITE_EXPORT_REQUEST_CODE -> Preferences.cleanupExportDirectory(context)
|
||||
WRITE_EXPORT_REQUEST_CODE -> Preferences.cleanupExportDirectory(ctx)
|
||||
READ_IMPORT_REQUEST_CODE -> {
|
||||
if (resultCode == Activity.RESULT_OK && data?.data != null) {
|
||||
val dialog = indeterminateProgressDialog(R.string.import_data_summary, R.string.import_data)
|
||||
doAsync {
|
||||
val ctx = Singleton.getBitmessageContext(context)
|
||||
val bmc = Singleton.getBitmessageContext(ctx)
|
||||
val labels = mutableMapOf<String, Label>()
|
||||
val zipFile = data.data
|
||||
|
||||
processEntry(zipFile, "contacts.json") { json ->
|
||||
processEntry(ctx, zipFile, "contacts.json") { json ->
|
||||
ContactExport.importContacts(json).forEach { contact ->
|
||||
ctx.addresses.save(contact)
|
||||
bmc.addresses.save(contact)
|
||||
}
|
||||
}
|
||||
ctx.messages.getLabels().forEach { label ->
|
||||
bmc.messages.getLabels().forEach { label ->
|
||||
labels[label.toString()] = label
|
||||
}
|
||||
processEntry(zipFile, "labels.json") { json ->
|
||||
processEntry(ctx, zipFile, "labels.json") { json ->
|
||||
MessageExport.importLabels(json).forEach { label ->
|
||||
if (!labels.contains(label.toString())) {
|
||||
ctx.messages.save(label)
|
||||
bmc.messages.save(label)
|
||||
labels[label.toString()] = label
|
||||
}
|
||||
}
|
||||
}
|
||||
processEntry(zipFile, "messages.json") { json ->
|
||||
processEntry(ctx, zipFile, "messages.json") { json ->
|
||||
MessageExport.importMessages(json, labels).forEach { message ->
|
||||
ctx.messages.save(message)
|
||||
bmc.messages.save(message)
|
||||
}
|
||||
}
|
||||
uiThread {
|
||||
@ -227,19 +229,21 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
||||
when (key) {
|
||||
PREFERENCE_TRUSTED_NODE -> {
|
||||
val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null)
|
||||
val ctx = context ?: throw IllegalStateException("No context available")
|
||||
if (node != null) {
|
||||
SyncAdapter.startSync(activity)
|
||||
SyncAdapter.startSync(ctx)
|
||||
} else {
|
||||
SyncAdapter.stopSync(activity)
|
||||
SyncAdapter.stopSync(ctx)
|
||||
}
|
||||
}
|
||||
PREFERENCE_SERVER_POW -> {
|
||||
val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null)
|
||||
if (node != null) {
|
||||
val ctx = context ?: throw IllegalStateException("No context available")
|
||||
if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
|
||||
SyncAdapter.startPowSync(activity)
|
||||
SyncAdapter.startPowSync(ctx)
|
||||
} else {
|
||||
SyncAdapter.stopPowSync(activity)
|
||||
SyncAdapter.stopPowSync(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package ch.dissem.apps.abit
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import com.mikepenz.materialize.MaterializeBuilder
|
||||
import kotlinx.android.synthetic.main.activity_status.*
|
||||
|
@ -33,14 +33,16 @@ class StatusFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val bmc = Singleton.getBitmessageContext(context)
|
||||
val bmc = Singleton.getBitmessageContext(
|
||||
context ?: throw IllegalStateException("No context available")
|
||||
)
|
||||
val status = StringBuilder()
|
||||
for (address in bmc.addresses.getIdentities()) {
|
||||
status.append(address.address).append('\n')
|
||||
}
|
||||
status.append('\n')
|
||||
status.append(bmc.status())
|
||||
(view.findViewById(R.id.content) as TextView).text = status
|
||||
view.findViewById<TextView>(R.id.content).text = status
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ class AddressSelectorAdapter(identities: List<BitmessageAddress>) : RecyclerView
|
||||
|
||||
class ViewHolder internal constructor(v: View) : RecyclerView.ViewHolder(v) {
|
||||
var data: Selectable<BitmessageAddress>? = null
|
||||
val checkbox = v.findViewById(R.id.checkbox) as CheckBox
|
||||
val address = v.findViewById(R.id.address) as TextView
|
||||
val checkbox = v.findViewById<CheckBox>(R.id.checkbox)!!
|
||||
val address = v.findViewById<TextView>(R.id.address)!!
|
||||
|
||||
init {
|
||||
checkbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
@ -63,12 +63,8 @@ class AddressSelectorAdapter(identities: List<BitmessageAddress>) : RecyclerView
|
||||
|
||||
val selected: List<BitmessageAddress>
|
||||
get() {
|
||||
val result = LinkedList<BitmessageAddress>()
|
||||
for (selectable in data) {
|
||||
if (selectable.selected) {
|
||||
result.add(selectable.data)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return data
|
||||
.filter { it.selected }
|
||||
.mapTo(LinkedList()) { it.data }
|
||||
}
|
||||
}
|
||||
|
@ -37,15 +37,9 @@ import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
* An adapter for contacts. Can be filtered by alias or address.
|
||||
*/
|
||||
class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
|
||||
private val inflater: LayoutInflater
|
||||
private val originalData: List<BitmessageAddress>
|
||||
private var data: List<BitmessageAddress> = emptyList()
|
||||
|
||||
init {
|
||||
inflater = LayoutInflater.from(ctx)
|
||||
originalData = Singleton.getAddressRepository(ctx).getContacts()
|
||||
data = originalData
|
||||
}
|
||||
private val inflater = LayoutInflater.from(ctx)
|
||||
private val originalData = Singleton.getAddressRepository(ctx).getContacts()
|
||||
private var data: List<BitmessageAddress> = originalData
|
||||
|
||||
override fun getCount() = data.size
|
||||
|
||||
@ -69,10 +63,15 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
|
||||
override fun getFilter(): Filter = ContactFilter()
|
||||
|
||||
private inner class ViewHolder(val view: View) {
|
||||
val avatar = view.findViewById(R.id.avatar) as ImageView
|
||||
val name = view.findViewById(R.id.name) as TextView
|
||||
val address = view.findViewById(R.id.address) as TextView
|
||||
val avatar = view.findViewById<ImageView>(R.id.avatar)!!
|
||||
val name = view.findViewById<TextView>(R.id.name)!!
|
||||
val address = view.findViewById<TextView>(R.id.address)!!
|
||||
|
||||
init {
|
||||
view.tag = this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private inner class ContactFilter : Filter() {
|
||||
override fun performFiltering(prefix: CharSequence?): Filter.FilterResults {
|
||||
@ -116,7 +115,7 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
|
||||
return results
|
||||
}
|
||||
|
||||
override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) {
|
||||
override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
data = results.values as List<BitmessageAddress>
|
||||
if (results.count > 0) {
|
||||
|
@ -19,6 +19,6 @@ package ch.dissem.apps.abit.adapter
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
class Selectable<T>(val data: T) {
|
||||
class Selectable<out T>(val data: T) {
|
||||
var selected = false
|
||||
}
|
||||
|
@ -72,12 +72,12 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie
|
||||
}
|
||||
|
||||
class ViewHolder(v: View) : AbstractSwipeableItemViewHolder(v) {
|
||||
val container = v.findViewById(R.id.container) as FrameLayout
|
||||
val avatar = v.findViewById(R.id.avatar) as ImageView
|
||||
val status = v.findViewById(R.id.status) as ImageView
|
||||
val sender = v.findViewById(R.id.sender) as TextView
|
||||
val subject = v.findViewById(R.id.subject) as TextView
|
||||
val extract = v.findViewById(R.id.text) as TextView
|
||||
val container = v.findViewById<FrameLayout>(R.id.container)!!
|
||||
val avatar = v.findViewById<ImageView>(R.id.avatar)!!
|
||||
val status = v.findViewById<ImageView>(R.id.status)!!
|
||||
val sender = v.findViewById<TextView>(R.id.sender)!!
|
||||
val subject = v.findViewById<TextView>(R.id.subject)!!
|
||||
val extract = v.findViewById<TextView>(R.id.text)!!
|
||||
|
||||
override fun getSwipeableContainerView() = container
|
||||
}
|
||||
@ -110,20 +110,17 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie
|
||||
|
||||
fun remove(item: Plaintext) {
|
||||
val index = data.indexOf(item)
|
||||
data.removeIf { it.id == item.id }
|
||||
data.removeAll { it.id == item.id }
|
||||
notifyItemRemoved(index)
|
||||
}
|
||||
|
||||
fun update(item: Plaintext) {
|
||||
data.replaceAll {
|
||||
if (it.id == item.id) {
|
||||
item
|
||||
} else {
|
||||
it
|
||||
val index = data.indexOfFirst { it.id == item.id }
|
||||
if (index >= 0) {
|
||||
data[index] = item
|
||||
notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
notifyItemChanged(data.indexOf(item))
|
||||
}
|
||||
|
||||
fun clear(newLabel: Label?) {
|
||||
label = newLabel
|
||||
@ -188,28 +185,25 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie
|
||||
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun onGetSwipeReactionType(holder: ViewHolder, position: Int, x: Int, y: Int): Int {
|
||||
return if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) {
|
||||
override fun onGetSwipeReactionType(holder: ViewHolder, position: Int, x: Int, y: Int): Int =
|
||||
if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) {
|
||||
REACTION_CAN_SWIPE_LEFT or REACTION_CAN_NOT_SWIPE_RIGHT_WITH_RUBBER_BAND_EFFECT
|
||||
} else {
|
||||
REACTION_CAN_SWIPE_BOTH_H
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
override fun onSetSwipeBackground(holder: ViewHolder, position: Int, type: Int) {
|
||||
var bgRes = 0
|
||||
when (type) {
|
||||
DRAWABLE_SWIPE_NEUTRAL_BACKGROUND -> bgRes = R.drawable.bg_swipe_item_neutral
|
||||
DRAWABLE_SWIPE_LEFT_BACKGROUND -> bgRes = R.drawable.bg_swipe_item_left
|
||||
override fun onSetSwipeBackground(holder: ViewHolder, position: Int, type: Int) =
|
||||
holder.itemView.setBackgroundResource(when (type) {
|
||||
DRAWABLE_SWIPE_NEUTRAL_BACKGROUND -> R.drawable.bg_swipe_item_neutral
|
||||
DRAWABLE_SWIPE_LEFT_BACKGROUND -> R.drawable.bg_swipe_item_left
|
||||
DRAWABLE_SWIPE_RIGHT_BACKGROUND -> if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) {
|
||||
bgRes = R.drawable.bg_swipe_item_neutral
|
||||
R.drawable.bg_swipe_item_neutral
|
||||
} else {
|
||||
bgRes = R.drawable.bg_swipe_item_right
|
||||
}
|
||||
}
|
||||
holder.itemView.setBackgroundResource(bgRes)
|
||||
R.drawable.bg_swipe_item_right
|
||||
}
|
||||
else -> R.drawable.bg_swipe_item_neutral
|
||||
})
|
||||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int) =
|
||||
@ -219,6 +213,8 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun onSwipeItemStarted(holder: ViewHolder?, position: Int) = Unit
|
||||
|
||||
fun setSelectedPosition(selectedPosition: Int) {
|
||||
val oldPosition = this.selectedPosition
|
||||
this.selectedPosition = selectedPosition
|
||||
|
@ -17,11 +17,7 @@
|
||||
package ch.dissem.apps.abit.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
|
||||
import java.util.Arrays
|
||||
|
||||
import ch.dissem.bitmessage.InternalContext
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
|
||||
|
||||
@ -46,9 +42,7 @@ class SwitchingProofOfWorkEngine(
|
||||
}
|
||||
}
|
||||
|
||||
override fun setContext(context: InternalContext) {
|
||||
listOf(option, fallback)
|
||||
override fun setContext(context: InternalContext) = listOf(option, fallback)
|
||||
.filterIsInstance<InternalContext.ContextHolder>()
|
||||
.forEach { it.setContext(context) }
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import org.jetbrains.anko.uiThread
|
||||
|
||||
class AddIdentityDialogFragment : AppCompatDialogFragment() {
|
||||
private lateinit var bmc: BitmessageContext
|
||||
private var parent: ViewGroup? = null
|
||||
|
||||
override fun onAttach(context: Context?) {
|
||||
super.onAttach(context)
|
||||
@ -50,13 +51,15 @@ class AddIdentityDialogFragment : AppCompatDialogFragment() {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
dialog.setTitle(R.string.add_identity)
|
||||
parent = container
|
||||
return inflater.inflate(R.layout.dialog_add_identity, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
ok.setOnClickListener(View.OnClickListener {
|
||||
val ctx = activity.baseContext
|
||||
val ctx = activity?.baseContext ?: throw IllegalStateException("No context available")
|
||||
|
||||
when (radioGroup.checkedRadioButtonId) {
|
||||
R.id.create_identity -> {
|
||||
Toast.makeText(ctx,
|
||||
@ -84,14 +87,14 @@ class AddIdentityDialogFragment : AppCompatDialogFragment() {
|
||||
}
|
||||
|
||||
private fun addChanDialog() {
|
||||
val activity = activity
|
||||
val ctx = activity.baseContext
|
||||
val dialogView = activity.layoutInflater.inflate(R.layout.dialog_input_passphrase, null)
|
||||
val activity = activity ?: throw IllegalStateException("No activity available")
|
||||
val ctx = activity.baseContext ?: throw IllegalStateException("No context available")
|
||||
val dialogView = activity.layoutInflater.inflate(R.layout.dialog_input_passphrase, parent)
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.add_chan)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
val passphrase = dialogView.findViewById(R.id.passphrase) as TextView
|
||||
val passphrase = dialogView.findViewById<TextView>(R.id.passphrase)
|
||||
Toast.makeText(ctx, R.string.toast_long_running_operation,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
val pass = passphrase.text.toString()
|
||||
|
@ -22,7 +22,6 @@ import android.support.v7.app.AppCompatDialogFragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import ch.dissem.apps.abit.MainActivity
|
||||
import ch.dissem.apps.abit.R
|
||||
@ -53,7 +52,7 @@ class DeterministicIdentityDialogFragment : AppCompatDialogFragment() {
|
||||
super.onViewCreated(dialogView, savedInstanceState)
|
||||
ok.setOnClickListener {
|
||||
dismiss()
|
||||
val context = activity.baseContext
|
||||
val context = activity?.baseContext ?: throw IllegalStateException("No context available")
|
||||
val passphraseText = passphrase.text.toString()
|
||||
|
||||
Toast.makeText(context, R.string.toast_long_running_operation, Toast.LENGTH_SHORT).show()
|
||||
|
@ -39,12 +39,12 @@ class SelectEncodingDialogFragment : AppCompatDialogFragment() {
|
||||
private lateinit var encoding: Plaintext.Encoding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
encoding = (arguments.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding) ?: SIMPLE
|
||||
encoding = (arguments?.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding) ?: SIMPLE
|
||||
dialog.setTitle(R.string.select_encoding_title)
|
||||
return inflater.inflate(R.layout.dialog_select_message_encoding, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
when (encoding) {
|
||||
SIMPLE -> radioGroup.check(R.id.simple)
|
||||
@ -62,7 +62,7 @@ class SelectEncodingDialogFragment : AppCompatDialogFragment() {
|
||||
}
|
||||
val result = Intent()
|
||||
result.putExtra(EXTRA_ENCODING, encoding)
|
||||
targetFragment.onActivityResult(targetRequestCode, RESULT_OK, result)
|
||||
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, result)
|
||||
dismiss()
|
||||
})
|
||||
dismiss.setOnClickListener { dismiss() }
|
||||
|
@ -3,19 +3,16 @@ package ch.dissem.apps.abit.drawer
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Point
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import android.widget.RelativeLayout
|
||||
|
||||
import com.mikepenz.materialdrawer.AccountHeader
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.util.Drawables
|
||||
import com.mikepenz.materialdrawer.AccountHeader
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
|
||||
class ProfileImageListener(private val ctx: Context) : AccountHeader.OnAccountHeaderProfileImageListener {
|
||||
|
||||
|
@ -49,7 +49,5 @@ class ProfileSelectionListener(
|
||||
return false
|
||||
}
|
||||
|
||||
private fun addIdentityDialog() {
|
||||
AddIdentityDialogFragment().show(fragmentManager, "dialog")
|
||||
}
|
||||
private fun addIdentityDialog() = AddIdentityDialogFragment().show(fragmentManager, "dialog")
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ package ch.dissem.apps.abit.listener
|
||||
* implement. This mechanism allows activities to be notified of item
|
||||
* selections.
|
||||
*/
|
||||
interface ListSelectionListener<T> {
|
||||
interface ListSelectionListener<in T> {
|
||||
/**
|
||||
* Callback for when an item has been selected.
|
||||
*/
|
||||
|
@ -19,13 +19,14 @@ package ch.dissem.apps.abit.notification
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import org.jetbrains.anko.notificationManager
|
||||
|
||||
/**
|
||||
* Some base class to create and handle notifications.
|
||||
*/
|
||||
abstract class AbstractNotification(ctx: Context) {
|
||||
protected val ctx = ctx.applicationContext
|
||||
protected val manager = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
protected val ctx = ctx.applicationContext!!
|
||||
private val manager = ctx.notificationManager
|
||||
var notification: Notification? = null
|
||||
protected set
|
||||
protected var showing = false
|
||||
|
@ -18,7 +18,7 @@ package ch.dissem.apps.abit.notification
|
||||
|
||||
import android.content.Context
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v7.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat
|
||||
|
||||
import ch.dissem.apps.abit.R
|
||||
|
||||
@ -30,7 +30,7 @@ import ch.dissem.apps.abit.R
|
||||
*/
|
||||
class ErrorNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
|
||||
private val builder = NotificationCompat.Builder(ctx)
|
||||
private val builder = NotificationCompat.Builder(ctx, "abit.error")
|
||||
.setContentTitle(ctx.getString(R.string.app_name))
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
||||
|
@ -21,7 +21,7 @@ import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.v7.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import ch.dissem.apps.abit.MainActivity
|
||||
import ch.dissem.apps.abit.R
|
||||
import ch.dissem.apps.abit.service.BitmessageIntentService
|
||||
@ -34,7 +34,7 @@ import kotlin.concurrent.fixedRateTimer
|
||||
*/
|
||||
class NetworkNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
|
||||
private val builder = NotificationCompat.Builder(ctx)
|
||||
private val builder = NotificationCompat.Builder(ctx, "abit.network")
|
||||
private var timer: Timer? = null
|
||||
|
||||
init {
|
||||
|
@ -20,7 +20,7 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.support.v7.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat.BigTextStyle
|
||||
import android.support.v4.app.NotificationCompat.InboxStyle
|
||||
import android.text.Spannable
|
||||
@ -43,7 +43,7 @@ import ch.dissem.apps.abit.util.Drawables.toBitmap
|
||||
class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
|
||||
fun singleNotification(plaintext: Plaintext): NewMessageNotification {
|
||||
val builder = NotificationCompat.Builder(ctx)
|
||||
val builder = NotificationCompat.Builder(ctx, CHANNEL_ID)
|
||||
val bigText = SpannableString(plaintext.subject + "\n" + plaintext.text)
|
||||
plaintext.subject?.let { subject ->
|
||||
bigText.setSpan(SPAN_EMPHASIS, 0, subject.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
@ -83,7 +83,7 @@ class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
* * {}` block
|
||||
*/
|
||||
fun multiNotification(unacknowledged: Collection<Plaintext>, numberOfUnacknowledgedMessages: Int): NewMessageNotification {
|
||||
val builder = NotificationCompat.Builder(ctx)
|
||||
val builder = NotificationCompat.Builder(ctx, CHANNEL_ID)
|
||||
builder.setSmallIcon(R.drawable.ic_notification_new_message)
|
||||
.setContentTitle(ctx.getString(R.string.n_new_messages, numberOfUnacknowledgedMessages))
|
||||
.setContentText(ctx.getString(R.string.app_name))
|
||||
@ -113,5 +113,6 @@ class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
companion object {
|
||||
private val NEW_MESSAGE_NOTIFICATION_ID = 1
|
||||
private val SPAN_EMPHASIS = StyleSpan(Typeface.BOLD)
|
||||
private val CHANNEL_ID = "abit.message"
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package ch.dissem.apps.abit.notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.v7.app.NotificationCompat
|
||||
import android.support.v4.app.NotificationCompat
|
||||
|
||||
import ch.dissem.apps.abit.MainActivity
|
||||
import ch.dissem.apps.abit.R
|
||||
@ -33,7 +33,7 @@ import kotlin.concurrent.fixedRateTimer
|
||||
*/
|
||||
class ProofOfWorkNotification(ctx: Context) : AbstractNotification(ctx) {
|
||||
|
||||
private val builder = NotificationCompat.Builder(ctx)
|
||||
private val builder = NotificationCompat.Builder(ctx, "abit.pow")
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setUsesChronometer(true)
|
||||
.setOngoing(true)
|
||||
|
@ -17,26 +17,18 @@
|
||||
package ch.dissem.apps.abit.pow
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.synchronization.SyncAdapter
|
||||
import ch.dissem.apps.abit.util.Preferences
|
||||
import ch.dissem.bitmessage.InternalContext
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import ch.dissem.bitmessage.extensions.CryptoCustomMessage
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
|
||||
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
|
||||
import ch.dissem.bitmessage.utils.Singleton.cryptography
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
@ -54,7 +46,7 @@ class ServerPowEngine(private val ctx: Context) : ProofOfWorkEngine, InternalCon
|
||||
}
|
||||
}
|
||||
|
||||
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) {
|
||||
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) =
|
||||
pool.execute {
|
||||
val identity = Singleton.getIdentity(ctx) ?: throw RuntimeException("No Identity for calculating POW")
|
||||
|
||||
@ -80,7 +72,6 @@ class ServerPowEngine(private val ctx: Context) : ProofOfWorkEngine, InternalCon
|
||||
LOG.error(e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setContext(context: InternalContext) {
|
||||
this.context = context
|
||||
|
@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import ch.dissem.bitmessage.entity.payload.V3Pubkey
|
||||
import ch.dissem.bitmessage.entity.payload.V4Pubkey
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey
|
||||
import ch.dissem.bitmessage.exception.ApplicationException
|
||||
import ch.dissem.bitmessage.factory.Factory
|
||||
import ch.dissem.bitmessage.ports.AddressRepository
|
||||
import ch.dissem.bitmessage.utils.Encode
|
||||
@ -149,13 +148,11 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository {
|
||||
return result
|
||||
}
|
||||
|
||||
override fun save(address: BitmessageAddress) {
|
||||
if (exists(address)) {
|
||||
override fun save(address: BitmessageAddress) = if (exists(address)) {
|
||||
update(address)
|
||||
} else {
|
||||
insert(address)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exists(address: BitmessageAddress): Boolean {
|
||||
val db = sql.readableDatabase
|
||||
@ -219,15 +216,6 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository {
|
||||
db.delete(TABLE_NAME, "address = ?", arrayOf(address.address))
|
||||
}
|
||||
|
||||
fun getById(id: String): BitmessageAddress {
|
||||
val result = find("address = '$id'")
|
||||
return if (result.isNotEmpty()) {
|
||||
result[0]
|
||||
} else {
|
||||
throw ApplicationException("Address with id $id not found.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAddress(address: String) = find("address = '$address'").firstOrNull()
|
||||
|
||||
companion object {
|
||||
|
@ -18,7 +18,6 @@ package ch.dissem.apps.abit.repository
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteConstraintException
|
||||
import ch.dissem.apps.abit.repository.SqlHelper.Companion.join
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
|
||||
|
@ -48,12 +48,6 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
|
||||
super.findMessages(label, offset, limit)
|
||||
}
|
||||
|
||||
fun findMessageIds(label: Label) = if (label === LABEL_ARCHIVE) {
|
||||
findIds("id NOT IN (SELECT message_id FROM Message_Label)")
|
||||
} else {
|
||||
findIds("id IN (SELECT message_id FROM Message_Label WHERE label_id=${label.id})")
|
||||
}
|
||||
|
||||
public override fun findLabels(where: String): List<Label> {
|
||||
val result = LinkedList<Label>()
|
||||
|
||||
@ -203,27 +197,6 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context:
|
||||
db.update(PARENTS_TABLE_NAME, values, where, null)
|
||||
}
|
||||
|
||||
private fun findIds(where: String): List<Long> {
|
||||
val result = LinkedList<Long>()
|
||||
|
||||
// Define a projection that specifies which columns from the database
|
||||
// you will actually use after this query.
|
||||
val projection = arrayOf(COLUMN_ID)
|
||||
|
||||
val db = sql.readableDatabase
|
||||
db.query(
|
||||
TABLE_NAME, projection,
|
||||
where, null, null, null,
|
||||
"$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC"
|
||||
).use { c ->
|
||||
while (c.moveToNext()) {
|
||||
val id = c.getLong(0)
|
||||
result.add(id)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun find(where: String, offset: Int, limit: Int): List<Plaintext> {
|
||||
val result = LinkedList<Plaintext>()
|
||||
|
||||
|
@ -54,10 +54,10 @@ class AndroidNodeRegistry(private val sql: SqlHelper) : NodeRegistry {
|
||||
statement.bindLong(1, node.stream)
|
||||
statement.bindBlob(2, node.IPv6)
|
||||
statement.bindLong(3, node.port.toLong())
|
||||
try {
|
||||
return statement.simpleQueryForLong()
|
||||
return try {
|
||||
statement.simpleQueryForLong()
|
||||
} catch (e: SQLiteDoneException) {
|
||||
return null
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,9 +117,8 @@ class AndroidProofOfWorkRepository(private val sql: SqlHelper) : ProofOfWorkRepo
|
||||
|
||||
}
|
||||
|
||||
override fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) {
|
||||
override fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) =
|
||||
putObject(ProofOfWorkRepository.Item(objectMessage, nonceTrialsPerByte, extraBytes))
|
||||
}
|
||||
|
||||
override fun removeObject(initialHash: ByteArray) {
|
||||
val db = sql.writableDatabase
|
||||
|
@ -29,12 +29,9 @@ import java.util.*
|
||||
*/
|
||||
class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME, null, DATABASE_VERSION) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
onUpgrade(db, 0, DATABASE_VERSION)
|
||||
}
|
||||
override fun onCreate(db: SQLiteDatabase) = onUpgrade(db, 0, DATABASE_VERSION)
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
mapOf(
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = mapOf(
|
||||
0 to {
|
||||
executeMigration(db, "V1.0__Create_table_inventory")
|
||||
executeMigration(db, "V1.1__Create_table_address")
|
||||
@ -63,14 +60,12 @@ class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME,
|
||||
7 to {
|
||||
setMissingConversationIds(db)
|
||||
}
|
||||
).filterKeys { it in oldVersion..(newVersion - 1) }.forEach { (_, v) -> v.invoke() }
|
||||
}
|
||||
).filterKeys { it in oldVersion until newVersion }.forEach { (_, v) -> v.invoke() }
|
||||
|
||||
/**
|
||||
* Set UUIDs for all messages that have no conversation ID
|
||||
*/
|
||||
private fun setMissingConversationIds(db: SQLiteDatabase) {
|
||||
db.query(
|
||||
private fun setMissingConversationIds(db: SQLiteDatabase) = db.query(
|
||||
"Message", arrayOf("id"),
|
||||
"conversation IS NULL", null, null, null, null
|
||||
).use { c ->
|
||||
@ -80,8 +75,6 @@ class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun setMissingConversationId(id: Long, db: SQLiteDatabase) {
|
||||
val values = ContentValues(1)
|
||||
values.put("conversation", UuidUtils.asBytes(UUID.randomUUID()))
|
||||
@ -98,7 +91,5 @@ class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME,
|
||||
// If you change the database schema, you must increment the database version.
|
||||
private val DATABASE_VERSION = 7
|
||||
val DATABASE_NAME = "jabit.db"
|
||||
|
||||
internal fun join(vararg types: Enum<*>): String = types.joinToString(separator = "', '", prefix = "'", postfix = "'", transform = { it.name })
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class BitmessageService : Service() {
|
||||
@Volatile private var running = false
|
||||
|
||||
val isRunning: Boolean
|
||||
get() = running && Singleton.bitmessageContext?.isRunning() ?: false
|
||||
get() = running && Singleton.bitmessageContext?.isRunning() == true
|
||||
|
||||
val status: Property
|
||||
get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context")
|
||||
|
@ -38,19 +38,12 @@ class ProofOfWorkService : Service() {
|
||||
notification = ProofOfWorkNotification(this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return PowBinder(this)
|
||||
}
|
||||
override fun onBind(intent: Intent) = PowBinder(this)
|
||||
|
||||
class PowBinder internal constructor(private val service: ProofOfWorkService) : Binder() {
|
||||
private val notification: ProofOfWorkNotification
|
||||
private val notification = service.notification
|
||||
|
||||
init {
|
||||
this.notification = service.notification
|
||||
}
|
||||
|
||||
fun process(item: PowItem) {
|
||||
synchronized(queue) {
|
||||
fun process(item: PowItem) = synchronized(queue) {
|
||||
service.startService(Intent(service, ProofOfWorkService::class.java))
|
||||
service.startForeground(ONGOING_NOTIFICATION_ID,
|
||||
notification.notification)
|
||||
@ -63,10 +56,27 @@ class ProofOfWorkService : Service() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class PowItem(val initialHash: ByteArray, val targetValue: ByteArray, val callback: ProofOfWorkEngine.Callback) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as PowItem
|
||||
|
||||
if (!Arrays.equals(initialHash, other.initialHash)) return false
|
||||
if (!Arrays.equals(targetValue, other.targetValue)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
data class PowItem(val initialHash: ByteArray, val targetValue: ByteArray, val callback: ProofOfWorkEngine.Callback)
|
||||
override fun hashCode(): Int {
|
||||
var result = Arrays.hashCode(initialHash)
|
||||
result = 31 * result + Arrays.hashCode(targetValue)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateNonce(item: PowItem) {
|
||||
notification.start(item)
|
||||
|
@ -38,14 +38,12 @@ class ServicePowEngine(private val ctx: Context) : ProofOfWorkEngine {
|
||||
private var service: PowBinder? = null
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
synchronized(lock) {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) = synchronized(lock) {
|
||||
this@ServicePowEngine.service = service as PowBinder
|
||||
while (!queue.isEmpty()) {
|
||||
service.process(queue.poll())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
service = null
|
||||
|
@ -50,8 +50,8 @@ object Singleton {
|
||||
private var powRepo: AndroidProofOfWorkRepository? = null
|
||||
private var creatingIdentity: Boolean = false
|
||||
|
||||
fun getBitmessageContext(context: Context): BitmessageContext {
|
||||
return init({ bitmessageContext }, { bitmessageContext = it }) {
|
||||
fun getBitmessageContext(context: Context): BitmessageContext =
|
||||
init({ bitmessageContext }, { bitmessageContext = it }) {
|
||||
val ctx = context.applicationContext
|
||||
val sqlHelper = SqlHelper(ctx)
|
||||
val powRepo = AndroidProofOfWorkRepository(sqlHelper)
|
||||
@ -75,7 +75,6 @@ object Singleton {
|
||||
.doNotSendPubkeyOnIdentityCreation()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessageListener(ctx: Context) = init({ messageListener }, { messageListener = it }) { MessageListener(ctx) }
|
||||
|
||||
@ -85,8 +84,8 @@ object Singleton {
|
||||
|
||||
fun getProofOfWorkRepository(ctx: Context) = powRepo ?: getBitmessageContext(ctx).internals.proofOfWorkRepository
|
||||
|
||||
fun getIdentity(ctx: Context): BitmessageAddress? {
|
||||
return init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc ->
|
||||
fun getIdentity(ctx: Context): BitmessageAddress? =
|
||||
init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc ->
|
||||
val identities = bmc.addresses.getIdentities()
|
||||
if (identities.isNotEmpty()) {
|
||||
identities[0]
|
||||
@ -112,7 +111,6 @@ object Singleton {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setIdentity(identity: BitmessageAddress) {
|
||||
if (identity.privateKey == null)
|
||||
@ -122,8 +120,8 @@ object Singleton {
|
||||
|
||||
fun getConversationService(ctx: Context) = init(ctx, { conversationService }, { conversationService = it }) { ConversationService(it.messages) }
|
||||
|
||||
private inline fun <T> init(crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: () -> T): T {
|
||||
return getter() ?: {
|
||||
private inline fun <T> init(crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: () -> T): T =
|
||||
getter() ?: {
|
||||
synchronized(Singleton) {
|
||||
getter() ?: {
|
||||
val v = creator()
|
||||
@ -132,10 +130,9 @@ object Singleton {
|
||||
}.invoke()
|
||||
}
|
||||
}.invoke()
|
||||
}
|
||||
|
||||
private inline fun <T> init(ctx: Context, crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: (BitmessageContext) -> T): T {
|
||||
return getter() ?: {
|
||||
private inline fun <T> init(ctx: Context, crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: (BitmessageContext) -> T): T =
|
||||
getter() ?: {
|
||||
val bmc = getBitmessageContext(ctx)
|
||||
synchronized(Singleton) {
|
||||
getter() ?: {
|
||||
@ -146,4 +143,3 @@ object Singleton {
|
||||
}
|
||||
}.invoke()
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,14 @@ import ch.dissem.apps.abit.util.NetworkUtils
|
||||
import ch.dissem.apps.abit.util.Preferences
|
||||
|
||||
/**
|
||||
* Created by chrigu on 18.08.17.
|
||||
* Starts the Bitmessage "full node" service if conditions allow it
|
||||
*/
|
||||
class StartServiceReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
if (intent?.action == "android.intent.action.BOOT_COMPLETED") {
|
||||
if (Preferences.isFullNodeActive(context)) {
|
||||
NetworkUtils.enableNode(context, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,11 @@ class StartupNodeOnWifiService : JobService() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStopJob(params: JobParameters?): Boolean {
|
||||
if (Preferences.isWifiOnly(this)) {
|
||||
override fun onStopJob(params: JobParameters?) = if (Preferences.isWifiOnly(this)) {
|
||||
// Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes
|
||||
// depending on Android version.
|
||||
return Preferences.isFullNodeActive(this)
|
||||
Preferences.isFullNodeActive(this)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
|
||||
/**
|
||||
* A bound Service that instantiates the authenticator
|
||||
|
@ -30,28 +30,17 @@ class SyncService : Service() {
|
||||
/**
|
||||
* Instantiate the sync adapter object.
|
||||
*/
|
||||
override fun onCreate() {
|
||||
// Create the sync adapter as a singleton.
|
||||
// Set the sync adapter as syncable
|
||||
// Disallow parallel syncs
|
||||
synchronized(syncAdapterLock) {
|
||||
override fun onCreate() = synchronized(syncAdapterLock) {
|
||||
if (syncAdapter == null) {
|
||||
syncAdapter = SyncAdapter(this, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that allows the system to invoke
|
||||
* the sync adapter.
|
||||
*/
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
// Get the object that allows external processes
|
||||
// to call onPerformSync(). The object is created
|
||||
// in the base class code when the SyncAdapter
|
||||
// constructors call super()
|
||||
return syncAdapter?.syncAdapterBinder
|
||||
}
|
||||
override fun onBind(intent: Intent) = syncAdapter?.syncAdapterBinder
|
||||
|
||||
companion object {
|
||||
// Storage for an instance of the sync adapter
|
||||
|
@ -47,28 +47,24 @@ object Assets {
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
fun getStatusDrawable(status: Plaintext.Status): Int {
|
||||
when (status) {
|
||||
Plaintext.Status.RECEIVED -> return 0
|
||||
Plaintext.Status.DRAFT -> return R.drawable.draft
|
||||
Plaintext.Status.PUBKEY_REQUESTED -> return R.drawable.public_key
|
||||
Plaintext.Status.DOING_PROOF_OF_WORK -> return R.drawable.ic_notification_proof_of_work
|
||||
Plaintext.Status.SENT -> return R.drawable.sent
|
||||
Plaintext.Status.SENT_ACKNOWLEDGED -> return R.drawable.sent_acknowledged
|
||||
else -> return 0
|
||||
}
|
||||
fun getStatusDrawable(status: Plaintext.Status) = when (status) {
|
||||
Plaintext.Status.RECEIVED -> 0
|
||||
Plaintext.Status.DRAFT -> R.drawable.draft
|
||||
Plaintext.Status.PUBKEY_REQUESTED -> R.drawable.public_key
|
||||
Plaintext.Status.DOING_PROOF_OF_WORK -> R.drawable.ic_notification_proof_of_work
|
||||
Plaintext.Status.SENT -> R.drawable.sent
|
||||
Plaintext.Status.SENT_ACKNOWLEDGED -> R.drawable.sent_acknowledged
|
||||
else -> 0
|
||||
}
|
||||
|
||||
@StringRes
|
||||
fun getStatusString(status: Plaintext.Status): Int {
|
||||
when (status) {
|
||||
Plaintext.Status.RECEIVED -> return R.string.status_received
|
||||
Plaintext.Status.DRAFT -> return R.string.status_draft
|
||||
Plaintext.Status.PUBKEY_REQUESTED -> return R.string.status_public_key
|
||||
Plaintext.Status.DOING_PROOF_OF_WORK -> return R.string.proof_of_work_title
|
||||
Plaintext.Status.SENT -> return R.string.status_sent
|
||||
Plaintext.Status.SENT_ACKNOWLEDGED -> return R.string.status_sent_acknowledged
|
||||
else -> return 0
|
||||
}
|
||||
fun getStatusString(status: Plaintext.Status) = when (status) {
|
||||
Plaintext.Status.RECEIVED -> R.string.status_received
|
||||
Plaintext.Status.DRAFT -> R.string.status_draft
|
||||
Plaintext.Status.PUBKEY_REQUESTED -> R.string.status_public_key
|
||||
Plaintext.Status.DOING_PROOF_OF_WORK -> R.string.proof_of_work_title
|
||||
Plaintext.Status.SENT -> R.string.status_sent
|
||||
Plaintext.Status.SENT_ACKNOWLEDGED -> R.string.status_sent_acknowledged
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
@ -33,5 +33,5 @@ object Constants {
|
||||
|
||||
const val BITMESSAGE_URL_SCHEMA = "bitmessage:"
|
||||
|
||||
val BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b")
|
||||
val BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b")!!
|
||||
}
|
||||
|
@ -39,9 +39,7 @@ object Labels {
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun getColor(label: Label): Int {
|
||||
return if (label.type == null) {
|
||||
fun getColor(label: Label) = if (label.type == null) {
|
||||
label.color
|
||||
} else 0xFF000000.toInt()
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import java.math.BigInteger
|
||||
object PowStats {
|
||||
private val TWO_POW_64: BigInteger = BigInteger.valueOf(2).pow(64)
|
||||
|
||||
var averagePowUnitTime = 0L
|
||||
var powCount = 0L
|
||||
private var averagePowUnitTime = 0L
|
||||
private var powCount = 0L
|
||||
|
||||
fun getExpectedPowTimeInMilliseconds(ctx: Context, target: ByteArray): Long {
|
||||
if (averagePowUnitTime == 0L) {
|
||||
|
@ -19,7 +19,6 @@ package ch.dissem.apps.abit.util
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import ch.dissem.apps.abit.R
|
||||
import ch.dissem.apps.abit.listener.WifiReceiver
|
||||
import ch.dissem.apps.abit.notification.ErrorNotification
|
||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_FULL_NODE
|
||||
import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK
|
||||
@ -27,7 +26,6 @@ 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_WIFI_ONLY
|
||||
import org.jetbrains.anko.connectivityManager
|
||||
import org.jetbrains.anko.networkStatsManager
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -24,17 +24,15 @@ import java.util.regex.Pattern
|
||||
object Strings {
|
||||
private val WHITESPACES = Pattern.compile("\\s+")
|
||||
|
||||
private fun trim(string: CharSequence?, length: Int) = if (string == null) {
|
||||
""
|
||||
} else if (string.length <= length) {
|
||||
string
|
||||
} else {
|
||||
string.subSequence(0, length)
|
||||
private fun trim(string: CharSequence?, length: Int) = when {
|
||||
string == null -> ""
|
||||
string.length <= length -> string
|
||||
else -> string.subSequence(0, length)
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the string to 200 characters and normalizes all whitespaces by replacing any sequence
|
||||
* of whitespace characters with a single space character.
|
||||
*/
|
||||
fun prepareMessageExtract(string: CharSequence?) = WHITESPACES.matcher(trim(string, 200)).replaceAll(" ")
|
||||
fun prepareMessageExtract(string: CharSequence?): String = WHITESPACES.matcher(trim(string, 200)).replaceAll(" ")
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.9 KiB |
9
app/src/main/res/drawable/header.xml
Normal file
9
app/src/main/res/drawable/header.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="315"
|
||||
android:endColor="#ff6502"
|
||||
android:centerColor="#ff4f02"
|
||||
android:startColor="#ff3902"
|
||||
android:type="linear" />
|
||||
</shape>
|
@ -13,8 +13,8 @@
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
app:iiv_color="@android:color/black"
|
||||
app:iiv_icon="cmd-label" />
|
||||
app:ico_color="@android:color/black"
|
||||
app:ico_icon="cmd-label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label"
|
||||
|
@ -70,7 +70,7 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:iiv_color="@android:color/black"
|
||||
app:iiv_icon="cmd-rss"/>
|
||||
app:ico_color="@android:color/black"
|
||||
app:ico_icon="cmd-rss"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/send"
|
||||
app:showAsAction="always"
|
||||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_action_send"
|
||||
android:title="@string/send"/>
|
||||
<item
|
||||
|
@ -20,7 +20,7 @@
|
||||
<string name="add_deterministic_address">Deterministic identity</string>
|
||||
<string name="add_deterministic_address_description">Create or recreate a deterministic identity</string>
|
||||
<string name="add_chan">Add Chan</string>
|
||||
<string name="add_chan_description">Create or join a isChan</string>
|
||||
<string name="add_chan_description">Create or join a Chan</string>
|
||||
<string name="title_activity_open_bitmessage_link">Import Contact</string>
|
||||
|
||||
<string name="connection_info_1">Stream #%1$d: one connection</string>
|
||||
|
@ -32,14 +32,14 @@ import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
class AndroidAddressRepositoryTest : TestBase() {
|
||||
private val CONTACT_A = "BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"
|
||||
private val CONTACT_B = "BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj"
|
||||
private val CONTACT_C = "BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke"
|
||||
private val contactA = "BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"
|
||||
private val contactB = "BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj"
|
||||
private val contactC = "BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke"
|
||||
|
||||
private lateinit var IDENTITY_A: String
|
||||
private lateinit var IDENTITY_B: String
|
||||
private lateinit var identityA: String
|
||||
private lateinit var identityB: String
|
||||
|
||||
private lateinit var repo: AndroidAddressRepository
|
||||
|
||||
@ -50,21 +50,23 @@ class AndroidAddressRepositoryTest : TestBase() {
|
||||
|
||||
repo = AndroidAddressRepository(sqlHelper)
|
||||
|
||||
repo.save(BitmessageAddress(CONTACT_A))
|
||||
repo.save(BitmessageAddress(CONTACT_B))
|
||||
repo.save(BitmessageAddress(CONTACT_C))
|
||||
repo.save(BitmessageAddress(contactA))
|
||||
repo.save(BitmessageAddress(contactB))
|
||||
repo.save(BitmessageAddress(contactC))
|
||||
|
||||
val identityA = BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK))
|
||||
repo.save(identityA)
|
||||
IDENTITY_A = identityA.address
|
||||
val identityB = BitmessageAddress(PrivateKey(false, 1, 1000, 1000))
|
||||
repo.save(identityB)
|
||||
IDENTITY_B = identityB.address
|
||||
BitmessageAddress(PrivateKey(false, 1, 1000, 1000, DOES_ACK)).let {
|
||||
repo.save(it)
|
||||
identityA = it.address
|
||||
}
|
||||
BitmessageAddress(PrivateKey(false, 1, 1000, 1000)).let {
|
||||
repo.save(it)
|
||||
identityB = it.address
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure contact can be found`() {
|
||||
val address = BitmessageAddress(CONTACT_A)
|
||||
val address = BitmessageAddress(contactA)
|
||||
assertEquals(4, address.version)
|
||||
assertEquals(address, repo.findContact(address.tag!!))
|
||||
assertNull(repo.findIdentity(address.tag!!))
|
||||
@ -72,7 +74,7 @@ class AndroidAddressRepositoryTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `ensure identity can be found`() {
|
||||
val identity = BitmessageAddress(IDENTITY_A)
|
||||
val identity = BitmessageAddress(identityA)
|
||||
assertEquals(4, identity.version)
|
||||
assertNull(repo.findContact(identity.tag!!))
|
||||
|
||||
@ -131,7 +133,7 @@ class AndroidAddressRepositoryTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `ensure existing address is updated`() {
|
||||
var address = repo.getAddress(CONTACT_A)
|
||||
var address = repo.getAddress(contactA)
|
||||
address!!.alias = "Test-Alias"
|
||||
repo.save(address)
|
||||
address = repo.getAddress(address.address)
|
||||
@ -140,10 +142,10 @@ class AndroidAddressRepositoryTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `ensure existing keys are not deleted`() {
|
||||
val address = BitmessageAddress(IDENTITY_A)
|
||||
val address = BitmessageAddress(identityA)
|
||||
address.alias = "Test"
|
||||
repo.save(address)
|
||||
val identityA = repo.getAddress(IDENTITY_A)
|
||||
val identityA = repo.getAddress(identityA)
|
||||
assertNotNull(identityA!!.pubkey)
|
||||
assertNotNull(identityA.privateKey)
|
||||
assertEquals("Test", identityA.alias)
|
||||
@ -169,14 +171,14 @@ class AndroidAddressRepositoryTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `ensure address is removed`() {
|
||||
val address = repo.getAddress(IDENTITY_A)
|
||||
val address = repo.getAddress(identityA)
|
||||
repo.remove(address!!)
|
||||
assertNull(repo.getAddress(IDENTITY_A))
|
||||
assertNull(repo.getAddress(identityA))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure address can be retrieved`() {
|
||||
val address = repo.getAddress(IDENTITY_A)
|
||||
val address = repo.getAddress(identityA)
|
||||
assertNotNull(address)
|
||||
assertNotNull(address!!.privateKey)
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ import org.robolectric.annotation.Config
|
||||
import java.util.*
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
class AndroidInventoryTest : TestBase() {
|
||||
private lateinit var inventory: Inventory
|
||||
|
||||
@ -134,14 +134,12 @@ class AndroidInventoryTest : TestBase() {
|
||||
assertNull(inventory.getObject(inventoryVectorIgnore))
|
||||
}
|
||||
|
||||
private fun getObjectMessage(stream: Long, TTL: Long, payload: ObjectPayload): ObjectMessage {
|
||||
return ObjectMessage(
|
||||
private fun getObjectMessage(stream: Long, TTL: Long, payload: ObjectPayload) = ObjectMessage(
|
||||
nonce = ByteArray(8),
|
||||
expiresTime = now + TTL,
|
||||
stream = stream,
|
||||
payload = payload
|
||||
)
|
||||
}
|
||||
|
||||
private val getPubkey: GetPubkey = GetPubkey(BitmessageAddress("BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"))
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ import org.robolectric.annotation.Config
|
||||
import java.util.*
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
class AndroidMessageRepositoryTest : TestBase() {
|
||||
private lateinit var contactA: BitmessageAddress
|
||||
private lateinit var contactB: BitmessageAddress
|
||||
@ -320,8 +320,7 @@ class AndroidMessageRepositoryTest : TestBase() {
|
||||
return repo.getMessage(root.id!!)
|
||||
}
|
||||
|
||||
private fun hasMessage(subject: String?, body: String?): Matcher<Plaintext> {
|
||||
return object : BaseMatcher<Plaintext>() {
|
||||
private fun hasMessage(subject: String?, body: String?) = object : BaseMatcher<Plaintext>() {
|
||||
override fun describeTo(description: Description) {
|
||||
description.appendText("Subject: ").appendText(subject)
|
||||
description.appendText(", ")
|
||||
@ -343,4 +342,3 @@ class AndroidMessageRepositoryTest : TestBase() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import java.util.*
|
||||
* as the initial nodes' IP addresses are determined by DNS lookup.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
class AndroidNodeRegistryTest : TestBase() {
|
||||
private lateinit var registry: NodeRegistry
|
||||
|
||||
@ -51,17 +51,16 @@ class AndroidNodeRegistryTest : TestBase() {
|
||||
registry = AndroidNodeRegistry(sqlHelper)
|
||||
|
||||
registry.offerAddresses(Arrays.asList(
|
||||
createAddress(1, 8444, 1, now),
|
||||
createAddress(2, 8444, 1, now),
|
||||
createAddress(3, 8444, 1, now),
|
||||
createAddress(4, 8444, 2, now)
|
||||
createAddress(lastByte = 1),
|
||||
createAddress(lastByte = 2),
|
||||
createAddress(lastByte = 3),
|
||||
createAddress(lastByte = 4, stream = 2)
|
||||
))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure getKnownNodes() without streams yields empty`() {
|
||||
fun `ensure getKnownNodes() without streams yields empty`() =
|
||||
assertThat(registry.getKnownAddresses(10), empty<NetworkAddress>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure predefined node is returned when database is empty`() {
|
||||
@ -86,26 +85,25 @@ class AndroidNodeRegistryTest : TestBase() {
|
||||
@Test
|
||||
fun `ensure offered addresses are added`() {
|
||||
registry.offerAddresses(Arrays.asList(
|
||||
createAddress(1, 8444, 1, now),
|
||||
createAddress(10, 8444, 1, now),
|
||||
createAddress(11, 8444, 1, now)
|
||||
createAddress(lastByte = 1),
|
||||
createAddress(lastByte = 10),
|
||||
createAddress(lastByte = 11)
|
||||
))
|
||||
|
||||
var knownAddresses = registry.getKnownAddresses(1000, 1)
|
||||
assertEquals(5, knownAddresses.size.toLong())
|
||||
|
||||
registry.offerAddresses(listOf(createAddress(1, 8445, 1, now)))
|
||||
registry.offerAddresses(listOf(createAddress(lastByte = 1, port = 8445)))
|
||||
|
||||
knownAddresses = registry.getKnownAddresses(1000, 1)
|
||||
assertEquals(6, knownAddresses.size.toLong())
|
||||
}
|
||||
|
||||
private fun createAddress(lastByte: Int, port: Int, stream: Long, time: Long): NetworkAddress {
|
||||
return NetworkAddress.Builder()
|
||||
private fun createAddress(lastByte: Int, port: Int = 8444, stream: Long = 1, time: Long = now) =
|
||||
NetworkAddress.Builder()
|
||||
.ipv6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, lastByte)
|
||||
.port(port)
|
||||
.stream(stream)
|
||||
.time(time)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package ch.dissem.bitmessage.repository
|
||||
|
||||
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||
import ch.dissem.apps.abit.BuildConfig
|
||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository
|
||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
||||
import ch.dissem.apps.abit.repository.AndroidProofOfWorkRepository
|
||||
@ -50,14 +49,14 @@ import kotlin.properties.Delegates
|
||||
* @author Christian Basler
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = intArrayOf(LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
@Config(sdk = intArrayOf(LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
||||
class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
private lateinit var repo: ProofOfWorkRepository
|
||||
private lateinit var addressRepo: AddressRepository
|
||||
private lateinit var messageRepo: MessageRepository
|
||||
|
||||
private var initialHash1: ByteArray by Delegates.notNull<ByteArray>()
|
||||
private var initialHash2: ByteArray by Delegates.notNull<ByteArray>()
|
||||
private var initialHash1: ByteArray by Delegates.notNull()
|
||||
private var initialHash2: ByteArray by Delegates.notNull()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@ -74,9 +73,13 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
cryptography = cryptography()
|
||||
)
|
||||
|
||||
repo.putObject(ObjectMessage.Builder()
|
||||
.payload(GetPubkey(BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn"))).build(),
|
||||
1000, 1000)
|
||||
repo.putObject(
|
||||
objectMessage = ObjectMessage.Builder()
|
||||
.payload(GetPubkey(BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn")))
|
||||
.build(),
|
||||
nonceTrialsPerByte = 1000,
|
||||
extraBytes = 1000
|
||||
)
|
||||
initialHash1 = repo.getItems()[0]
|
||||
|
||||
val sender = loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")
|
||||
@ -91,21 +94,27 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
.status(Plaintext.Status.DOING_PROOF_OF_WORK)
|
||||
.build()
|
||||
messageRepo.save(plaintext)
|
||||
initialHash2 = cryptography().getInitialHash(plaintext.ackMessage!!)
|
||||
plaintext.ackMessage!!.let { ackMessage ->
|
||||
initialHash2 = cryptography().getInitialHash(ackMessage)
|
||||
repo.putObject(ProofOfWorkRepository.Item(
|
||||
plaintext.ackMessage!!,
|
||||
1000, 1000,
|
||||
UnixTime.now + 10 * UnixTime.MINUTE,
|
||||
plaintext
|
||||
objectMessage = ackMessage,
|
||||
nonceTrialsPerByte = 1000, extraBytes = 1000,
|
||||
expirationTime = UnixTime.now + 10 * UnixTime.MINUTE,
|
||||
message = plaintext
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure object is stored`() {
|
||||
val sizeBefore = repo.getItems().size
|
||||
repo.putObject(ObjectMessage.Builder()
|
||||
.payload(GetPubkey(BitmessageAddress("BM-2D9U2hv3YBMHM1zERP32anKfVKohyPN9x2"))).build(),
|
||||
1000, 1000)
|
||||
repo.putObject(
|
||||
objectMessage = ObjectMessage.Builder()
|
||||
.payload(GetPubkey(BitmessageAddress("BM-2D9U2hv3YBMHM1zERP32anKfVKohyPN9x2")))
|
||||
.build(),
|
||||
nonceTrialsPerByte = 1000,
|
||||
extraBytes = 1000
|
||||
)
|
||||
assertThat(repo.getItems().size, `is`(sizeBefore + 1))
|
||||
}
|
||||
|
||||
@ -124,12 +133,15 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
.status(Plaintext.Status.DOING_PROOF_OF_WORK)
|
||||
.build()
|
||||
messageRepo.save(plaintext)
|
||||
plaintext.ackMessage!!.let { ackMessage ->
|
||||
repo.putObject(ProofOfWorkRepository.Item(
|
||||
plaintext.ackMessage!!,
|
||||
1000, 1000,
|
||||
UnixTime.now + 10 * UnixTime.MINUTE,
|
||||
plaintext
|
||||
objectMessage = ackMessage,
|
||||
nonceTrialsPerByte = 1000,
|
||||
extraBytes = 1000,
|
||||
expirationTime = UnixTime.now + 10 * UnixTime.MINUTE,
|
||||
message = plaintext
|
||||
))
|
||||
}
|
||||
assertThat(repo.getItems().size, `is`(sizeBefore + 1))
|
||||
}
|
||||
|
||||
@ -156,7 +168,7 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException::class)
|
||||
fun `ensure retrieving nonexisting item causes exception`() {
|
||||
fun `ensure retrieving non-existing item causes exception`() {
|
||||
repo.getItem(ByteArray(0))
|
||||
}
|
||||
|
||||
@ -168,7 +180,7 @@ class AndroidProofOfWorkRepositoryTest : TestBase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure deletion of nonexisting item is handled silently`() {
|
||||
fun `ensure deletion of non-existing item is handled silently`() {
|
||||
repo.removeObject(ByteArray(0))
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,7 @@ open class TestBase {
|
||||
port: Int = 0,
|
||||
connectionTTL: Long = 0,
|
||||
connectionLimit: Int = 0
|
||||
): InternalContext {
|
||||
return spy(InternalContext(
|
||||
) = spy(InternalContext(
|
||||
cryptography,
|
||||
inventory,
|
||||
nodeRegistry,
|
||||
@ -78,7 +77,6 @@ open class TestBase {
|
||||
connectionTTL,
|
||||
connectionLimit
|
||||
))
|
||||
}
|
||||
|
||||
fun randomInventoryVector(): InventoryVector {
|
||||
val bytes = ByteArray(32)
|
||||
@ -86,16 +84,16 @@ open class TestBase {
|
||||
return InventoryVector(bytes)
|
||||
}
|
||||
|
||||
fun getResource(resourceName: String) =
|
||||
private fun getResource(resourceName: String) =
|
||||
TestBase::class.java.classLoader.getResourceAsStream(resourceName)
|
||||
|
||||
fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage {
|
||||
private fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage {
|
||||
val data = getBytes(resourceName)
|
||||
val `in` = ByteArrayInputStream(data)
|
||||
return Factory.getObjectMessage(version, `in`, data.size) ?: throw NoSuchElementException("error loading object message")
|
||||
}
|
||||
|
||||
fun getBytes(resourceName: String): ByteArray {
|
||||
private fun getBytes(resourceName: String): ByteArray {
|
||||
val `in` = getResource(resourceName)
|
||||
val out = ByteArrayOutputStream()
|
||||
val buffer = ByteArray(1024)
|
||||
|
16
build.gradle
16
build.gradle
@ -1,19 +1,14 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
configurations.all {
|
||||
// check for updates every build
|
||||
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.4-3'
|
||||
ext.anko_version = '0.10.1'
|
||||
ext.kotlin_version = '1.1.51'
|
||||
ext.anko_version = '0.10.2'
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -28,5 +23,6 @@ allprojects {
|
||||
mavenCentral()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||
maven { url "https://jitpack.io" }
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Wed Jul 05 00:16:56 CEST 2017
|
||||
#Mon Oct 23 08:19:50 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-all.zip
|
||||
|
Loading…
Reference in New Issue
Block a user