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