Merge branch 'feature/drafts' into develop
This commit is contained in:
commit
ec645d70ce
@ -62,7 +62,8 @@ dependencies {
|
|||||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||||
implementation "com.android.support:preference-v7:$supportVersion"
|
implementation "com.android.support:preference-v7:$supportVersion"
|
||||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||||
implementation "com.android.support:support-v4:$supportVersion"
|
implementation "com.android.support:support-v13:$supportVersion"
|
||||||
|
implementation "com.android.support:preference-v14:$supportVersion"
|
||||||
implementation "com.android.support:design:$supportVersion"
|
implementation "com.android.support:design:$supportVersion"
|
||||||
implementation "com.android.support:multidex:1.0.2"
|
implementation "com.android.support:multidex:1.0.2"
|
||||||
|
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="ch.dissem.apps.abit"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
package="ch.dissem.apps.abit">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name="android.support.multidex.MultiDexApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme"
|
android:supportsRtl="true"
|
||||||
android:name="android.support.multidex.MultiDexApplication">
|
android:theme="@style/AppTheme">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
@ -36,7 +36,7 @@
|
|||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".MainActivity"/>
|
android:value=".MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".AddressDetailActivity"
|
android:name=".AddressDetailActivity"
|
||||||
@ -45,42 +45,42 @@
|
|||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".MainActivity"/>
|
android:value=".MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".dialog.FullNodeDialogActivity"
|
android:name=".dialog.FullNodeDialogActivity"
|
||||||
android:label="@string/full_node"
|
android:label="@string/full_node"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog"/>
|
android:theme="@style/Theme.AppCompat.Light.Dialog" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ComposeMessageActivity"
|
android:name=".ComposeMessageActivity"
|
||||||
android:label="@string/compose_message"
|
android:label="@string/compose_message"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName=".MainActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".MainActivity"/>
|
android:value=".MainActivity" />
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SENDTO"/>
|
<action android:name="android.intent.action.SENDTO" />
|
||||||
|
|
||||||
<data android:scheme="bitmessage"/>
|
<data android:scheme="bitmessage" />
|
||||||
<data android:scheme="bitmsg"/>
|
<data android:scheme="bitmsg" />
|
||||||
<data android:scheme="bm"/>
|
<data android:scheme="bm" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
@ -88,14 +88,14 @@
|
|||||||
android:label="@string/title_activity_open_bitmessage_link"
|
android:label="@string/title_activity_open_bitmessage_link"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<data android:scheme="bitmessage"/>
|
<data android:scheme="bitmessage" />
|
||||||
<data android:scheme="bitmsg"/>
|
<data android:scheme="bitmsg" />
|
||||||
<data android:scheme="bm"/>
|
<data android:scheme="bm" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
@ -104,34 +104,34 @@
|
|||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName=".MainActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".MainActivity"/>
|
android:value=".MainActivity" />
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<data
|
<data
|
||||||
android:host="*"
|
android:host="*"
|
||||||
android:mimeType="*/*"
|
android:mimeType="*/*"
|
||||||
android:pathPattern=".*\\.dat"
|
android:pathPattern=".*\\.dat"
|
||||||
android:scheme="file"/>
|
android:scheme="file" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.BitmessageService"
|
android:name=".service.BitmessageService"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
<service
|
<service
|
||||||
android:name=".service.ProofOfWorkService"
|
android:name=".service.ProofOfWorkService"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- Synchronization -->
|
<!-- Synchronization -->
|
||||||
<provider
|
<provider
|
||||||
android:name=".synchronization.StubProvider"
|
android:name=".synchronization.StubProvider"
|
||||||
android:authorities="ch.dissem.apps.abit.provider"
|
android:authorities="ch.dissem.apps.abit.provider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:syncable="true"/>
|
android:syncable="true" />
|
||||||
|
|
||||||
<!-- Exports -->
|
<!-- Exports -->
|
||||||
<provider
|
<provider
|
||||||
@ -149,44 +149,54 @@
|
|||||||
android:exported="true"
|
android:exported="true"
|
||||||
tools:ignore="ExportedService">
|
tools:ignore="ExportedService">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.accounts.AccountAuthenticator"/>
|
<action android:name="android.accounts.AccountAuthenticator" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.accounts.AccountAuthenticator"
|
android:name="android.accounts.AccountAuthenticator"
|
||||||
android:resource="@xml/authenticator"/>
|
android:resource="@xml/authenticator" />
|
||||||
</service>
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name=".synchronization.SyncService"
|
android:name=".synchronization.SyncService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
tools:ignore="ExportedService">
|
tools:ignore="ExportedService">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.content.SyncAdapter"/>
|
<action android:name="android.content.SyncAdapter" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/syncadapter"/>
|
android:resource="@xml/syncadapter" />
|
||||||
</service>
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name=".service.BitmessageIntentService"
|
android:name=".service.BitmessageIntentService"
|
||||||
android:exported="false"/>
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- Receive Wi-Fi connection state changes -->
|
<!-- Receive Wi-Fi connection state changes -->
|
||||||
<receiver android:name=".listener.WifiReceiver" android:enabled="@bool/is_pre_api_21">
|
<receiver
|
||||||
|
android:name=".listener.WifiReceiver"
|
||||||
|
android:enabled="@bool/is_pre_api_21">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
<!-- This is bad for battery life, but needed on older devices to check
|
||||||
|
if WiFi is available. Let's be honest, the whole app is bad for
|
||||||
|
battery life. -->
|
||||||
|
<action
|
||||||
|
android:name="android.net.conn.CONNECTIVITY_CHANGE"
|
||||||
|
tools:ignore="BatteryLife" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name=".service.StartServiceReceiver" android:enabled="@bool/is_post_api_21">
|
<receiver
|
||||||
|
android:name=".service.StartServiceReceiver"
|
||||||
|
android:enabled="@bool/is_post_api_21">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.StartupNodeOnWifiService"
|
android:name=".service.StartupNodeOnWifiService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:exported="true"
|
||||||
android:exported="true"/>
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".StatusActivity"
|
android:name=".StatusActivity"
|
||||||
@ -194,7 +204,7 @@
|
|||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName=".MainActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".MainActivity"/>
|
android:value=".MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ class AddressDetailFragment : Fragment() {
|
|||||||
* The fragment argument representing the item ID that this fragment
|
* The fragment argument representing the item ID that this fragment
|
||||||
* represents.
|
* represents.
|
||||||
*/
|
*/
|
||||||
val ARG_ITEM = "item"
|
const val ARG_ITEM = "item"
|
||||||
val EXPORT_POSTFIX = ".keys.dat"
|
const val EXPORT_POSTFIX = ".keys.dat"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ class ComposeMessageActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val EXTRA_DRAFT = "ch.dissem.abit.Message.DRAFT"
|
||||||
const val EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER"
|
const val EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER"
|
||||||
const val EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT"
|
const val EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT"
|
||||||
const val EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT"
|
const val EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT"
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package ch.dissem.apps.abit
|
package ch.dissem.apps.abit
|
||||||
|
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
@ -25,6 +26,7 @@ import android.widget.AdapterView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_BROADCAST
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_BROADCAST
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_CONTENT
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_CONTENT
|
||||||
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_DRAFT
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_ENCODING
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_ENCODING
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_IDENTITY
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_IDENTITY
|
||||||
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_PARENT
|
import ch.dissem.apps.abit.ComposeMessageActivity.Companion.EXTRA_PARENT
|
||||||
@ -38,6 +40,9 @@ import ch.dissem.bitmessage.entity.BitmessageAddress
|
|||||||
import ch.dissem.bitmessage.entity.Plaintext
|
import ch.dissem.bitmessage.entity.Plaintext
|
||||||
import ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST
|
import ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST
|
||||||
import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
|
import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||||
import ch.dissem.bitmessage.entity.valueobject.extended.Message
|
import ch.dissem.bitmessage.entity.valueobject.extended.Message
|
||||||
import kotlinx.android.synthetic.main.fragment_compose_message.*
|
import kotlinx.android.synthetic.main.fragment_compose_message.*
|
||||||
|
|
||||||
@ -52,72 +57,107 @@ class ComposeMessageFragment : Fragment() {
|
|||||||
|
|
||||||
private var broadcast: Boolean = false
|
private var broadcast: Boolean = false
|
||||||
private var encoding: Plaintext.Encoding = Plaintext.Encoding.SIMPLE
|
private var encoding: Plaintext.Encoding = Plaintext.Encoding.SIMPLE
|
||||||
private var parent: Plaintext? = null
|
private val parents = mutableListOf<InventoryVector>()
|
||||||
|
|
||||||
|
private var draft: Plaintext? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
arguments?.let { arguments ->
|
arguments?.apply {
|
||||||
var id = arguments.getSerializable(EXTRA_IDENTITY) as? BitmessageAddress
|
val draft = getSerializable(EXTRA_DRAFT) as Plaintext?
|
||||||
if (context != null && (id == null || id.privateKey == null)) {
|
if (draft != null) {
|
||||||
id = Singleton.getIdentity(context!!)
|
this@ComposeMessageFragment.draft = draft
|
||||||
}
|
identity = draft.from
|
||||||
if (id?.privateKey != null) {
|
recipient = draft.to
|
||||||
identity = id
|
subject = draft.subject ?: ""
|
||||||
|
content = draft.text ?: ""
|
||||||
|
encoding = draft.encoding ?: Plaintext.Encoding.SIMPLE
|
||||||
|
parents.addAll(draft.parents)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException("No identity set for ComposeMessageFragment")
|
var id = getSerializable(EXTRA_IDENTITY) as? BitmessageAddress
|
||||||
}
|
if (context != null && (id == null || id.privateKey == null)) {
|
||||||
broadcast = arguments.getBoolean(EXTRA_BROADCAST, false)
|
id = Singleton.getIdentity(context!!)
|
||||||
if (arguments.containsKey(EXTRA_RECIPIENT)) {
|
}
|
||||||
recipient = arguments.getSerializable(EXTRA_RECIPIENT) as BitmessageAddress
|
if (id?.privateKey != null) {
|
||||||
}
|
identity = id
|
||||||
if (arguments.containsKey(EXTRA_SUBJECT)) {
|
} else {
|
||||||
subject = arguments.getString(EXTRA_SUBJECT)
|
throw IllegalStateException("No identity set for ComposeMessageFragment")
|
||||||
}
|
}
|
||||||
if (arguments.containsKey(EXTRA_CONTENT)) {
|
broadcast = getBoolean(EXTRA_BROADCAST, false)
|
||||||
content = arguments.getString(EXTRA_CONTENT)
|
if (containsKey(EXTRA_RECIPIENT)) {
|
||||||
}
|
recipient = getSerializable(EXTRA_RECIPIENT) as BitmessageAddress
|
||||||
encoding = arguments.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?: Plaintext.Encoding.SIMPLE
|
}
|
||||||
|
if (containsKey(EXTRA_SUBJECT)) {
|
||||||
|
subject = getString(EXTRA_SUBJECT)
|
||||||
|
}
|
||||||
|
if (containsKey(EXTRA_CONTENT)) {
|
||||||
|
content = getString(EXTRA_CONTENT)
|
||||||
|
}
|
||||||
|
encoding = getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding ?:
|
||||||
|
Plaintext.Encoding.SIMPLE
|
||||||
|
|
||||||
if (arguments.containsKey(EXTRA_PARENT)) {
|
if (containsKey(EXTRA_PARENT)) {
|
||||||
parent = arguments.getSerializable(EXTRA_PARENT) as Plaintext
|
val parent = getSerializable(EXTRA_PARENT) as Plaintext
|
||||||
|
parent.inventoryVector?.let { parents.add(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: {
|
} ?: throw IllegalStateException("No identity set for ComposeMessageFragment")
|
||||||
throw IllegalStateException("No identity set for ComposeMessageFragment")
|
|
||||||
}.invoke()
|
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(
|
||||||
savedInstanceState: Bundle?): View =
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
inflater.inflate(R.layout.fragment_compose_message, container, false)
|
savedInstanceState: Bundle?
|
||||||
|
): View = inflater.inflate(R.layout.fragment_compose_message, container, false)
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
if (broadcast) {
|
context?.let { ctx ->
|
||||||
recipient_input.visibility = View.GONE
|
val identities = Singleton.getAddressRepository(ctx).getIdentities()
|
||||||
} else {
|
sender_input.adapter = ContactAdapter(ctx, identities, true)
|
||||||
val adapter = ContactAdapter(context!!)
|
val index = identities.indexOf(Singleton.getIdentity(ctx))
|
||||||
recipient_input.setAdapter(adapter)
|
if (index >= 0) {
|
||||||
recipient_input.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) }
|
sender_input.setSelection(index)
|
||||||
recipient_input.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
|
||||||
recipient = adapter.getItem(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>) = Unit // leave current selection
|
|
||||||
}
|
}
|
||||||
recipient?.let { recipient_input.setText(it.toString()) }
|
|
||||||
}
|
|
||||||
subject_input.setText(subject)
|
|
||||||
body_input.setText(content)
|
|
||||||
|
|
||||||
when {
|
if (broadcast) {
|
||||||
recipient == null -> recipient_input.requestFocus()
|
recipient_input.visibility = View.GONE
|
||||||
subject.isEmpty() -> subject_input.requestFocus()
|
} else {
|
||||||
else -> {
|
val adapter = ContactAdapter(
|
||||||
body_input.requestFocus()
|
ctx,
|
||||||
body_input.setSelection(0)
|
Singleton.getAddressRepository(ctx).getContacts()
|
||||||
|
)
|
||||||
|
recipient_input.setAdapter(adapter)
|
||||||
|
recipient_input.onItemClickListener =
|
||||||
|
AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) }
|
||||||
|
recipient_input.onItemSelectedListener =
|
||||||
|
object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>,
|
||||||
|
view: View,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
|
recipient = adapter.getItem(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>) =
|
||||||
|
Unit // leave current selection
|
||||||
|
}
|
||||||
|
recipient?.let { recipient_input.setText(it.toString()) }
|
||||||
|
}
|
||||||
|
subject_input.setText(subject)
|
||||||
|
body_input.setText(content)
|
||||||
|
|
||||||
|
when {
|
||||||
|
recipient == null -> recipient_input.requestFocus()
|
||||||
|
subject.isEmpty() -> subject_input.requestFocus()
|
||||||
|
else -> {
|
||||||
|
body_input.requestFocus()
|
||||||
|
body_input.setSelection(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,18 +186,17 @@ class ComposeMessageFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = if (requestCode == 0 && data != null && resultCode == RESULT_OK) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) =
|
||||||
encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding
|
if (requestCode == 0 && data != null && resultCode == RESULT_OK) {
|
||||||
} else {
|
encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
} else {
|
||||||
}
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
}
|
||||||
|
|
||||||
private fun send() {
|
private fun build(ctx: Context): Plaintext {
|
||||||
val builder: Plaintext.Builder
|
val builder: Plaintext.Builder
|
||||||
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)
|
||||||
} else {
|
} else {
|
||||||
val inputString = recipient_input.text.toString()
|
val inputString = recipient_input.text.toString()
|
||||||
if (recipient == null || recipient?.toString() != inputString) {
|
if (recipient == null || recipient?.toString() != inputString) {
|
||||||
@ -175,42 +214,69 @@ class ComposeMessageFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (recipient == null) {
|
|
||||||
Toast.makeText(context, R.string.error_msg_recipient_missing, Toast.LENGTH_LONG).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
builder = Plaintext.Builder(MSG)
|
builder = Plaintext.Builder(MSG)
|
||||||
.from(identity)
|
.to(recipient)
|
||||||
.to(recipient)
|
|
||||||
}
|
}
|
||||||
|
val sender = sender_input.selectedItem as? ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
|
sender?.let { builder.from(it) }
|
||||||
if (!Preferences.requestAcknowledgements(ctx)) {
|
if (!Preferences.requestAcknowledgements(ctx)) {
|
||||||
builder.preventAck()
|
builder.preventAck()
|
||||||
}
|
}
|
||||||
when (encoding) {
|
when (encoding) {
|
||||||
Plaintext.Encoding.SIMPLE -> builder.message(
|
Plaintext.Encoding.SIMPLE -> builder.message(
|
||||||
subject_input.text.toString(),
|
subject_input.text.toString(),
|
||||||
body_input.text.toString()
|
body_input.text.toString()
|
||||||
)
|
)
|
||||||
Plaintext.Encoding.EXTENDED -> builder.message(
|
Plaintext.Encoding.EXTENDED -> builder.message(
|
||||||
Message.Builder()
|
ExtendedEncoding(
|
||||||
.subject(subject_input.text.toString())
|
Message(
|
||||||
.body(body_input.text.toString())
|
subject = subject_input.text.toString(),
|
||||||
.addParent(parent)
|
body = body_input.text.toString(),
|
||||||
.build()
|
parents = parents,
|
||||||
|
files = emptyList()
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else -> {
|
else -> {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
ctx,
|
ctx,
|
||||||
ctx.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(
|
||||||
subject_input.text.toString(),
|
subject_input.text.toString(),
|
||||||
body_input.text.toString()
|
body_input.text.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bmc.send(builder.build())
|
draft?.id?.let { builder.id(it) }
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
if (draft?.labels?.any { it.type == Label.Type.DRAFT } != false) {
|
||||||
|
context?.let { ctx ->
|
||||||
|
draft = build(ctx).also { msg ->
|
||||||
|
Singleton.labeler.markAsDraft(msg)
|
||||||
|
Singleton.getMessageRepository(ctx).save(msg)
|
||||||
|
}
|
||||||
|
Toast.makeText(ctx, "Message saved as draft", Toast.LENGTH_LONG).show()
|
||||||
|
} ?: throw IllegalStateException("Context is not available")
|
||||||
|
}
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun send() {
|
||||||
|
val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity")
|
||||||
|
if (recipient == null) {
|
||||||
|
Toast.makeText(ctx, R.string.error_msg_recipient_missing, Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
build(ctx).let { message ->
|
||||||
|
draft = message
|
||||||
|
Singleton.getBitmessageContext(ctx).send(message)
|
||||||
|
}
|
||||||
ctx.finish()
|
ctx.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ class Identicon(input: BitmessageAddress) : Drawable() {
|
|||||||
override fun getOpacity() = PixelFormat.TRANSPARENT
|
override fun getOpacity() = PixelFormat.TRANSPARENT
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val SIZE = 9
|
private const val SIZE = 9
|
||||||
private val CENTER_COLUMN = 5
|
private const val CENTER_COLUMN = 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,6 @@ class ImportIdentitiesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val WIF_DATA = "wif_data"
|
const val WIF_DATA = "wif_data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ package ch.dissem.apps.abit
|
|||||||
/**
|
/**
|
||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
interface ListHolder<L> {
|
interface ListHolder<in L> {
|
||||||
fun updateList(label: L)
|
fun updateList(label: L)
|
||||||
|
|
||||||
fun setActivateOnItemClick(activateOnItemClick: Boolean)
|
fun setActivateOnItemClick(activateOnItemClick: Boolean)
|
||||||
|
@ -146,12 +146,15 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
SyncAdapter.stopSync(this)
|
SyncAdapter.stopSync(this)
|
||||||
}
|
}
|
||||||
if (drawer.isDrawerOpen) {
|
if (drawer.isDrawerOpen) {
|
||||||
val lps = RelativeLayout.LayoutParams(ViewGroup
|
val lps = RelativeLayout.LayoutParams(
|
||||||
.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
ViewGroup
|
||||||
lps.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
|
.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
lps.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
).apply {
|
||||||
val margin = ((resources.displayMetrics.density * 12) as Number).toInt()
|
addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
|
||||||
lps.setMargins(margin, margin, margin, margin)
|
addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||||
|
val margin = ((resources.displayMetrics.density * 12) as Number).toInt()
|
||||||
|
setMargins(margin, margin, margin, margin)
|
||||||
|
}
|
||||||
|
|
||||||
ShowcaseView.Builder(this)
|
ShowcaseView.Builder(this)
|
||||||
.withMaterialShowcase()
|
.withMaterialShowcase()
|
||||||
@ -192,19 +195,23 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
|
|
||||||
private fun createDrawer(toolbar: Toolbar) {
|
private fun createDrawer(toolbar: Toolbar) {
|
||||||
val profiles = ArrayList<IProfile<*>>()
|
val profiles = ArrayList<IProfile<*>>()
|
||||||
profiles.add(ProfileSettingDrawerItem()
|
profiles.add(
|
||||||
.withName(getString(R.string.add_identity))
|
ProfileSettingDrawerItem()
|
||||||
.withDescription(getString(R.string.add_identity_summary))
|
.withName(getString(R.string.add_identity))
|
||||||
.withIcon(IconicsDrawable(this, GoogleMaterial.Icon.gmd_add)
|
.withDescription(getString(R.string.add_identity_summary))
|
||||||
.actionBar()
|
.withIcon(
|
||||||
.paddingDp(5)
|
IconicsDrawable(this, GoogleMaterial.Icon.gmd_add)
|
||||||
.colorRes(R.color.icons))
|
.actionBar()
|
||||||
.withIdentifier(ADD_IDENTITY.toLong())
|
.paddingDp(5)
|
||||||
|
.colorRes(R.color.icons)
|
||||||
|
)
|
||||||
|
.withIdentifier(ADD_IDENTITY.toLong())
|
||||||
)
|
)
|
||||||
profiles.add(ProfileSettingDrawerItem()
|
profiles.add(
|
||||||
.withName(getString(R.string.manage_identity))
|
ProfileSettingDrawerItem()
|
||||||
.withIcon(GoogleMaterial.Icon.gmd_settings)
|
.withName(getString(R.string.manage_identity))
|
||||||
.withIdentifier(MANAGE_IDENTITY.toLong())
|
.withIcon(GoogleMaterial.Icon.gmd_settings)
|
||||||
|
.withIdentifier(MANAGE_IDENTITY.toLong())
|
||||||
)
|
)
|
||||||
// Create the AccountHeader
|
// Create the AccountHeader
|
||||||
accountHeader = AccountHeaderBuilder()
|
accountHeader = AccountHeaderBuilder()
|
||||||
@ -212,26 +219,36 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
.withHeaderBackground(R.drawable.header)
|
.withHeaderBackground(R.drawable.header)
|
||||||
.withProfiles(profiles)
|
.withProfiles(profiles)
|
||||||
.withOnAccountHeaderProfileImageListener(ProfileImageListener(this))
|
.withOnAccountHeaderProfileImageListener(ProfileImageListener(this))
|
||||||
.withOnAccountHeaderListener(ProfileSelectionListener(this@MainActivity, supportFragmentManager))
|
.withOnAccountHeaderListener(
|
||||||
|
ProfileSelectionListener(
|
||||||
|
this@MainActivity,
|
||||||
|
supportFragmentManager
|
||||||
|
)
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
if (profiles.size > 2) { // There's always the add and manage identity items
|
if (profiles.size > 2) { // There's always the add and manage identity items
|
||||||
accountHeader.setActiveProfile(profiles[0], true)
|
accountHeader.setActiveProfile(profiles[0], true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val drawerItems = ArrayList<IDrawerItem<*, *>>()
|
val drawerItems = ArrayList<IDrawerItem<*, *>>()
|
||||||
drawerItems.add(PrimaryDrawerItem()
|
drawerItems.add(
|
||||||
.withIdentifier(LABEL_ARCHIVE.id as Long)
|
PrimaryDrawerItem()
|
||||||
.withName(R.string.archive)
|
.withIdentifier(LABEL_ARCHIVE.id as Long)
|
||||||
.withTag(LABEL_ARCHIVE)
|
.withName(R.string.archive)
|
||||||
.withIcon(CommunityMaterial.Icon.cmd_archive)
|
.withTag(LABEL_ARCHIVE)
|
||||||
|
.withIcon(CommunityMaterial.Icon.cmd_archive)
|
||||||
)
|
)
|
||||||
drawerItems.add(DividerDrawerItem())
|
drawerItems.add(DividerDrawerItem())
|
||||||
drawerItems.add(PrimaryDrawerItem()
|
drawerItems.add(
|
||||||
.withName(R.string.contacts_and_subscriptions)
|
PrimaryDrawerItem()
|
||||||
.withIcon(GoogleMaterial.Icon.gmd_contacts))
|
.withName(R.string.contacts_and_subscriptions)
|
||||||
drawerItems.add(PrimaryDrawerItem()
|
.withIcon(GoogleMaterial.Icon.gmd_contacts)
|
||||||
.withName(R.string.settings)
|
)
|
||||||
.withIcon(GoogleMaterial.Icon.gmd_settings))
|
drawerItems.add(
|
||||||
|
PrimaryDrawerItem()
|
||||||
|
.withName(R.string.settings)
|
||||||
|
.withIcon(GoogleMaterial.Icon.gmd_settings)
|
||||||
|
)
|
||||||
|
|
||||||
nodeSwitch = SwitchDrawerItem()
|
nodeSwitch = SwitchDrawerItem()
|
||||||
.withIdentifier(ID_NODE_SWITCH)
|
.withIdentifier(ID_NODE_SWITCH)
|
||||||
@ -369,7 +386,8 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
// we know that there are 2 setting elements.
|
// we know that there are 2 setting elements.
|
||||||
// Set the new profile above them ;)
|
// Set the new profile above them ;)
|
||||||
accountHeader.addProfile(
|
accountHeader.addProfile(
|
||||||
newProfile, accountHeader.profiles.size - 2)
|
newProfile, accountHeader.profiles.size - 2
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
accountHeader.addProfiles(newProfile)
|
accountHeader.addProfiles(newProfile)
|
||||||
}
|
}
|
||||||
@ -435,14 +453,31 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
// In two-pane mode, show the detail view in this activity by
|
// In two-pane mode, show the detail view in this activity by
|
||||||
// adding or replacing the detail fragment using a
|
// adding or replacing the detail fragment using a
|
||||||
// fragment transaction.
|
// fragment transaction.
|
||||||
val arguments = Bundle()
|
|
||||||
arguments.putSerializable(MessageDetailFragment.ARG_ITEM, item)
|
|
||||||
val fragment = when (item) {
|
val fragment = when (item) {
|
||||||
is Plaintext -> MessageDetailFragment()
|
is Plaintext -> {
|
||||||
is BitmessageAddress -> AddressDetailFragment()
|
if (item.labels.any { it.type == Label.Type.DRAFT }) {
|
||||||
|
ComposeMessageFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(ComposeMessageActivity.EXTRA_DRAFT, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MessageDetailFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(MessageDetailFragment.ARG_ITEM, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BitmessageAddress -> {
|
||||||
|
AddressDetailFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(AddressDetailFragment.ARG_ITEM, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}")
|
else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}")
|
||||||
}
|
}
|
||||||
fragment.arguments = arguments
|
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.message_detail_container, fragment)
|
.replace(R.id.message_detail_container, fragment)
|
||||||
.commit()
|
.commit()
|
||||||
@ -451,19 +486,29 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
// for the selected item ID.
|
// for the selected item ID.
|
||||||
val detailIntent = when (item) {
|
val detailIntent = when (item) {
|
||||||
is Plaintext -> {
|
is Plaintext -> {
|
||||||
Intent(this, MessageDetailActivity::class.java)
|
if (item.labels.any { it.type == Label.Type.DRAFT }) {
|
||||||
|
Intent(this, ComposeMessageActivity::class.java).apply {
|
||||||
|
putExtra(ComposeMessageActivity.EXTRA_DRAFT, item)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent(this, MessageDetailActivity::class.java).apply {
|
||||||
|
putExtra(MessageDetailFragment.ARG_ITEM, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BitmessageAddress -> Intent(this, AddressDetailActivity::class.java).apply {
|
||||||
|
putExtra(AddressDetailFragment.ARG_ITEM, item)
|
||||||
}
|
}
|
||||||
is BitmessageAddress -> Intent(this, AddressDetailActivity::class.java)
|
|
||||||
else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}")
|
else -> throw IllegalArgumentException("Plaintext or BitmessageAddress expected, but was ${item::class.simpleName}")
|
||||||
}
|
}
|
||||||
detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item)
|
|
||||||
startActivity(detailIntent)
|
startActivity(detailIntent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDetailView(fragment: Fragment) {
|
fun setDetailView(fragment: Fragment) {
|
||||||
if (hasDetailPane) {
|
if (hasDetailPane) {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
.replace(R.id.message_detail_container, fragment)
|
.replace(R.id.message_detail_container, fragment)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
@ -474,15 +519,15 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage"
|
const val EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage"
|
||||||
val EXTRA_SHOW_LABEL = "ch.dissem.abit.ShowLabel"
|
const val EXTRA_SHOW_LABEL = "ch.dissem.abit.ShowLabel"
|
||||||
val EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage"
|
const val EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage"
|
||||||
val ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"
|
const val ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"
|
||||||
|
|
||||||
val ADD_IDENTITY = 1
|
const val ADD_IDENTITY = 1
|
||||||
val MANAGE_IDENTITY = 2
|
const val MANAGE_IDENTITY = 2
|
||||||
|
|
||||||
private val ID_NODE_SWITCH: Long = 1
|
private const val ID_NODE_SWITCH: Long = 1
|
||||||
|
|
||||||
private var instance: WeakReference<MainActivity>? = null
|
private var instance: WeakReference<MainActivity>? = null
|
||||||
|
|
||||||
|
@ -70,7 +70,11 @@ class MessageDetailFragment : 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_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?) {
|
||||||
@ -84,6 +88,13 @@ class MessageDetailFragment : Fragment() {
|
|||||||
status.setImageResource(Assets.getStatusDrawable(item.status))
|
status.setImageResource(Assets.getStatusDrawable(item.status))
|
||||||
status.contentDescription = getString(Assets.getStatusString(item.status))
|
status.contentDescription = getString(Assets.getStatusString(item.status))
|
||||||
avatar.setImageDrawable(Identicon(item.from))
|
avatar.setImageDrawable(Identicon(item.from))
|
||||||
|
val senderClickListener: (View) -> Unit = {
|
||||||
|
MainActivity.apply {
|
||||||
|
onItemSelected(item.from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avatar.setOnClickListener(senderClickListener)
|
||||||
|
sender.setOnClickListener(senderClickListener)
|
||||||
sender.text = item.from.toString()
|
sender.text = item.from.toString()
|
||||||
item.to?.let { to ->
|
item.to?.let { to ->
|
||||||
recipient.text = to.toString()
|
recipient.text = to.toString()
|
||||||
@ -124,7 +135,11 @@ class MessageDetailFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showRelatedMessages(ctx: Context, 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<RecyclerView>(id)
|
val recyclerView = rootView.findViewById<RecyclerView>(id)
|
||||||
val adapter = RelatedMessageAdapter(ctx, messages)
|
val adapter = RelatedMessageAdapter(ctx, messages)
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
@ -136,8 +151,10 @@ class MessageDetailFragment : Fragment() {
|
|||||||
activity?.let { activity ->
|
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(
|
||||||
.gmd_markunread)
|
activity, menu, R.id.mark_unread, GoogleMaterial.Icon
|
||||||
|
.gmd_markunread
|
||||||
|
)
|
||||||
Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive)
|
Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +204,15 @@ class MessageDetailFragment : Fragment() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RelatedMessageAdapter internal constructor(private val ctx: Context, private val messages: List<Plaintext>) : RecyclerView.Adapter<RelatedMessageAdapter.ViewHolder>() {
|
private class RelatedMessageAdapter internal constructor(
|
||||||
|
private val ctx: Context,
|
||||||
|
private val messages: List<Plaintext>
|
||||||
|
) : RecyclerView.Adapter<RelatedMessageAdapter.ViewHolder>() {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RelatedMessageAdapter.ViewHolder {
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
): RelatedMessageAdapter.ViewHolder {
|
||||||
val context = parent.context
|
val context = parent.context
|
||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
|
|
||||||
@ -236,7 +259,8 @@ class MessageDetailFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LabelAdapter internal constructor(private val ctx: Context, labels: Set<Label>) : RecyclerView.Adapter<LabelAdapter.ViewHolder>() {
|
private class LabelAdapter internal constructor(private val ctx: Context, labels: Set<Label>) :
|
||||||
|
RecyclerView.Adapter<LabelAdapter.ViewHolder>() {
|
||||||
|
|
||||||
private val labels = labels.toMutableList()
|
private val labels = labels.toMutableList()
|
||||||
|
|
||||||
@ -274,7 +298,7 @@ class MessageDetailFragment : Fragment() {
|
|||||||
* The fragment argument representing the item ID that this fragment
|
* The fragment argument representing the item ID that this fragment
|
||||||
* represents.
|
* represents.
|
||||||
*/
|
*/
|
||||||
val ARG_ITEM = "item"
|
const val ARG_ITEM = "item"
|
||||||
|
|
||||||
fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } == true
|
fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } == true
|
||||||
}
|
}
|
||||||
|
@ -20,25 +20,22 @@ import android.content.Context
|
|||||||
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.BaseAdapter
|
import android.widget.*
|
||||||
import android.widget.Filter
|
|
||||||
import android.widget.Filterable
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
import ch.dissem.apps.abit.Identicon
|
import ch.dissem.apps.abit.Identicon
|
||||||
import ch.dissem.apps.abit.R
|
import ch.dissem.apps.abit.R
|
||||||
import ch.dissem.apps.abit.service.Singleton
|
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
|
private val originalData: List<BitmessageAddress>,
|
||||||
|
private val slim: Boolean = false
|
||||||
|
) :
|
||||||
|
BaseAdapter(), Filterable {
|
||||||
private val inflater = LayoutInflater.from(ctx)
|
private val inflater = LayoutInflater.from(ctx)
|
||||||
private val originalData = Singleton.getAddressRepository(ctx).getContacts()
|
|
||||||
private var data: List<BitmessageAddress> = originalData
|
private var data: List<BitmessageAddress> = originalData
|
||||||
|
|
||||||
override fun getCount() = data.size
|
override fun getCount() = data.size
|
||||||
@ -49,23 +46,33 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
|
|||||||
|
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
val viewHolder = if (convertView == null) {
|
val viewHolder = if (convertView == null) {
|
||||||
ViewHolder(inflater.inflate(R.layout.contact_row, parent, false))
|
ViewHolder(
|
||||||
|
inflater.inflate(
|
||||||
|
if (slim) {
|
||||||
|
R.layout.contact_row_slim
|
||||||
|
} else {
|
||||||
|
R.layout.contact_row
|
||||||
|
},
|
||||||
|
parent, false
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
convertView.tag as ViewHolder
|
convertView.tag as ViewHolder
|
||||||
}
|
}
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
viewHolder.avatar.setImageDrawable(Identicon(item))
|
viewHolder.avatar.setImageDrawable(Identicon(item))
|
||||||
viewHolder.name.text = item.toString()
|
viewHolder.name.text = item.toString()
|
||||||
viewHolder.address.text = item.address
|
viewHolder.address?.text = item.address
|
||||||
|
|
||||||
return viewHolder.view
|
return viewHolder.view
|
||||||
}
|
}
|
||||||
|
|
||||||
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<ImageView>(R.id.avatar)!!
|
val avatar: ImageView = view.findViewById(R.id.avatar)
|
||||||
val name = view.findViewById<TextView>(R.id.name)!!
|
val name: TextView = view.findViewById(R.id.name)
|
||||||
val address = view.findViewById<TextView>(R.id.address)!!
|
val address: TextView? = view.findViewById(R.id.address)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
view.tag = this
|
view.tag = this
|
||||||
@ -83,27 +90,29 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable {
|
|||||||
val newValues = ArrayList<BitmessageAddress>()
|
val newValues = ArrayList<BitmessageAddress>()
|
||||||
|
|
||||||
originalData
|
originalData
|
||||||
.forEach { value ->
|
.forEach { value ->
|
||||||
value.alias?.toLowerCase()?.let { alias ->
|
value.alias?.toLowerCase()?.let { alias ->
|
||||||
if (alias.startsWith(prefixString)) {
|
if (alias.startsWith(prefixString)) {
|
||||||
newValues.add(value)
|
newValues.add(value)
|
||||||
} else {
|
} else {
|
||||||
val words = alias.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
val words =
|
||||||
|
alias.split(" ".toRegex()).dropLastWhile { it.isEmpty() }
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
for (word in words) {
|
for (word in words) {
|
||||||
if (word.startsWith(prefixString)) {
|
if (word.startsWith(prefixString)) {
|
||||||
newValues.add(value)
|
newValues.add(value)
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: {
|
}
|
||||||
val address = value.address.toLowerCase()
|
} ?: {
|
||||||
if (address.contains(prefixString)) {
|
val address = value.address.toLowerCase()
|
||||||
newValues.add(value)
|
if (address.contains(prefixString)) {
|
||||||
}
|
newValues.add(value)
|
||||||
}.invoke()
|
}
|
||||||
}
|
}.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
results.values = newValues
|
results.values = newValues
|
||||||
results.count = newValues.size
|
results.count = newValues.size
|
||||||
|
@ -55,6 +55,6 @@ class ErrorNotification(ctx: Context) : AbstractNotification(ctx) {
|
|||||||
override val notificationId = ERROR_NOTIFICATION_ID
|
override val notificationId = ERROR_NOTIFICATION_ID
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val ERROR_NOTIFICATION_ID = 4
|
const val ERROR_NOTIFICATION_ID = 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) {
|
|||||||
override val notificationId = NEW_MESSAGE_NOTIFICATION_ID
|
override val notificationId = NEW_MESSAGE_NOTIFICATION_ID
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val NEW_MESSAGE_NOTIFICATION_ID = 1
|
private const val NEW_MESSAGE_NOTIFICATION_ID = 1
|
||||||
private val SPAN_EMPHASIS = StyleSpan(Typeface.BOLD)
|
private val SPAN_EMPHASIS = StyleSpan(Typeface.BOLD)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class AndroidInventory(private val sql: SqlHelper) : Inventory {
|
|||||||
private fun getCache(stream: Long): MutableMap<InventoryVector, Long> {
|
private fun getCache(stream: Long): MutableMap<InventoryVector, Long> {
|
||||||
fun addToCache(stream: Long): MutableMap<InventoryVector, Long> {
|
fun addToCache(stream: Long): MutableMap<InventoryVector, Long> {
|
||||||
val result: MutableMap<InventoryVector, Long> = ConcurrentHashMap()
|
val result: MutableMap<InventoryVector, Long> = ConcurrentHashMap()
|
||||||
cache.put(stream, result)
|
cache[stream] = result
|
||||||
|
|
||||||
val projection = arrayOf(COLUMN_HASH, COLUMN_EXPIRES)
|
val projection = arrayOf(COLUMN_HASH, COLUMN_EXPIRES)
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ class AndroidInventory(private val sql: SqlHelper) : Inventory {
|
|||||||
|
|
||||||
sql.writableDatabase.insertOrThrow(TABLE_NAME, null, values)
|
sql.writableDatabase.insertOrThrow(TABLE_NAME, null, values)
|
||||||
|
|
||||||
getCache(objectMessage.stream).put(iv, objectMessage.expiresTime)
|
getCache(objectMessage.stream)[iv] = objectMessage.expiresTime
|
||||||
} catch (e: SQLiteConstraintException) {
|
} catch (e: SQLiteConstraintException) {
|
||||||
LOG.trace(e.message, e)
|
LOG.trace(e.message, e)
|
||||||
}
|
}
|
||||||
|
@ -125,13 +125,13 @@ class AndroidProofOfWorkRepository(private val sql: SqlHelper) : ProofOfWorkRepo
|
|||||||
companion object {
|
companion object {
|
||||||
private val LOG = LoggerFactory.getLogger(AndroidProofOfWorkRepository::class.java)
|
private val LOG = LoggerFactory.getLogger(AndroidProofOfWorkRepository::class.java)
|
||||||
|
|
||||||
private val TABLE_NAME = "POW"
|
private const val TABLE_NAME = "POW"
|
||||||
private val COLUMN_INITIAL_HASH = "initial_hash"
|
private const val COLUMN_INITIAL_HASH = "initial_hash"
|
||||||
private val COLUMN_DATA = "data"
|
private const val COLUMN_DATA = "data"
|
||||||
private val COLUMN_VERSION = "version"
|
private const val COLUMN_VERSION = "version"
|
||||||
private val COLUMN_NONCE_TRIALS_PER_BYTE = "nonce_trials_per_byte"
|
private const val COLUMN_NONCE_TRIALS_PER_BYTE = "nonce_trials_per_byte"
|
||||||
private val COLUMN_EXTRA_BYTES = "extra_bytes"
|
private const val COLUMN_EXTRA_BYTES = "extra_bytes"
|
||||||
private val COLUMN_EXPIRATION_TIME = "expiration_time"
|
private const val COLUMN_EXPIRATION_TIME = "expiration_time"
|
||||||
private val COLUMN_MESSAGE_ID = "message_id"
|
private const val COLUMN_MESSAGE_ID = "message_id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME,
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// 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 const val DATABASE_VERSION = 7
|
||||||
val DATABASE_NAME = "jabit.db"
|
const val DATABASE_NAME = "jabit.db"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,9 @@ import android.net.ConnectivityManager
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import ch.dissem.apps.abit.notification.NetworkNotification
|
import ch.dissem.apps.abit.notification.NetworkNotification
|
||||||
import ch.dissem.apps.abit.notification.NetworkNotification.Companion.NETWORK_NOTIFICATION_ID
|
import ch.dissem.apps.abit.notification.NetworkNotification.Companion.NETWORK_NOTIFICATION_ID
|
||||||
import ch.dissem.apps.abit.util.NetworkUtils
|
|
||||||
import ch.dissem.apps.abit.util.Preferences
|
import ch.dissem.apps.abit.util.Preferences
|
||||||
import ch.dissem.bitmessage.BitmessageContext
|
import ch.dissem.bitmessage.BitmessageContext
|
||||||
import ch.dissem.bitmessage.utils.Property
|
import ch.dissem.bitmessage.utils.Property
|
||||||
import org.jetbrains.anko.connectivityManager
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a Service that returns an IBinder for the
|
* Define a Service that returns an IBinder for the
|
||||||
|
@ -19,7 +19,6 @@ package ch.dissem.apps.abit.service
|
|||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.IBinder
|
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
import ch.dissem.apps.abit.notification.ProofOfWorkNotification
|
import ch.dissem.apps.abit.notification.ProofOfWorkNotification
|
||||||
import ch.dissem.apps.abit.notification.ProofOfWorkNotification.Companion.ONGOING_NOTIFICATION_ID
|
import ch.dissem.apps.abit.notification.ProofOfWorkNotification.Companion.ONGOING_NOTIFICATION_ID
|
||||||
|
@ -2,7 +2,6 @@ package ch.dissem.apps.abit.service
|
|||||||
|
|
||||||
import android.app.job.JobParameters
|
import android.app.job.JobParameters
|
||||||
import android.app.job.JobService
|
import android.app.job.JobService
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.annotation.RequiresApi
|
import android.support.annotation.RequiresApi
|
||||||
import ch.dissem.apps.abit.util.NetworkUtils
|
import ch.dissem.apps.abit.util.NetworkUtils
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a Service that returns an IBinder for the
|
* Define a Service that returns an IBinder for the
|
||||||
|
@ -44,7 +44,7 @@ import java.io.ByteArrayOutputStream
|
|||||||
object Drawables {
|
object Drawables {
|
||||||
private val LOG = LoggerFactory.getLogger(Drawables::class.java)
|
private val LOG = LoggerFactory.getLogger(Drawables::class.java)
|
||||||
|
|
||||||
private val QR_CODE_SIZE = 350
|
private const val QR_CODE_SIZE = 350
|
||||||
|
|
||||||
fun addIcon(ctx: Context, menu: Menu, menuItem: Int, icon: IIcon): MenuItem {
|
fun addIcon(ctx: Context, menu: Menu, menuItem: Int, icon: IIcon): MenuItem {
|
||||||
val item = menu.findItem(menuItem)
|
val item = menu.findItem(menuItem)
|
||||||
|
@ -25,7 +25,7 @@ class Observable<T>(value: T) {
|
|||||||
* To prevent memory leaks, the observer must be removed if it isn't used anymore.
|
* To prevent memory leaks, the observer must be removed if it isn't used anymore.
|
||||||
*/
|
*/
|
||||||
fun addObserver(key: Any, observer: (T) -> Unit) {
|
fun addObserver(key: Any, observer: (T) -> Unit) {
|
||||||
observers.put(key, observer)
|
observers[key] = observer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,7 @@ public final class PRNGFixes {
|
|||||||
private static final int VERSION_CODE_JELLY_BEAN = 16;
|
private static final int VERSION_CODE_JELLY_BEAN = 16;
|
||||||
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
|
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
|
||||||
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
||||||
getBuildFingerprintAndDeviceSerial();
|
getBuildFingerprintAndDeviceSerial();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hidden constructor to prevent instantiation.
|
* Hidden constructor to prevent instantiation.
|
||||||
@ -70,7 +70,7 @@ public final class PRNGFixes {
|
|||||||
*/
|
*/
|
||||||
private static void applyOpenSSLFix() throws SecurityException {
|
private static void applyOpenSSLFix() throws SecurityException {
|
||||||
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|
||||||
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
|
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
|
||||||
// No need to apply the fix
|
// No need to apply the fix
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -78,18 +78,18 @@ public final class PRNGFixes {
|
|||||||
try {
|
try {
|
||||||
// Mix in the device- and invocation-specific seed.
|
// Mix in the device- and invocation-specific seed.
|
||||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
.getMethod("RAND_seed", byte[].class)
|
.getMethod("RAND_seed", byte[].class)
|
||||||
.invoke(null, (Object) generateSeed());
|
.invoke(null, (Object) generateSeed());
|
||||||
|
|
||||||
// Mix output of Linux PRNG into OpenSSL's PRNG
|
// Mix output of Linux PRNG into OpenSSL's PRNG
|
||||||
int bytesRead = (Integer) Class.forName(
|
int bytesRead = (Integer) Class.forName(
|
||||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
.getMethod("RAND_load_file", String.class, long.class)
|
.getMethod("RAND_load_file", String.class, long.class)
|
||||||
.invoke(null, "/dev/urandom", 1024);
|
.invoke(null, "/dev/urandom", 1024);
|
||||||
if (bytesRead != 1024) {
|
if (bytesRead != 1024) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"Unexpected number of bytes read from Linux PRNG: "
|
"Unexpected number of bytes read from Linux PRNG: "
|
||||||
+ bytesRead);
|
+ bytesRead);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
||||||
@ -104,7 +104,7 @@ public final class PRNGFixes {
|
|||||||
* @throws SecurityException if the fix is needed but could not be applied.
|
* @throws SecurityException if the fix is needed but could not be applied.
|
||||||
*/
|
*/
|
||||||
private static void installLinuxPRNGSecureRandom()
|
private static void installLinuxPRNGSecureRandom()
|
||||||
throws SecurityException {
|
throws SecurityException {
|
||||||
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
|
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
|
||||||
// No need to apply the fix
|
// No need to apply the fix
|
||||||
return;
|
return;
|
||||||
@ -113,11 +113,11 @@ public final class PRNGFixes {
|
|||||||
// Install a Linux PRNG-based SecureRandom implementation as the
|
// Install a Linux PRNG-based SecureRandom implementation as the
|
||||||
// default, if not yet installed.
|
// default, if not yet installed.
|
||||||
Provider[] secureRandomProviders =
|
Provider[] secureRandomProviders =
|
||||||
Security.getProviders("SecureRandom.SHA1PRNG");
|
Security.getProviders("SecureRandom.SHA1PRNG");
|
||||||
if ((secureRandomProviders == null)
|
if ((secureRandomProviders == null)
|
||||||
|| (secureRandomProviders.length < 1)
|
|| (secureRandomProviders.length < 1)
|
||||||
|| (!LinuxPRNGSecureRandomProvider.class.equals(
|
|| (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
secureRandomProviders[0].getClass()))) {
|
secureRandomProviders[0].getClass()))) {
|
||||||
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,10 +126,10 @@ public final class PRNGFixes {
|
|||||||
// by the Linux PRNG-based SecureRandom implementation.
|
// by the Linux PRNG-based SecureRandom implementation.
|
||||||
SecureRandom rng1 = new SecureRandom();
|
SecureRandom rng1 = new SecureRandom();
|
||||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
rng1.getProvider().getClass())) {
|
rng1.getProvider().getClass())) {
|
||||||
throw new SecurityException(
|
throw new SecurityException(
|
||||||
"new SecureRandom() backed by wrong Provider: "
|
"new SecureRandom() backed by wrong Provider: "
|
||||||
+ rng1.getProvider().getClass());
|
+ rng1.getProvider().getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
SecureRandom rng2;
|
SecureRandom rng2;
|
||||||
@ -139,10 +139,10 @@ public final class PRNGFixes {
|
|||||||
throw new SecurityException("SHA1PRNG not available", e);
|
throw new SecurityException("SHA1PRNG not available", e);
|
||||||
}
|
}
|
||||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
rng2.getProvider().getClass())) {
|
rng2.getProvider().getClass())) {
|
||||||
throw new SecurityException(
|
throw new SecurityException(
|
||||||
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
||||||
+ " Provider: " + rng2.getProvider().getClass());
|
+ " Provider: " + rng2.getProvider().getClass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,11 +152,11 @@ public final class PRNGFixes {
|
|||||||
*/
|
*/
|
||||||
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
||||||
|
|
||||||
public LinuxPRNGSecureRandomProvider() {
|
LinuxPRNGSecureRandomProvider() {
|
||||||
super("LinuxPRNG",
|
super("LinuxPRNG",
|
||||||
1.0,
|
1.0,
|
||||||
"A Linux-specific random number provider that uses"
|
"A Linux-specific random number provider that uses"
|
||||||
+ " /dev/urandom");
|
+ " /dev/urandom");
|
||||||
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
||||||
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
||||||
// prevent them from getting the default implementation whose output
|
// prevent them from getting the default implementation whose output
|
||||||
@ -225,7 +225,7 @@ public final class PRNGFixes {
|
|||||||
// On a small fraction of devices /dev/urandom is not writable.
|
// On a small fraction of devices /dev/urandom is not writable.
|
||||||
// Log and ignore.
|
// Log and ignore.
|
||||||
Log.w(PRNGFixes.class.getSimpleName(),
|
Log.w(PRNGFixes.class.getSimpleName(),
|
||||||
"Failed to mix seed into " + URANDOM_FILE);
|
"Failed to mix seed into " + URANDOM_FILE);
|
||||||
} finally {
|
} finally {
|
||||||
mSeeded = true;
|
mSeeded = true;
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ public final class PRNGFixes {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new SecurityException(
|
throw new SecurityException(
|
||||||
"Failed to read from " + URANDOM_FILE, e);
|
"Failed to read from " + URANDOM_FILE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,10 +269,10 @@ public final class PRNGFixes {
|
|||||||
// output being pulled into this process prematurely.
|
// output being pulled into this process prematurely.
|
||||||
try {
|
try {
|
||||||
sUrandomIn = new DataInputStream(
|
sUrandomIn = new DataInputStream(
|
||||||
new FileInputStream(URANDOM_FILE));
|
new FileInputStream(URANDOM_FILE));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new SecurityException("Failed to open "
|
throw new SecurityException("Failed to open "
|
||||||
+ URANDOM_FILE + " for reading", e);
|
+ URANDOM_FILE + " for reading", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sUrandomIn;
|
return sUrandomIn;
|
||||||
@ -297,7 +297,7 @@ public final class PRNGFixes {
|
|||||||
try {
|
try {
|
||||||
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
||||||
DataOutputStream seedBufferOut =
|
DataOutputStream seedBufferOut =
|
||||||
new DataOutputStream(seedBuffer);
|
new DataOutputStream(seedBuffer);
|
||||||
seedBufferOut.writeLong(System.currentTimeMillis());
|
seedBufferOut.writeLong(System.currentTimeMillis());
|
||||||
seedBufferOut.writeLong(System.nanoTime());
|
seedBufferOut.writeLong(System.nanoTime());
|
||||||
seedBufferOut.writeInt(Process.myPid());
|
seedBufferOut.writeInt(Process.myPid());
|
||||||
|
@ -37,11 +37,11 @@
|
|||||||
android:layout_alignTop="@+id/avatar"
|
android:layout_alignTop="@+id/avatar"
|
||||||
android:layout_toEndOf="@+id/avatar"
|
android:layout_toEndOf="@+id/avatar"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="1"
|
|
||||||
android:paddingBottom="0dp"
|
android:paddingBottom="0dp"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
android:paddingTop="0dp"
|
android:paddingTop="0dp"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="Name" />
|
tools:text="Name" />
|
||||||
@ -53,9 +53,9 @@
|
|||||||
android:layout_alignBottom="@+id/avatar"
|
android:layout_alignBottom="@+id/avatar"
|
||||||
android:layout_toEndOf="@+id/avatar"
|
android:layout_toEndOf="@+id/avatar"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:lines="1"
|
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
tools:text="BM-2cW0000000000000000000000000000000" />
|
tools:text="BM-2cW0000000000000000000000000000000" />
|
||||||
|
|
||||||
|
47
app/src/main/res/layout/contact_row_slim.xml
Normal file
47
app/src/main/res/layout/contact_row_slim.xml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright 2015 Christian Basler
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
android:src="@color/colorAccent"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toEndOf="@+id/avatar"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="1"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
tools:text="Name" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Copyright 2016 Christian Basler
|
~ Copyright 2016 Christian Basler
|
||||||
~
|
~
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -15,8 +14,7 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -34,13 +32,13 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:layout_constraintLeft_creator="1"
|
tools:layout_constraintLeft_creator="1"
|
||||||
tools:layout_constraintTop_creator="1"/>
|
tools:layout_constraintTop_creator="1" />
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<android.support.design.widget.TextInputLayout
|
||||||
android:id="@+id/label_wrapper"
|
android:id="@+id/label_wrapper"
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/description">
|
app:layout_constraintTop_toBottomOf="@id/description">
|
||||||
|
|
||||||
@ -48,7 +46,7 @@
|
|||||||
android:id="@+id/label"
|
android:id="@+id/label"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/label"/>
|
android:hint="@string/label" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
@ -64,7 +62,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/passphrase"
|
android:hint="@string/passphrase"
|
||||||
android:inputType="textMultiLine"/>
|
android:inputType="textMultiLine" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
@ -82,7 +80,8 @@
|
|||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:hint="@string/number_of_identities"
|
android:hint="@string/number_of_identities"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:text="1"/>
|
android:text="1"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
@ -92,7 +91,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/shorter"
|
android:text="@string/shorter"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/number_of_identities_wrapper"/>
|
app:layout_constraintTop_toBottomOf="@id/number_of_identities_wrapper" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/ok"
|
android:id="@+id/ok"
|
||||||
@ -103,7 +102,7 @@
|
|||||||
android:text="@string/ok"
|
android:text="@string/ok"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/shorter"/>
|
app:layout_constraintTop_toBottomOf="@id/shorter" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/dismiss"
|
android:id="@+id/dismiss"
|
||||||
@ -113,6 +112,6 @@
|
|||||||
android:text="@string/cancel"
|
android:text="@string/cancel"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/ok"
|
app:layout_constraintBottom_toBottomOf="@id/ok"
|
||||||
app:layout_constraintRight_toLeftOf="@id/ok"/>
|
app:layout_constraintRight_toLeftOf="@id/ok" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
@ -1,46 +1,68 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:orientation="vertical">
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingTop="4dp">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/recipient_input"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/to"
|
|
||||||
android:inputType="textNoSuggestions"
|
|
||||||
android:maxLines="1"/>
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/subject_input"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/subject"
|
|
||||||
android:inputType="textEmailSubject"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/body_input"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:orientation="vertical">
|
||||||
android:gravity="start|top"
|
|
||||||
android:hint="@string/compose_body_hint"
|
|
||||||
android:inputType="textMultiLine|textCapSentences"
|
|
||||||
android:scrollbars="vertical"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:labelFor="@id/sender_input"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:text="@string/from"
|
||||||
|
android:textColor="#9b9b9b"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/sender_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="4dp">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:id="@+id/recipient_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/to"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/subject_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/subject"
|
||||||
|
android:inputType="textEmailSubject"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/body_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="start|top"
|
||||||
|
android:hint="@string/compose_body_hint"
|
||||||
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
|
android:scrollbars="none"
|
||||||
|
tools:ignore="InefficientWeight" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/bg_item_normal_state"
|
android:background="@drawable/bg_item_normal_state"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:foreground="?attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Copyright 2016 Christian Basler
|
~ Copyright 2016 Christian Basler
|
||||||
~
|
~
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -16,9 +15,9 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp">
|
android:layout_height="64dp">
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/checkbox"
|
android:id="@+id/checkbox"
|
||||||
@ -31,21 +30,22 @@
|
|||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
tools:text="Name"/>
|
tools:text="Name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/address"
|
android:id="@+id/address"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:lines="1"
|
|
||||||
android:paddingBottom="8dp"
|
android:paddingBottom="8dp"
|
||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingStart="48dp"
|
android:paddingStart="48dp"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
tools:text="BM-2cW0000000000000000000000000000000"/>
|
tools:text="BM-2cW0000000000000000000000000000000" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -127,4 +127,5 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu
|
|||||||
<string name="import_data">Import</string>
|
<string name="import_data">Import</string>
|
||||||
<string name="import_data_summary">Nachrichten und Kontakte importieren (aber keine Identitäten)</string>
|
<string name="import_data_summary">Nachrichten und Kontakte importieren (aber keine Identitäten)</string>
|
||||||
<string name="select_encoding">Kodierung auswählen</string>
|
<string name="select_encoding">Kodierung auswählen</string>
|
||||||
|
<string name="from">Von</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -126,4 +126,5 @@ As an alternative you could configure a trusted node in the settings, but as of
|
|||||||
<string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string>
|
<string name="request_acknowledgements_summary">Acknowledges allow making sure a message was received, but require additional time to send</string>
|
||||||
<string name="got_it">Got it</string>
|
<string name="got_it">Got it</string>
|
||||||
<string name="select_encoding">Select encoding</string>
|
<string name="select_encoding">Select encoding</string>
|
||||||
|
<string name="from">From</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<item name="android:activatedBackgroundIndicator">@drawable/bg_item_activated</item>
|
<item name="android:activatedBackgroundIndicator">@drawable/bg_item_activated</item>
|
||||||
<item name="android:textColor">@color/colorPrimaryText</item>
|
<item name="android:textColor">@color/colorPrimaryText</item>
|
||||||
<item name="android:textColorSecondary">@color/colorSecondaryText</item>
|
<item name="android:textColorSecondary">@color/colorSecondaryText</item>
|
||||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="CustomShowcaseTheme" parent="ShowcaseView">
|
<style name="CustomShowcaseTheme" parent="ShowcaseView">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths>
|
||||||
<files-path name="exports" path="exports/"/>
|
<files-path name="exports" path="exports/"/>
|
||||||
</paths>
|
</paths>
|
||||||
|
@ -1,64 +1,58 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<SwitchPreferenceCompat
|
<android.support.v7.preference.SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="wifi_only"
|
android:key="wifi_only"
|
||||||
android:summary="@string/wifi_only_summary"
|
android:summary="@string/wifi_only_summary"
|
||||||
android:title="@string/wifi_only"/>
|
android:title="@string/wifi_only" />
|
||||||
<SwitchPreferenceCompat
|
<android.support.v7.preference.SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="request_acknowledgements"
|
android:key="request_acknowledgements"
|
||||||
android:summary="@string/request_acknowledgements_summary"
|
android:summary="@string/request_acknowledgements_summary"
|
||||||
android:title="@string/request_acknowledgements"/>
|
android:title="@string/request_acknowledgements" />
|
||||||
<EditTextPreference
|
<android.support.v7.preference.EditTextPreference
|
||||||
android:inputType="textUri"
|
android:inputType="textUri"
|
||||||
android:key="trusted_node"
|
android:key="trusted_node"
|
||||||
android:summary="@string/trusted_node_summary"
|
android:summary="@string/trusted_node_summary"
|
||||||
android:title="@string/trusted_node"/>
|
android:title="@string/trusted_node" />
|
||||||
<EditTextPreference
|
<android.support.v7.preference.EditTextPreference
|
||||||
android:defaultValue="120"
|
android:defaultValue="120"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:key="sync_timeout"
|
android:key="sync_timeout"
|
||||||
android:summary="@string/sync_timeout_summary"
|
android:summary="@string/sync_timeout_summary"
|
||||||
android:title="@string/sync_timeout"/>
|
android:title="@string/sync_timeout" />
|
||||||
<SwitchPreferenceCompat
|
<android.support.v7.preference.SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:dependency="trusted_node"
|
android:dependency="trusted_node"
|
||||||
android:key="server_pow"
|
android:key="server_pow"
|
||||||
android:summary="@string/server_pow_summary"
|
android:summary="@string/server_pow_summary"
|
||||||
android:title="@string/server_pow"
|
android:title="@string/server_pow" />
|
||||||
/>
|
<android.support.v7.preference.Preference
|
||||||
<Preference
|
|
||||||
android:key="about"
|
android:key="about"
|
||||||
android:summary="@string/about_summary"
|
android:summary="@string/about_summary"
|
||||||
android:title="@string/about"
|
android:title="@string/about" />
|
||||||
/>
|
<android.support.v7.preference.Preference
|
||||||
<Preference
|
|
||||||
android:key="help_out"
|
android:key="help_out"
|
||||||
android:summary="@string/help_out_summary"
|
android:summary="@string/help_out_summary"
|
||||||
android:title="@string/help_out">
|
android:title="@string/help_out">
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:data="@string/help_out_link"/>
|
android:data="@string/help_out_link" />
|
||||||
</Preference>
|
</android.support.v7.preference.Preference>
|
||||||
<Preference
|
<android.support.v7.preference.Preference
|
||||||
android:key="cleanup"
|
android:key="cleanup"
|
||||||
android:title="@string/cleanup"
|
|
||||||
android:summary="@string/cleanup_summary"
|
android:summary="@string/cleanup_summary"
|
||||||
/>
|
android:title="@string/cleanup" />
|
||||||
<Preference
|
<android.support.v7.preference.Preference
|
||||||
android:key="export"
|
android:key="export"
|
||||||
android:title="@string/export_data"
|
|
||||||
android:summary="@string/export_data_summary"
|
android:summary="@string/export_data_summary"
|
||||||
/>
|
android:title="@string/export_data" />
|
||||||
<Preference
|
<android.support.v7.preference.Preference
|
||||||
android:key="import"
|
android:key="import"
|
||||||
android:title="@string/import_data"
|
|
||||||
android:summary="@string/import_data_summary"
|
android:summary="@string/import_data_summary"
|
||||||
/>
|
android:title="@string/import_data" />
|
||||||
<Preference
|
<android.support.v7.preference.Preference
|
||||||
android:key="status"
|
android:key="status"
|
||||||
android:summary="@string/status_summary"
|
android:summary="@string/status_summary"
|
||||||
android:title="@string/status"
|
android:title="@string/status" />
|
||||||
/>
|
</android.support.v7.preference.PreferenceScreen>
|
||||||
</PreferenceScreen>
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.repository
|
package ch.dissem.bitmessage.repository
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import ch.dissem.apps.abit.BuildConfig
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository
|
import ch.dissem.apps.abit.repository.AndroidAddressRepository
|
||||||
import ch.dissem.apps.abit.repository.SqlHelper
|
import ch.dissem.apps.abit.repository.SqlHelper
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
@ -32,7 +32,7 @@ import org.robolectric.RuntimeEnvironment
|
|||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [LOLLIPOP], packageName = "ch.dissem.apps.abit")
|
||||||
class AndroidAddressRepositoryTest : TestBase() {
|
class AndroidAddressRepositoryTest : TestBase() {
|
||||||
private val contactA = "BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"
|
private val contactA = "BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"
|
||||||
private val contactB = "BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj"
|
private val contactB = "BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.repository
|
package ch.dissem.bitmessage.repository
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import ch.dissem.apps.abit.BuildConfig
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
import ch.dissem.apps.abit.repository.AndroidInventory
|
import ch.dissem.apps.abit.repository.AndroidInventory
|
||||||
import ch.dissem.apps.abit.repository.SqlHelper
|
import ch.dissem.apps.abit.repository.SqlHelper
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||||
@ -40,7 +40,7 @@ import org.robolectric.annotation.Config
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [LOLLIPOP], packageName = "ch.dissem.apps.abit")
|
||||||
class AndroidInventoryTest : TestBase() {
|
class AndroidInventoryTest : TestBase() {
|
||||||
private lateinit var inventory: Inventory
|
private lateinit var inventory: Inventory
|
||||||
|
|
||||||
@ -135,11 +135,12 @@ class AndroidInventoryTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getObjectMessage(stream: Long, TTL: Long, payload: ObjectPayload) = ObjectMessage(
|
private fun getObjectMessage(stream: Long, TTL: Long, payload: ObjectPayload) = 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"))
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.repository
|
package ch.dissem.bitmessage.repository
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
import ch.dissem.apps.abit.repository.AndroidLabelRepository
|
import ch.dissem.apps.abit.repository.AndroidLabelRepository
|
||||||
import ch.dissem.apps.abit.repository.SqlHelper
|
import ch.dissem.apps.abit.repository.SqlHelper
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||||
@ -30,7 +31,7 @@ import org.robolectric.RuntimeEnvironment
|
|||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [LOLLIPOP], packageName = "ch.dissem.apps.abit")
|
||||||
class AndroidLabelRepositoryTest : TestBase() {
|
class AndroidLabelRepositoryTest : TestBase() {
|
||||||
|
|
||||||
private lateinit var repo: LabelRepository
|
private lateinit var repo: LabelRepository
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.repository
|
package ch.dissem.bitmessage.repository
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository
|
import ch.dissem.apps.abit.repository.AndroidAddressRepository
|
||||||
import ch.dissem.apps.abit.repository.AndroidLabelRepository
|
import ch.dissem.apps.abit.repository.AndroidLabelRepository
|
||||||
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
import ch.dissem.apps.abit.repository.AndroidMessageRepository
|
||||||
@ -31,7 +31,6 @@ import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding
|
|||||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey
|
import ch.dissem.bitmessage.entity.valueobject.PrivateKey
|
||||||
import ch.dissem.bitmessage.entity.valueobject.extended.Message
|
import ch.dissem.bitmessage.entity.valueobject.extended.Message
|
||||||
import ch.dissem.bitmessage.ports.LabelRepository
|
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository
|
import ch.dissem.bitmessage.ports.MessageRepository
|
||||||
import ch.dissem.bitmessage.utils.UnixTime
|
import ch.dissem.bitmessage.utils.UnixTime
|
||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
@ -48,7 +47,7 @@ import org.robolectric.annotation.Config
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [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
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.repository
|
package ch.dissem.bitmessage.repository
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import ch.dissem.apps.abit.BuildConfig
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
import ch.dissem.apps.abit.repository.AndroidNodeRegistry
|
import ch.dissem.apps.abit.repository.AndroidNodeRegistry
|
||||||
import ch.dissem.apps.abit.repository.SqlHelper
|
import ch.dissem.apps.abit.repository.SqlHelper
|
||||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
|
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
|
||||||
@ -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(sdk = intArrayOf(Build.VERSION_CODES.LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [LOLLIPOP], packageName = "ch.dissem.apps.abit")
|
||||||
class AndroidNodeRegistryTest : TestBase() {
|
class AndroidNodeRegistryTest : TestBase() {
|
||||||
private lateinit var registry: NodeRegistry
|
private lateinit var registry: NodeRegistry
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ import kotlin.properties.Delegates
|
|||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = intArrayOf(LOLLIPOP), packageName = "ch.dissem.apps.abit")
|
@Config(sdk = [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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.2.20'
|
ext.kotlin_version = '1.2.21'
|
||||||
ext.anko_version = '0.10.4'
|
ext.anko_version = '0.10.4'
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
Loading…
Reference in New Issue
Block a user