Switch to API level 27 and updated libraries
This commit is contained in:
		| @@ -40,7 +40,7 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> { | ||||
|     private var activatedPosition = ListView.INVALID_POSITION | ||||
|     private var activateOnItemClick: Boolean = false | ||||
|  | ||||
|     override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         // Restore the previously serialized activated item position. | ||||
| @@ -92,9 +92,9 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onSaveInstanceState(outState: Bundle?) { | ||||
|     override fun onSaveInstanceState(outState: Bundle) { | ||||
|         super.onSaveInstanceState(outState) | ||||
|         if (outState != null && activatedPosition != ListView.INVALID_POSITION) { | ||||
|         if (activatedPosition != ListView.INVALID_POSITION) { | ||||
|             // Serialize and persist the activated item position. | ||||
|             outState.putInt(STATE_ACTIVATED_POSITION, activatedPosition) | ||||
|         } | ||||
| @@ -136,9 +136,7 @@ abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> { | ||||
|      * nothing. Used only when this fragment is not attached to an activity. | ||||
|      */ | ||||
|     internal object DummyCallback : ListSelectionListener<Any> { | ||||
|         override fun onItemSelected(item: Any) { | ||||
|             // NO OP | ||||
|         } | ||||
|         override fun onItemSelected(item: Any) = Unit // NO OP | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|   | ||||
| @@ -49,8 +49,10 @@ class AddressDetailFragment : Fragment() { | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|  | ||||
|         if (arguments.containsKey(ARG_ITEM)) { | ||||
|             item = arguments.getSerializable(ARG_ITEM) as BitmessageAddress | ||||
|         arguments?.let { arguments -> | ||||
|             if (arguments.containsKey(ARG_ITEM)) { | ||||
|                 item = arguments.getSerializable(ARG_ITEM) as BitmessageAddress | ||||
|             } | ||||
|         } | ||||
|         setHasOptionsMenu(true) | ||||
|     } | ||||
| @@ -58,7 +60,7 @@ class AddressDetailFragment : Fragment() { | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||||
|         inflater.inflate(R.menu.address, menu) | ||||
|  | ||||
|         val ctx = activity | ||||
|         val ctx = activity!! | ||||
|         Drawables.addIcon(ctx, menu, R.id.write_message, GoogleMaterial.Icon.gmd_mail) | ||||
|         Drawables.addIcon(ctx, menu, R.id.share, GoogleMaterial.Icon.gmd_share) | ||||
|         Drawables.addIcon(ctx, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete) | ||||
| @@ -69,7 +71,7 @@ class AddressDetailFragment : Fragment() { | ||||
|  | ||||
|     override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { | ||||
|         val item = item ?: return false | ||||
|         val ctx = activity | ||||
|         val ctx = activity ?: return false | ||||
|         when (menuItem.itemId) { | ||||
|             R.id.write_message -> { | ||||
|                 val identity = Singleton.getIdentity(ctx) | ||||
| @@ -89,35 +91,35 @@ class AddressDetailFragment : Fragment() { | ||||
|                 else | ||||
|                     R.string.delete_contact_warning | ||||
|                 AlertDialog.Builder(ctx) | ||||
|                         .setMessage(warning) | ||||
|                         .setPositiveButton(android.R.string.yes) { _, _ -> | ||||
|                             Singleton.getAddressRepository(ctx).remove(item) | ||||
|                             val mainActivity = MainActivity.getInstance() | ||||
|                             if (item.privateKey != null && mainActivity != null) { | ||||
|                                 mainActivity.removeIdentityEntry(item) | ||||
|                             } | ||||
|                             this.item = null | ||||
|                             ctx.onBackPressed() | ||||
|                     .setMessage(warning) | ||||
|                     .setPositiveButton(android.R.string.yes) { _, _ -> | ||||
|                         Singleton.getAddressRepository(ctx).remove(item) | ||||
|                         val mainActivity = MainActivity.getInstance() | ||||
|                         if (item.privateKey != null && mainActivity != null) { | ||||
|                             mainActivity.removeIdentityEntry(item) | ||||
|                         } | ||||
|                         .setNegativeButton(android.R.string.no, null) | ||||
|                         .show() | ||||
|                         this.item = null | ||||
|                         ctx.onBackPressed() | ||||
|                     } | ||||
|                     .setNegativeButton(android.R.string.no, null) | ||||
|                     .show() | ||||
|                 return true | ||||
|             } | ||||
|             R.id.export -> { | ||||
|                 AlertDialog.Builder(ctx) | ||||
|                         .setMessage(R.string.confirm_export) | ||||
|                         .setPositiveButton(android.R.string.yes) { _, _ -> | ||||
|                             val shareIntent = Intent(Intent.ACTION_SEND) | ||||
|                             shareIntent.type = "text/plain" | ||||
|                             shareIntent.putExtra(Intent.EXTRA_TITLE, item.toString() + EXPORT_POSTFIX) | ||||
|                             val exporter = WifExporter(Singleton | ||||
|                                     .getBitmessageContext(ctx)) | ||||
|                             exporter.addIdentity(item) | ||||
|                             shareIntent.putExtra(Intent.EXTRA_TEXT, exporter.toString()) | ||||
|                             startActivity(Intent.createChooser(shareIntent, null)) | ||||
|                         } | ||||
|                         .setNegativeButton(android.R.string.no, null) | ||||
|                         .show() | ||||
|                     .setMessage(R.string.confirm_export) | ||||
|                     .setPositiveButton(android.R.string.yes) { _, _ -> | ||||
|                         val shareIntent = Intent(Intent.ACTION_SEND) | ||||
|                         shareIntent.type = "text/plain" | ||||
|                         shareIntent.putExtra(Intent.EXTRA_TITLE, item.toString() + EXPORT_POSTFIX) | ||||
|                         val exporter = WifExporter(Singleton | ||||
|                             .getBitmessageContext(ctx)) | ||||
|                         exporter.addIdentity(item) | ||||
|                         shareIntent.putExtra(Intent.EXTRA_TEXT, exporter.toString()) | ||||
|                         startActivity(Intent.createChooser(shareIntent, null)) | ||||
|                     } | ||||
|                     .setNegativeButton(android.R.string.no, null) | ||||
|                     .show() | ||||
|                 return true | ||||
|             } | ||||
|             R.id.share -> { | ||||
| @@ -132,30 +134,27 @@ class AddressDetailFragment : Fragment() { | ||||
|     } | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View | ||||
|             = inflater.inflate(R.layout.fragment_address_detail, container, false) | ||||
|         = inflater.inflate(R.layout.fragment_address_detail, container, false) | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         // Show the dummy content as text in a TextView. | ||||
|         item?.let { item -> | ||||
|             val activity = activity | ||||
|             when { | ||||
|                 item.isChan -> activity.setTitle(R.string.title_chan_detail) | ||||
|                 item.privateKey != null -> activity.setTitle(R.string.title_identity_detail) | ||||
|                 item.isSubscribed -> activity.setTitle(R.string.title_subscription_detail) | ||||
|                 else -> activity.setTitle(R.string.title_contact_detail) | ||||
|             activity?.let { activity -> | ||||
|                 when { | ||||
|                     item.isChan -> activity.setTitle(R.string.title_chan_detail) | ||||
|                     item.privateKey != null -> activity.setTitle(R.string.title_identity_detail) | ||||
|                     item.isSubscribed -> activity.setTitle(R.string.title_subscription_detail) | ||||
|                     else -> activity.setTitle(R.string.title_contact_detail) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             avatar.setImageDrawable(Identicon(item)) | ||||
|             name.setText(item.toString()) | ||||
|             name.addTextChangedListener(object : TextWatcher { | ||||
|                 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { | ||||
|                     // Nothing to do | ||||
|                 } | ||||
|                 override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit // Nothing to do | ||||
|  | ||||
|                 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { | ||||
|                     // Nothing to do | ||||
|                 } | ||||
|                 override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) = Unit // Nothing to do | ||||
|  | ||||
|                 override fun afterTextChanged(s: Editable) { | ||||
|                     item.alias = s.toString() | ||||
| @@ -185,7 +184,7 @@ class AddressDetailFragment : Fragment() { | ||||
|  | ||||
|     override fun onPause() { | ||||
|         item?.let { item -> | ||||
|             Singleton.getAddressRepository(context).save(item) | ||||
|             Singleton.getAddressRepository(context!!).save(item) | ||||
|             if (item.privateKey != null) { | ||||
|                 MainActivity.getInstance()?.updateIdentityEntry(item) | ||||
|             } | ||||
|   | ||||
| @@ -45,10 +45,10 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() | ||||
|         super.onCreate(savedInstanceState) | ||||
|  | ||||
|         adapter = object : ArrayAdapter<BitmessageAddress>( | ||||
|                 activity, | ||||
|                 R.layout.subscription_row, | ||||
|                 R.id.name, | ||||
|                 LinkedList()) { | ||||
|             activity, | ||||
|             R.layout.subscription_row, | ||||
|             R.id.name, | ||||
|             LinkedList()) { | ||||
|             override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { | ||||
|                 val result: View | ||||
|                 val v: ViewHolder | ||||
| @@ -56,11 +56,11 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() | ||||
|                     val inflater = LayoutInflater.from(context) | ||||
|                     val view = inflater.inflate(R.layout.subscription_row, parent, false) | ||||
|                     v = ViewHolder( | ||||
|                             ctx = context, | ||||
|                             avatar = view.findViewById(R.id.avatar) as ImageView, | ||||
|                             name = view.findViewById(R.id.name) as TextView, | ||||
|                             streamNumber = view.findViewById(R.id.stream_number) as TextView, | ||||
|                             subscribed = view.findViewById(R.id.subscribed) | ||||
|                         ctx = context, | ||||
|                         avatar = view.findViewById(R.id.avatar), | ||||
|                         name = view.findViewById(R.id.name), | ||||
|                         streamNumber = view.findViewById(R.id.stream_number), | ||||
|                         subscribed = view.findViewById(R.id.subscribed) | ||||
|                     ) | ||||
|                     view.tag = v | ||||
|                     result = view | ||||
| @@ -89,12 +89,14 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() | ||||
|  | ||||
|     fun updateList() { | ||||
|         adapter.clear() | ||||
|         val addressRepo = Singleton.getAddressRepository(context) | ||||
|         doAsync { | ||||
|             addressRepo.getContactIds() | ||||
|         context?.let { context -> | ||||
|             val addressRepo = Singleton.getAddressRepository(context) | ||||
|             doAsync { | ||||
|                 addressRepo.getContactIds() | ||||
|                     .map { addressRepo.getAddress(it) } | ||||
|                     .forEach { address -> uiThread { adapter.add(address) } } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -104,23 +106,23 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() | ||||
|         menu.add(R.string.scan_qr_code).setIcon(R.drawable.ic_action_qr_code) | ||||
|         menu.add(R.string.create_contact).setIcon(R.drawable.ic_action_create_contact) | ||||
|         FabUtils.initFab(activity, R.drawable.ic_action_add_contact, menu) | ||||
|                 .addOnMenuItemClickListener { _, _, itemId -> | ||||
|                     when (itemId) { | ||||
|                         1 -> IntentIntegrator.forSupportFragment(this@AddressListFragment) | ||||
|                                 .setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) | ||||
|                                 .initiateScan() | ||||
|                         2 -> { | ||||
|                             val intent = Intent(getActivity(), CreateAddressActivity::class.java) | ||||
|                             startActivity(intent) | ||||
|                         } | ||||
|                         else -> { | ||||
|                         } | ||||
|             .addOnMenuItemClickListener { _, _, itemId -> | ||||
|                 when (itemId) { | ||||
|                     1 -> IntentIntegrator.forSupportFragment(this@AddressListFragment) | ||||
|                         .setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES) | ||||
|                         .initiateScan() | ||||
|                     2 -> { | ||||
|                         val intent = Intent(getActivity(), CreateAddressActivity::class.java) | ||||
|                         startActivity(intent) | ||||
|                     } | ||||
|                     else -> { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = | ||||
|             inflater.inflate(R.layout.fragment_address_list, container, false) | ||||
|         inflater.inflate(R.layout.fragment_address_list, container, false) | ||||
|  | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         if (data != null && data.hasExtra("SCAN_RESULT")) { | ||||
| @@ -131,15 +133,13 @@ class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun updateList(label: Void) { | ||||
|         updateList() | ||||
|     } | ||||
|     override fun updateList(label: Void) = updateList() | ||||
|  | ||||
|     private data class ViewHolder( | ||||
|             val ctx: Context, | ||||
|             val avatar: ImageView, | ||||
|             val name: TextView, | ||||
|             val streamNumber: TextView, | ||||
|             val subscribed: View | ||||
|         val ctx: Context, | ||||
|         val avatar: ImageView, | ||||
|         val name: TextView, | ||||
|         val streamNumber: TextView, | ||||
|         val subscribed: View | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -47,9 +47,9 @@ class ComposeMessageActivity : AppCompatActivity() { | ||||
|         val fragment = ComposeMessageFragment() | ||||
|         fragment.arguments = intent.extras | ||||
|         supportFragmentManager | ||||
|                 .beginTransaction() | ||||
|                 .replace(R.id.content, fragment) | ||||
|                 .commit() | ||||
|             .beginTransaction() | ||||
|             .replace(R.id.content, fragment) | ||||
|             .commit() | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
| @@ -61,18 +61,19 @@ class ComposeMessageActivity : AppCompatActivity() { | ||||
|         const val EXTRA_ENCODING = "ch.dissem.abit.Message.ENCODING" | ||||
|         const val EXTRA_PARENT = "ch.dissem.abit.Message.PARENT" | ||||
|  | ||||
|         fun launchReplyTo(fragment: Fragment, item: Plaintext) { | ||||
|             fragment.startActivity(getReplyIntent(fragment.activity, item)) | ||||
|         } | ||||
|         fun launchReplyTo(fragment: Fragment, item: Plaintext) = | ||||
|             fragment.startActivity(getReplyIntent( | ||||
|                 ctx = fragment.activity ?: throw IllegalStateException("Fragment not attached to an activity"), | ||||
|                 item = item | ||||
|             )) | ||||
|  | ||||
|         fun launchReplyTo(activity: Activity, item: Plaintext) { | ||||
|         fun launchReplyTo(activity: Activity, item: Plaintext) = | ||||
|             activity.startActivity(getReplyIntent(activity, item)) | ||||
|         } | ||||
|  | ||||
|         private fun getReplyIntent(ctx: Context, item: Plaintext): Intent { | ||||
|             val replyIntent = Intent(ctx, ComposeMessageActivity::class.java) | ||||
|             val receivingIdentity = item.to | ||||
|             if (receivingIdentity?.isChan ?: false) { | ||||
|             if (receivingIdentity?.isChan == true) { | ||||
|                 // reply to chan, not to the sender of the message | ||||
|                 replyIntent.putExtra(EXTRA_RECIPIENT, receivingIdentity) | ||||
|                 // I hate when people send as chan, so it won't be the default behaviour. | ||||
| @@ -96,7 +97,7 @@ class ComposeMessageActivity : AppCompatActivity() { | ||||
|                 replyIntent.putExtra(EXTRA_SUBJECT, prefix + subject) | ||||
|             } | ||||
|             replyIntent.putExtra(EXTRA_CONTENT, | ||||
|                     "\n\n------------------------------------------------------\n" + item.text!!) | ||||
|                 "\n\n------------------------------------------------------\n" + item.text!!) | ||||
|             return replyIntent | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|         arguments?.let { arguments -> | ||||
|             var id = arguments.getSerializable(EXTRA_IDENTITY) as? BitmessageAddress | ||||
|             if (context != null && (id == null || id.privateKey == null)) { | ||||
|                 id = Singleton.getIdentity(context) | ||||
|                 id = Singleton.getIdentity(context!!) | ||||
|             } | ||||
|             if (id?.privateKey != null) { | ||||
|                 identity = id | ||||
| @@ -97,7 +97,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|         if (broadcast) { | ||||
|             recipient_input.visibility = View.GONE | ||||
|         } else { | ||||
|             val adapter = ContactAdapter(context) | ||||
|             val adapter = ContactAdapter(context!!) | ||||
|             recipient_input.setAdapter(adapter) | ||||
|             recipient_input.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> adapter.getItem(pos) } | ||||
|             recipient_input.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { | ||||
| @@ -105,9 +105,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|                     recipient = adapter.getItem(position) | ||||
|                 } | ||||
|  | ||||
|                 override fun onNothingSelected(parent: AdapterView<*>) { | ||||
|                     // leave current selection | ||||
|                 } | ||||
|                 override fun onNothingSelected(parent: AdapterView<*>) = Unit // leave current selection | ||||
|             } | ||||
|             recipient?.let { recipient_input.setText(it.toString()) } | ||||
|         } | ||||
| @@ -148,17 +146,16 @@ class ComposeMessageFragment : Fragment() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         if (requestCode == 0 && data != null && resultCode == RESULT_OK) { | ||||
|             encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding | ||||
|         } else { | ||||
|             super.onActivityResult(requestCode, resultCode, data) | ||||
|         } | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) = if (requestCode == 0 && data != null && resultCode == RESULT_OK) { | ||||
|         encoding = data.getSerializableExtra(EXTRA_ENCODING) as Plaintext.Encoding | ||||
|     } else { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|     } | ||||
|  | ||||
|     private fun send() { | ||||
|         val builder: Plaintext.Builder | ||||
|         val bmc = Singleton.getBitmessageContext(context) | ||||
|         val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity") | ||||
|         val bmc = Singleton.getBitmessageContext(ctx) | ||||
|         if (broadcast) { | ||||
|             builder = Plaintext.Builder(BROADCAST).from(identity) | ||||
|         } else { | ||||
| @@ -167,7 +164,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|                 try { | ||||
|                     recipient = BitmessageAddress(inputString) | ||||
|                 } catch (e: Exception) { | ||||
|                     val contacts = Singleton.getAddressRepository(context).getContacts() | ||||
|                     val contacts = Singleton.getAddressRepository(ctx).getContacts() | ||||
|                     for (contact in contacts) { | ||||
|                         if (inputString.equals(contact.alias, ignoreCase = true)) { | ||||
|                             recipient = contact | ||||
| @@ -186,7 +183,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|                     .from(identity) | ||||
|                     .to(recipient) | ||||
|         } | ||||
|         if (!Preferences.requestAcknowledgements(context)) { | ||||
|         if (!Preferences.requestAcknowledgements(ctx)) { | ||||
|             builder.preventAck() | ||||
|         } | ||||
|         when (encoding) { | ||||
| @@ -203,8 +200,8 @@ class ComposeMessageFragment : Fragment() { | ||||
|             ) | ||||
|             else -> { | ||||
|                 Toast.makeText( | ||||
|                         context, | ||||
|                         context.getString(R.string.error_unsupported_encoding, encoding), | ||||
|                         ctx, | ||||
|                         ctx.getString(R.string.error_unsupported_encoding, encoding), | ||||
|                         Toast.LENGTH_LONG | ||||
|                 ).show() | ||||
|                 builder.message( | ||||
| @@ -214,7 +211,7 @@ class ComposeMessageFragment : Fragment() { | ||||
|             } | ||||
|         } | ||||
|         bmc.send(builder.build()) | ||||
|         activity.finish() | ||||
|         ctx.finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -46,9 +46,9 @@ class CreateAddressActivity : AppCompatActivity() { | ||||
|         else | ||||
|             setContentView(R.layout.activity_create_bitmessage_address) | ||||
|  | ||||
|         val address = findViewById(R.id.address) as TextView | ||||
|         val label = findViewById(R.id.label) as EditText | ||||
|         val subscribe = findViewById(R.id.subscribe) as Switch | ||||
|         val address = findViewById<TextView>(R.id.address) | ||||
|         val label = findViewById<EditText>(R.id.label) | ||||
|         val subscribe = findViewById<Switch>(R.id.subscribe) | ||||
|  | ||||
|         if (uri != null) { | ||||
|             val addressText = getAddress(uri) | ||||
| @@ -70,12 +70,12 @@ class CreateAddressActivity : AppCompatActivity() { | ||||
|             address.text = addressText | ||||
|         } | ||||
|  | ||||
|         val cancel = findViewById(R.id.cancel) as Button | ||||
|         val cancel = findViewById<Button>(R.id.cancel) | ||||
|         cancel.setOnClickListener { | ||||
|             setResult(Activity.RESULT_CANCELED) | ||||
|             finish() | ||||
|         } | ||||
|         findViewById(R.id.do_import).setOnClickListener { onOK(address, label, subscribe) } | ||||
|         findViewById<Button>(R.id.do_import).setOnClickListener { onOK(address, label, subscribe) } | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ import android.support.v7.widget.RecyclerView | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.Button | ||||
|  | ||||
| import com.h6ah4i.android.widget.advrecyclerview.decoration.SimpleListDividerDecorator | ||||
|  | ||||
| @@ -38,7 +39,7 @@ class ImportIdentitiesFragment : Fragment() { | ||||
|     private lateinit var adapter: AddressSelectorAdapter | ||||
|     private lateinit var importer: WifImporter | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle): View = | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = | ||||
|             inflater.inflate(R.layout.fragment_import_select_identities, container, false) | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
| @@ -52,14 +53,14 @@ class ImportIdentitiesFragment : Fragment() { | ||||
|         val layoutManager = LinearLayoutManager(activity, | ||||
|                 LinearLayoutManager.VERTICAL, | ||||
|                 false) | ||||
|         val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView | ||||
|         val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view) | ||||
|         recyclerView.layoutManager = layoutManager | ||||
|         recyclerView.adapter = adapter | ||||
|  | ||||
|         recyclerView.addItemDecoration(SimpleListDividerDecorator( | ||||
|                 ContextCompat.getDrawable(activity, R.drawable.list_divider_h), true)) | ||||
|  | ||||
|         view.findViewById(R.id.finish).setOnClickListener { | ||||
|         view.findViewById<Button>(R.id.finish).setOnClickListener { | ||||
|             importer.importAll(adapter.selected) | ||||
|             val mainActivity = MainActivity.getInstance() | ||||
|             if (mainActivity != null) { | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class InputWifFragment : Fragment() { | ||||
|         setHasOptionsMenu(true) | ||||
|     } | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle): View = | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = | ||||
|             inflater.inflate(R.layout.fragment_import_input, container, false) | ||||
|  | ||||
|     override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { | ||||
| @@ -58,9 +58,8 @@ class InputWifFragment : Fragment() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) = | ||||
|         inflater.inflate(R.menu.import_input_data, menu) | ||||
|     } | ||||
|  | ||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { | ||||
|         val properties = DialogProperties() | ||||
|   | ||||
| @@ -99,7 +99,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> { | ||||
|     private lateinit var nodeSwitch: SwitchDrawerItem | ||||
|  | ||||
|     val floatingActionButton: FabSpeedDial? | ||||
|         get() = findViewById(R.id.fab) as FabSpeedDial? | ||||
|         get() = findViewById(R.id.fab) | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
| @@ -109,7 +109,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> { | ||||
|         setContentView(R.layout.activity_main) | ||||
|         fab.hide() | ||||
|  | ||||
|         val toolbar = findViewById(R.id.toolbar) as Toolbar | ||||
|         val toolbar = findViewById<Toolbar>(R.id.toolbar) | ||||
|         setSupportActionBar(toolbar) | ||||
|  | ||||
|         val listFragment = MessageListFragment() | ||||
| @@ -118,7 +118,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> { | ||||
|                 .replace(R.id.item_list, listFragment) | ||||
|                 .commit() | ||||
|  | ||||
|         if (findViewById(R.id.message_detail_container) != null) { | ||||
|         if (findViewById<View>(R.id.message_detail_container) != null) { | ||||
|             // The detail container view will be present only in the | ||||
|             // large-screen layouts (res/values-large and | ||||
|             // res/values-sw600dp). If this view is present, then the | ||||
| @@ -358,7 +358,7 @@ class MainActivity : AppCompatActivity(), ListSelectionListener<Serializable> { | ||||
|     } | ||||
|  | ||||
|     override fun onRestoreInstanceState(savedInstanceState: Bundle) { | ||||
|         selectedLabel = savedInstanceState.getSerializable("selectedLabel") as Label? | ||||
|         selectedLabel = savedInstanceState.getSerializable("selectedLabel") as? Label | ||||
|  | ||||
|         selectedLabel?.let { selectedLabel -> | ||||
|             drawer.getDrawerItem(selectedLabel)?.let { selectedItem -> | ||||
|   | ||||
| @@ -25,32 +25,23 @@ import android.support.v7.widget.GridLayoutManager | ||||
| import android.support.v7.widget.LinearLayoutManager | ||||
| import android.support.v7.widget.RecyclerView | ||||
| import android.text.util.Linkify | ||||
| import android.view.LayoutInflater | ||||
| import android.view.Menu | ||||
| import android.view.MenuInflater | ||||
| import android.view.MenuItem | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.text.util.Linkify.WEB_URLS | ||||
| import android.view.* | ||||
| import android.widget.ImageView | ||||
| import android.widget.TextView | ||||
|  | ||||
| import com.mikepenz.google_material_typeface_library.GoogleMaterial | ||||
| import com.mikepenz.iconics.view.IconicsImageView | ||||
|  | ||||
| import java.util.ArrayList | ||||
|  | ||||
| import ch.dissem.apps.abit.service.Singleton | ||||
| import ch.dissem.apps.abit.util.Assets | ||||
| import ch.dissem.apps.abit.util.Drawables | ||||
| import ch.dissem.apps.abit.util.Labels | ||||
| import ch.dissem.bitmessage.entity.Plaintext | ||||
| import ch.dissem.bitmessage.entity.valueobject.Label | ||||
|  | ||||
| import android.text.util.Linkify.WEB_URLS | ||||
| import ch.dissem.apps.abit.util.Constants.BITMESSAGE_ADDRESS_PATTERN | ||||
| import ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA | ||||
| import ch.dissem.apps.abit.util.Drawables | ||||
| import ch.dissem.apps.abit.util.Labels | ||||
| import ch.dissem.apps.abit.util.Strings.prepareMessageExtract | ||||
| import ch.dissem.bitmessage.entity.Plaintext | ||||
| import ch.dissem.bitmessage.entity.valueobject.Label | ||||
| import com.mikepenz.google_material_typeface_library.GoogleMaterial | ||||
| import com.mikepenz.iconics.view.IconicsImageView | ||||
| import kotlinx.android.synthetic.main.fragment_message_detail.* | ||||
| import java.util.* | ||||
|  | ||||
| /** | ||||
|  * A fragment representing a single Message detail screen. | ||||
| @@ -68,21 +59,25 @@ class MessageDetailFragment : Fragment() { | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|  | ||||
|         if (arguments.containsKey(ARG_ITEM)) { | ||||
|             // Load the dummy content specified by the fragment | ||||
|             // arguments. In a real-world scenario, use a Loader | ||||
|             // to load content from a content provider. | ||||
|             item = arguments.getSerializable(ARG_ITEM) as Plaintext | ||||
|         arguments?.let { arguments -> | ||||
|             if (arguments.containsKey(ARG_ITEM)) { | ||||
|                 // Load the dummy content specified by the fragment | ||||
|                 // arguments. In a real-world scenario, use a Loader | ||||
|                 // to load content from a content provider. | ||||
|                 item = arguments.getSerializable(ARG_ITEM) as Plaintext | ||||
|             } | ||||
|         } | ||||
|         setHasOptionsMenu(true) | ||||
|     } | ||||
|  | ||||
|     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?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity") | ||||
|  | ||||
|         // Show the dummy content as text in a TextView. | ||||
|         item?.let { item -> | ||||
|             subject.text = item.subject | ||||
| @@ -97,7 +92,7 @@ class MessageDetailFragment : Fragment() { | ||||
|                     recipient.setText(R.string.broadcast) | ||||
|                 } | ||||
|             }.invoke() | ||||
|             val labelAdapter = LabelAdapter(activity, item.labels) | ||||
|             val labelAdapter = LabelAdapter(ctx, item.labels) | ||||
|             labels.adapter = labelAdapter | ||||
|             labels.layoutManager = GridLayoutManager(activity, 2) | ||||
|  | ||||
| @@ -105,18 +100,16 @@ class MessageDetailFragment : Fragment() { | ||||
|  | ||||
|             Linkify.addLinks(text, WEB_URLS) | ||||
|             Linkify.addLinks(text, BITMESSAGE_ADDRESS_PATTERN, BITMESSAGE_URL_SCHEMA, null, | ||||
|                     Linkify.TransformFilter { match, _ -> match.group() } | ||||
|                 Linkify.TransformFilter { match, _ -> match.group() } | ||||
|             ) | ||||
|  | ||||
|             text.linksClickable = true | ||||
|             text.setTextIsSelectable(true) | ||||
|  | ||||
|             val removed = item.labels.removeAll { it.type==Label.Type.UNREAD } | ||||
|             val messageRepo = Singleton.getMessageRepository(context) | ||||
|             val removed = item.labels.removeAll { it.type == Label.Type.UNREAD } | ||||
|             val messageRepo = Singleton.getMessageRepository(ctx) | ||||
|             if (removed) { | ||||
|                 if (activity is MainActivity) { | ||||
|                     (activity as MainActivity).updateUnread() | ||||
|                 } | ||||
|                 (activity as? MainActivity)?.updateUnread() | ||||
|                 messageRepo.save(item) | ||||
|             } | ||||
|             val parents = ArrayList<Plaintext>(item.parents.size) | ||||
| @@ -126,32 +119,35 @@ class MessageDetailFragment : Fragment() { | ||||
|                     parents.add(parent) | ||||
|                 } | ||||
|             } | ||||
|             showRelatedMessages(view, R.id.parents, parents) | ||||
|             showRelatedMessages(view, R.id.responses, messageRepo.findResponses(item)) | ||||
|             showRelatedMessages(ctx, view, R.id.parents, parents) | ||||
|             showRelatedMessages(ctx, view, R.id.responses, messageRepo.findResponses(item)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun showRelatedMessages(rootView: View, @IdRes id: Int, messages: List<Plaintext>) { | ||||
|         val recyclerView = rootView.findViewById(id) as RecyclerView | ||||
|         val adapter = RelatedMessageAdapter(activity, messages) | ||||
|     private fun showRelatedMessages(ctx: Context, rootView: View, @IdRes id: Int, messages: List<Plaintext>) { | ||||
|         val recyclerView = rootView.findViewById<RecyclerView>(id) | ||||
|         val adapter = RelatedMessageAdapter(ctx, messages) | ||||
|         recyclerView.adapter = adapter | ||||
|         recyclerView.layoutManager = LinearLayoutManager(activity) | ||||
|     } | ||||
|  | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||||
|         inflater.inflate(R.menu.message, menu) | ||||
|  | ||||
|         Drawables.addIcon(activity, menu, R.id.reply, GoogleMaterial.Icon.gmd_reply) | ||||
|         Drawables.addIcon(activity, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete) | ||||
|         Drawables.addIcon(activity, menu, R.id.mark_unread, GoogleMaterial.Icon | ||||
|         activity?.let { activity -> | ||||
|             Drawables.addIcon(activity, menu, R.id.reply, GoogleMaterial.Icon.gmd_reply) | ||||
|             Drawables.addIcon(activity, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete) | ||||
|             Drawables.addIcon(activity, menu, R.id.mark_unread, GoogleMaterial.Icon | ||||
|                 .gmd_markunread) | ||||
|         Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive) | ||||
|             Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive) | ||||
|         } | ||||
|  | ||||
|         super.onCreateOptionsMenu(menu, inflater) | ||||
|     } | ||||
|  | ||||
|     override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { | ||||
|         val messageRepo = Singleton.getMessageRepository(context) | ||||
|         val messageRepo = Singleton.getMessageRepository( | ||||
|             context ?: throw IllegalStateException("No context available") | ||||
|         ) | ||||
|         item?.let { item -> | ||||
|             when (menuItem.itemId) { | ||||
|                 R.id.reply -> { | ||||
| @@ -167,7 +163,7 @@ class MessageDetailFragment : Fragment() { | ||||
|                         messageRepo.save(item) | ||||
|                     } | ||||
|                     (activity as? MainActivity)?.updateUnread() | ||||
|                     activity.onBackPressed() | ||||
|                     activity?.onBackPressed() | ||||
|                     return true | ||||
|                 } | ||||
|                 R.id.mark_unread -> { | ||||
| @@ -220,10 +216,10 @@ class MessageDetailFragment : Fragment() { | ||||
|         override fun getItemCount() = messages.size | ||||
|  | ||||
|         internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | ||||
|             internal val avatar = itemView.findViewById(R.id.avatar) as ImageView | ||||
|             internal val status = itemView.findViewById(R.id.status) as ImageView | ||||
|             internal val sender = itemView.findViewById(R.id.sender) as TextView | ||||
|             internal val extract = itemView.findViewById(R.id.text) as TextView | ||||
|             internal val avatar = itemView.findViewById<ImageView>(R.id.avatar) | ||||
|             internal val status = itemView.findViewById<ImageView>(R.id.status) | ||||
|             internal val sender = itemView.findViewById<TextView>(R.id.sender) | ||||
|             internal val extract = itemView.findViewById<TextView>(R.id.text) | ||||
|             internal var item: Plaintext? = null | ||||
|  | ||||
|             init { | ||||
| @@ -260,16 +256,16 @@ class MessageDetailFragment : Fragment() { | ||||
|             // Get the data model based on position | ||||
|             val label = labels[position] | ||||
|  | ||||
|             viewHolder.icon.setColor(Labels.getColor(label)) | ||||
|             viewHolder.icon.setIcon(Labels.getIcon(label)) | ||||
|             viewHolder.icon.icon?.color(Labels.getColor(label)) | ||||
|             viewHolder.icon.icon?.icon(Labels.getIcon(label)) | ||||
|             viewHolder.label.text = Labels.getText(label, ctx) | ||||
|         } | ||||
|  | ||||
|         override fun getItemCount() = labels.size | ||||
|  | ||||
|         internal class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | ||||
|             var icon = itemView.findViewById(R.id.icon) as IconicsImageView | ||||
|             var label = itemView.findViewById(R.id.label) as TextView | ||||
|             var icon = itemView.findViewById<IconicsImageView>(R.id.icon)!! | ||||
|             var label = itemView.findViewById<TextView>(R.id.label)!! | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -280,6 +276,6 @@ class MessageDetailFragment : Fragment() { | ||||
|          */ | ||||
|         val ARG_ITEM = "item" | ||||
|  | ||||
|         fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } ?: false | ||||
|         fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } == true | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -165,6 +165,8 @@ class MessageListFragment : Fragment(), ListHolder<Label> { | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         val context = context ?: throw IllegalStateException("No context available") | ||||
|  | ||||
|         layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) | ||||
|  | ||||
|         // touch guard manager  (this class is required to suppress scrolling while swipe-dismiss | ||||
| @@ -190,12 +192,10 @@ class MessageListFragment : Fragment(), ListHolder<Label> { | ||||
|                     Singleton.labeler.delete(item) | ||||
|                     messageRepo.save(item) | ||||
|                 } | ||||
|                 recyclerViewOnScrollListener.onScrolled(null, 0, 0) | ||||
|             } | ||||
|  | ||||
|             override fun onItemArchived(item: Plaintext) { | ||||
|                 Singleton.labeler.archive(item) | ||||
|                 recyclerViewOnScrollListener.onScrolled(null, 0, 0) | ||||
|             } | ||||
|  | ||||
|             override fun onItemViewClicked(v: View?) { | ||||
| @@ -239,28 +239,27 @@ class MessageListFragment : Fragment(), ListHolder<Label> { | ||||
|         this.swipeableMessageAdapter = adapter | ||||
|  | ||||
|         Singleton.labeler.listener = { message, added, removed -> | ||||
|             when { | ||||
|                 currentLabel?.type == Label.Type.TRASH && added.all { it.type == Label.Type.TRASH } && removed.any { it.type == Label.Type.TRASH } -> { | ||||
|                     // work-around for messages that are deleted from trash | ||||
|                     swipeableMessageAdapter?.remove(message) | ||||
|                     recyclerViewOnScrollListener.onScrolled(null, 0, 0) | ||||
|                 } | ||||
|                 currentLabel?.type == Label.Type.UNREAD && added.all { it.type == Label.Type.TRASH } -> { | ||||
|                     // work-around for messages that are deleted from unread, which already have the unread label removed | ||||
|                     swipeableMessageAdapter?.remove(message) | ||||
|                     recyclerViewOnScrollListener.onScrolled(null, 0, 0) | ||||
|                 } | ||||
|                 added.contains(currentLabel) -> { | ||||
|                     // in most cases, top should be the correct position, but time will show if | ||||
|                     // the message should be properly sorted in | ||||
|                     swipeableMessageAdapter?.addFirst(message) | ||||
|                 } | ||||
|                 removed.contains(currentLabel) -> { | ||||
|                     swipeableMessageAdapter?.remove(message) | ||||
|                     recyclerViewOnScrollListener.onScrolled(null, 0, 0) | ||||
|                 } | ||||
|                 removed.any { it.type == Label.Type.UNREAD } || added.any { it.type == Label.Type.UNREAD } -> { | ||||
|                     swipeableMessageAdapter?.update(message) | ||||
|             swipeableMessageAdapter?.let { swipeableMessageAdapter -> | ||||
|                 when { | ||||
|                     currentLabel?.type == Label.Type.TRASH && added.all { it.type == Label.Type.TRASH } && removed.any { it.type == Label.Type.TRASH } -> { | ||||
|                         // work-around for messages that are deleted from trash | ||||
|                         swipeableMessageAdapter.remove(message) | ||||
|                     } | ||||
|                     currentLabel?.type == Label.Type.UNREAD && added.all { it.type == Label.Type.TRASH } -> { | ||||
|                         // work-around for messages that are deleted from unread, which already have the unread label removed | ||||
|                         swipeableMessageAdapter.remove(message) | ||||
|                     } | ||||
|                     added.contains(currentLabel) -> { | ||||
|                         // in most cases, top should be the correct position, but time will show if | ||||
|                         // the message should be properly sorted in | ||||
|                         swipeableMessageAdapter.addFirst(message) | ||||
|                     } | ||||
|                     removed.contains(currentLabel) -> { | ||||
|                         swipeableMessageAdapter.remove(message) | ||||
|                     } | ||||
|                     removed.any { it.type == Label.Type.UNREAD } || added.any { it.type == Label.Type.UNREAD } -> { | ||||
|                         swipeableMessageAdapter.update(message) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -272,7 +271,7 @@ class MessageListFragment : Fragment(), ListHolder<Label> { | ||||
|         menu.add(R.string.personal_message).setIcon(R.drawable.ic_action_personal) | ||||
|         FabUtils.initFab(context, R.drawable.ic_action_compose_message, menu) | ||||
|                 .addOnMenuItemClickListener { _, _, itemId -> | ||||
|                     val identity = Singleton.getIdentity(activity) | ||||
|                     val identity = Singleton.getIdentity(context) | ||||
|                     if (identity == null) { | ||||
|                         Toast.makeText(activity, R.string.no_identity_warning, | ||||
|                                 Toast.LENGTH_LONG).show() | ||||
| @@ -347,12 +346,10 @@ class MessageListFragment : Fragment(), ListHolder<Label> { | ||||
|         this.activateOnItemClick = activateOnItemClick | ||||
|     } | ||||
|  | ||||
|     override fun showPreviousList(): Boolean { | ||||
|         return if (backStack.isEmpty()) { | ||||
|             false | ||||
|         } else { | ||||
|             doUpdateList(backStack.pop()) | ||||
|             true | ||||
|         } | ||||
|     override fun showPreviousList() = if (backStack.isEmpty()) { | ||||
|         false | ||||
|     } else { | ||||
|         doUpdateList(backStack.pop()) | ||||
|         true | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -59,26 +59,26 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|         addPreferencesFromResource(R.xml.preferences) | ||||
|  | ||||
|         findPreference("about")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { | ||||
|             val libsBuilder = LibsBuilder() | ||||
|             (activity as? MainActivity)?.let { activity -> | ||||
|                 val libsBuilder = LibsBuilder() | ||||
|                     .withActivityTitle(activity.getString(R.string.about)) | ||||
|                     .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) | ||||
|                     .withAboutIconShown(true) | ||||
|                     .withAboutVersionShown(true) | ||||
|                     .withAboutDescription(getString(R.string.about_app)) | ||||
|             val activity = activity as MainActivity | ||||
|             if (activity.hasDetailPane) { | ||||
|                 activity.setDetailView(libsBuilder.supportFragment()) | ||||
|             } else { | ||||
|                 libsBuilder.start(getActivity()) | ||||
|                 if (activity.hasDetailPane) { | ||||
|                     activity.setDetailView(libsBuilder.supportFragment()) | ||||
|                 } else { | ||||
|                     libsBuilder.start(activity) | ||||
|                 } | ||||
|             } | ||||
|             return@OnPreferenceClickListener true | ||||
|         } | ||||
|         val cleanup = findPreference("cleanup") | ||||
|         cleanup?.onPreferenceClickListener = Preference.OnPreferenceClickListener { | ||||
|             val ctx = activity.applicationContext | ||||
|             val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available") | ||||
|             cleanup.isEnabled = false | ||||
|             Toast.makeText(ctx, R.string.cleanup_notification_start, Toast | ||||
|                     .LENGTH_SHORT).show() | ||||
|             Toast.makeText(ctx, R.string.cleanup_notification_start, Toast.LENGTH_SHORT).show() | ||||
|  | ||||
|             doAsync { | ||||
|                 val bmc = Singleton.getBitmessageContext(ctx) | ||||
| @@ -88,9 +88,9 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|  | ||||
|                 uiThread { | ||||
|                     Toast.makeText( | ||||
|                             ctx, | ||||
|                             R.string.cleanup_notification_end, | ||||
|                             Toast.LENGTH_LONG | ||||
|                         ctx, | ||||
|                         R.string.cleanup_notification_end, | ||||
|                         Toast.LENGTH_LONG | ||||
|                     ).show() | ||||
|                     cleanup.isEnabled = true | ||||
|                 } | ||||
| @@ -99,36 +99,38 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|         } | ||||
|  | ||||
|         findPreference("export")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { | ||||
|             val ctx = context ?: throw IllegalStateException("No context available") | ||||
|  | ||||
|             val dialog = indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data) | ||||
|             doAsync { | ||||
|                 val exportDirectory = Preferences.getExportDirectory(context) | ||||
|                 val exportDirectory = Preferences.getExportDirectory(ctx) | ||||
|                 exportDirectory.mkdirs() | ||||
|                 val temp = File(exportDirectory, "export-${UnixTime.now}.zip") | ||||
|                 ZipOutputStream(FileOutputStream(temp)).use { zip -> | ||||
|                     zip.putNextEntry(ZipEntry("contacts.json")) | ||||
|                     val addressRepo = Singleton.getAddressRepository(context) | ||||
|                     val addressRepo = Singleton.getAddressRepository(ctx) | ||||
|                     val exportContacts = ContactExport.exportContacts(addressRepo.getContacts()) | ||||
|                     zip.write( | ||||
|                             exportContacts.toJsonString(true).toByteArray() | ||||
|                         exportContacts.toJsonString(true).toByteArray() | ||||
|                     ) | ||||
|                     zip.closeEntry() | ||||
|  | ||||
|                     val messageRepo = Singleton.getMessageRepository(context) | ||||
|                     val messageRepo = Singleton.getMessageRepository(ctx) | ||||
|                     zip.putNextEntry(ZipEntry("labels.json")) | ||||
|                     val exportLabels = MessageExport.exportLabels(messageRepo.getLabels()) | ||||
|                     zip.write( | ||||
|                             exportLabels.toJsonString(true).toByteArray() | ||||
|                         exportLabels.toJsonString(true).toByteArray() | ||||
|                     ) | ||||
|                     zip.closeEntry() | ||||
|                     zip.putNextEntry(ZipEntry("messages.json")) | ||||
|                     val exportMessages = MessageExport.exportMessages(messageRepo.getAllMessages()) | ||||
|                     zip.write( | ||||
|                             exportMessages.toJsonString(true).toByteArray() | ||||
|                         exportMessages.toJsonString(true).toByteArray() | ||||
|                     ) | ||||
|                     zip.closeEntry() | ||||
|                 } | ||||
|  | ||||
|                 val contentUri = getUriForFile(context, "ch.dissem.apps.abit.fileprovider", temp) | ||||
|                 val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", temp) | ||||
|                 val intent = Intent(android.content.Intent.ACTION_SEND) | ||||
|                 intent.type = "application/zip" | ||||
|                 intent.putExtra(Intent.EXTRA_SUBJECT, "abit-export.zip") | ||||
| @@ -161,8 +163,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun processEntry(zipFile: Uri, entry: String, processor: (JsonArray<*>) -> Unit) { | ||||
|         ZipInputStream(context.contentResolver.openInputStream(zipFile)).use { zip -> | ||||
|     private fun processEntry(ctx: Context, zipFile: Uri, entry: String, processor: (JsonArray<*>) -> Unit) = | ||||
|         ZipInputStream(ctx.contentResolver.openInputStream(zipFile)).use { zip -> | ||||
|             var nextEntry = zip.nextEntry | ||||
|             while (nextEntry != null) { | ||||
|                 if (nextEntry.name == entry) { | ||||
| @@ -171,38 +173,38 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|                 nextEntry = zip.nextEntry | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         val ctx = context ?: throw IllegalStateException("No context available") | ||||
|         when (requestCode) { | ||||
|             WRITE_EXPORT_REQUEST_CODE -> Preferences.cleanupExportDirectory(context) | ||||
|             WRITE_EXPORT_REQUEST_CODE -> Preferences.cleanupExportDirectory(ctx) | ||||
|             READ_IMPORT_REQUEST_CODE -> { | ||||
|                 if (resultCode == Activity.RESULT_OK && data?.data != null) { | ||||
|                     val dialog = indeterminateProgressDialog(R.string.import_data_summary, R.string.import_data) | ||||
|                     doAsync { | ||||
|                         val ctx = Singleton.getBitmessageContext(context) | ||||
|                         val bmc = Singleton.getBitmessageContext(ctx) | ||||
|                         val labels = mutableMapOf<String, Label>() | ||||
|                         val zipFile = data.data | ||||
|  | ||||
|                         processEntry(zipFile, "contacts.json") { json -> | ||||
|                         processEntry(ctx, zipFile, "contacts.json") { json -> | ||||
|                             ContactExport.importContacts(json).forEach { contact -> | ||||
|                                 ctx.addresses.save(contact) | ||||
|                                 bmc.addresses.save(contact) | ||||
|                             } | ||||
|                         } | ||||
|                         ctx.messages.getLabels().forEach { label -> | ||||
|                         bmc.messages.getLabels().forEach { label -> | ||||
|                             labels[label.toString()] = label | ||||
|                         } | ||||
|                         processEntry(zipFile, "labels.json") { json -> | ||||
|                         processEntry(ctx, zipFile, "labels.json") { json -> | ||||
|                             MessageExport.importLabels(json).forEach { label -> | ||||
|                                 if (!labels.contains(label.toString())) { | ||||
|                                     ctx.messages.save(label) | ||||
|                                     bmc.messages.save(label) | ||||
|                                     labels[label.toString()] = label | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         processEntry(zipFile, "messages.json") { json -> | ||||
|                         processEntry(ctx, zipFile, "messages.json") { json -> | ||||
|                             MessageExport.importMessages(json, labels).forEach { message -> | ||||
|                                 ctx.messages.save(message) | ||||
|                                 bmc.messages.save(message) | ||||
|                             } | ||||
|                         } | ||||
|                         uiThread { | ||||
| @@ -218,7 +220,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|         super.onAttach(ctx) | ||||
|         (ctx as? MainActivity)?.floatingActionButton?.hide() | ||||
|         PreferenceManager.getDefaultSharedPreferences(ctx) | ||||
|                 .registerOnSharedPreferenceChangeListener(this) | ||||
|             .registerOnSharedPreferenceChangeListener(this) | ||||
|  | ||||
|         (ctx as? MainActivity)?.updateTitle(getString(R.string.settings)) | ||||
|     } | ||||
| @@ -227,19 +229,21 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP | ||||
|         when (key) { | ||||
|             PREFERENCE_TRUSTED_NODE -> { | ||||
|                 val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null) | ||||
|                 val ctx = context ?: throw IllegalStateException("No context available") | ||||
|                 if (node != null) { | ||||
|                     SyncAdapter.startSync(activity) | ||||
|                     SyncAdapter.startSync(ctx) | ||||
|                 } else { | ||||
|                     SyncAdapter.stopSync(activity) | ||||
|                     SyncAdapter.stopSync(ctx) | ||||
|                 } | ||||
|             } | ||||
|             PREFERENCE_SERVER_POW -> { | ||||
|                 val node = sharedPreferences.getString(PREFERENCE_TRUSTED_NODE, null) | ||||
|                 if (node != null) { | ||||
|                     val ctx = context ?: throw IllegalStateException("No context available") | ||||
|                     if (sharedPreferences.getBoolean(PREFERENCE_SERVER_POW, false)) { | ||||
|                         SyncAdapter.startPowSync(activity) | ||||
|                         SyncAdapter.startPowSync(ctx) | ||||
|                     } else { | ||||
|                         SyncAdapter.stopPowSync(activity) | ||||
|                         SyncAdapter.stopPowSync(ctx) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -18,7 +18,6 @@ package ch.dissem.apps.abit | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.support.v7.app.AppCompatActivity | ||||
| import android.support.v7.widget.Toolbar | ||||
| import ch.dissem.apps.abit.service.Singleton | ||||
| import com.mikepenz.materialize.MaterializeBuilder | ||||
| import kotlinx.android.synthetic.main.activity_status.* | ||||
|   | ||||
| @@ -28,19 +28,21 @@ import ch.dissem.apps.abit.service.Singleton | ||||
| class StatusFragment : Fragment() { | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = | ||||
|             inflater.inflate(R.layout.fragment_status, container, false) | ||||
|         inflater.inflate(R.layout.fragment_status, container, false) | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         val bmc = Singleton.getBitmessageContext(context) | ||||
|         val bmc = Singleton.getBitmessageContext( | ||||
|             context ?: throw IllegalStateException("No context available") | ||||
|         ) | ||||
|         val status = StringBuilder() | ||||
|         for (address in bmc.addresses.getIdentities()) { | ||||
|             status.append(address.address).append('\n') | ||||
|         } | ||||
|         status.append('\n') | ||||
|         status.append(bmc.status()) | ||||
|         (view.findViewById(R.id.content) as TextView).text = status | ||||
|         view.findViewById<TextView>(R.id.content).text = status | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -51,8 +51,8 @@ class AddressSelectorAdapter(identities: List<BitmessageAddress>) : RecyclerView | ||||
|  | ||||
|     class ViewHolder internal constructor(v: View) : RecyclerView.ViewHolder(v) { | ||||
|         var data: Selectable<BitmessageAddress>? = null | ||||
|         val checkbox = v.findViewById(R.id.checkbox) as CheckBox | ||||
|         val address = v.findViewById(R.id.address) as TextView | ||||
|         val checkbox = v.findViewById<CheckBox>(R.id.checkbox)!! | ||||
|         val address = v.findViewById<TextView>(R.id.address)!! | ||||
|  | ||||
|         init { | ||||
|             checkbox.setOnCheckedChangeListener { _, isChecked -> | ||||
| @@ -63,12 +63,8 @@ class AddressSelectorAdapter(identities: List<BitmessageAddress>) : RecyclerView | ||||
|  | ||||
|     val selected: List<BitmessageAddress> | ||||
|         get() { | ||||
|             val result = LinkedList<BitmessageAddress>() | ||||
|             for (selectable in data) { | ||||
|                 if (selectable.selected) { | ||||
|                     result.add(selectable.data) | ||||
|                 } | ||||
|             } | ||||
|             return result | ||||
|             return data | ||||
|                 .filter { it.selected } | ||||
|                 .mapTo(LinkedList()) { it.data } | ||||
|         } | ||||
| } | ||||
|   | ||||
| @@ -37,15 +37,9 @@ import ch.dissem.bitmessage.entity.BitmessageAddress | ||||
|  * An adapter for contacts. Can be filtered by alias or address. | ||||
|  */ | ||||
| class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable { | ||||
|     private val inflater: LayoutInflater | ||||
|     private val originalData: List<BitmessageAddress> | ||||
|     private var data: List<BitmessageAddress> = emptyList() | ||||
|  | ||||
|     init { | ||||
|         inflater = LayoutInflater.from(ctx) | ||||
|         originalData = Singleton.getAddressRepository(ctx).getContacts() | ||||
|         data = originalData | ||||
|     } | ||||
|     private val inflater = LayoutInflater.from(ctx) | ||||
|     private val originalData = Singleton.getAddressRepository(ctx).getContacts() | ||||
|     private var data: List<BitmessageAddress> = originalData | ||||
|  | ||||
|     override fun getCount() = data.size | ||||
|  | ||||
| @@ -69,11 +63,16 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable { | ||||
|     override fun getFilter(): Filter = ContactFilter() | ||||
|  | ||||
|     private inner class ViewHolder(val view: View) { | ||||
|         val avatar = view.findViewById(R.id.avatar) as ImageView | ||||
|         val name = view.findViewById(R.id.name) as TextView | ||||
|         val address = view.findViewById(R.id.address) as TextView | ||||
|         val avatar = view.findViewById<ImageView>(R.id.avatar)!! | ||||
|         val name = view.findViewById<TextView>(R.id.name)!! | ||||
|         val address = view.findViewById<TextView>(R.id.address)!! | ||||
|  | ||||
|         init { | ||||
|             view.tag = this | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private inner class ContactFilter : Filter() { | ||||
|         override fun performFiltering(prefix: CharSequence?): Filter.FilterResults { | ||||
|             val results = Filter.FilterResults() | ||||
| @@ -116,7 +115,7 @@ class ContactAdapter(ctx: Context) : BaseAdapter(), Filterable { | ||||
|             return results | ||||
|         } | ||||
|  | ||||
|         override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) { | ||||
|         override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults) { | ||||
|             @Suppress("UNCHECKED_CAST") | ||||
|             data = results.values as List<BitmessageAddress> | ||||
|             if (results.count > 0) { | ||||
|   | ||||
| @@ -19,6 +19,6 @@ package ch.dissem.apps.abit.adapter | ||||
| /** | ||||
|  * @author Christian Basler | ||||
|  */ | ||||
| class Selectable<T>(val data: T) { | ||||
| class Selectable<out T>(val data: T) { | ||||
|     var selected = false | ||||
| } | ||||
|   | ||||
| @@ -72,12 +72,12 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie | ||||
|     } | ||||
|  | ||||
|     class ViewHolder(v: View) : AbstractSwipeableItemViewHolder(v) { | ||||
|         val container = v.findViewById(R.id.container) as FrameLayout | ||||
|         val avatar = v.findViewById(R.id.avatar) as ImageView | ||||
|         val status = v.findViewById(R.id.status) as ImageView | ||||
|         val sender = v.findViewById(R.id.sender) as TextView | ||||
|         val subject = v.findViewById(R.id.subject) as TextView | ||||
|         val extract = v.findViewById(R.id.text) as TextView | ||||
|         val container = v.findViewById<FrameLayout>(R.id.container)!! | ||||
|         val avatar = v.findViewById<ImageView>(R.id.avatar)!! | ||||
|         val status = v.findViewById<ImageView>(R.id.status)!! | ||||
|         val sender = v.findViewById<TextView>(R.id.sender)!! | ||||
|         val subject = v.findViewById<TextView>(R.id.subject)!! | ||||
|         val extract = v.findViewById<TextView>(R.id.text)!! | ||||
|  | ||||
|         override fun getSwipeableContainerView() = container | ||||
|     } | ||||
| @@ -110,19 +110,16 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie | ||||
|  | ||||
|     fun remove(item: Plaintext) { | ||||
|         val index = data.indexOf(item) | ||||
|         data.removeIf { it.id == item.id } | ||||
|         data.removeAll { it.id == item.id } | ||||
|         notifyItemRemoved(index) | ||||
|     } | ||||
|  | ||||
|     fun update(item: Plaintext) { | ||||
|         data.replaceAll { | ||||
|             if (it.id == item.id) { | ||||
|                 item | ||||
|             } else { | ||||
|                 it | ||||
|             } | ||||
|         val index = data.indexOfFirst { it.id == item.id } | ||||
|         if (index >= 0) { | ||||
|             data[index] = item | ||||
|             notifyItemChanged(index) | ||||
|         } | ||||
|         notifyItemChanged(data.indexOf(item)) | ||||
|     } | ||||
|  | ||||
|     fun clear(newLabel: Label?) { | ||||
| @@ -137,7 +134,7 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie | ||||
|  | ||||
|     private fun onSwipeableViewContainerClick(v: View) { | ||||
|         eventListener?.onItemViewClicked( | ||||
|                 RecyclerViewAdapterUtils.getParentViewHolderItemView(v)) | ||||
|             RecyclerViewAdapterUtils.getParentViewHolderItemView(v)) | ||||
|     } | ||||
|  | ||||
|     fun getItem(position: Int) = data[position] | ||||
| @@ -156,10 +153,10 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie | ||||
|         holder.apply { | ||||
|             if (activateOnItemClick) { | ||||
|                 container.setBackgroundResource( | ||||
|                         if (position == selectedPosition) | ||||
|                             R.drawable.bg_item_selected_state | ||||
|                         else | ||||
|                             R.drawable.bg_item_normal_state | ||||
|                     if (position == selectedPosition) | ||||
|                         R.drawable.bg_item_selected_state | ||||
|                     else | ||||
|                         R.drawable.bg_item_normal_state | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
| @@ -188,36 +185,35 @@ class SwipeableMessageAdapter : RecyclerView.Adapter<SwipeableMessageAdapter.Vie | ||||
|  | ||||
|     override fun getItemCount() = data.size | ||||
|  | ||||
|     override fun onGetSwipeReactionType(holder: ViewHolder, position: Int, x: Int, y: Int): Int { | ||||
|         return if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) { | ||||
|     override fun onGetSwipeReactionType(holder: ViewHolder, position: Int, x: Int, y: Int): Int = | ||||
|         if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) { | ||||
|             REACTION_CAN_SWIPE_LEFT or REACTION_CAN_NOT_SWIPE_RIGHT_WITH_RUBBER_BAND_EFFECT | ||||
|         } else { | ||||
|             REACTION_CAN_SWIPE_BOTH_H | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SuppressLint("SwitchIntDef") | ||||
|     override fun onSetSwipeBackground(holder: ViewHolder, position: Int, type: Int) { | ||||
|         var bgRes = 0 | ||||
|         when (type) { | ||||
|             DRAWABLE_SWIPE_NEUTRAL_BACKGROUND -> bgRes = R.drawable.bg_swipe_item_neutral | ||||
|             DRAWABLE_SWIPE_LEFT_BACKGROUND -> bgRes = R.drawable.bg_swipe_item_left | ||||
|     override fun onSetSwipeBackground(holder: ViewHolder, position: Int, type: Int) = | ||||
|         holder.itemView.setBackgroundResource(when (type) { | ||||
|             DRAWABLE_SWIPE_NEUTRAL_BACKGROUND -> R.drawable.bg_swipe_item_neutral | ||||
|             DRAWABLE_SWIPE_LEFT_BACKGROUND -> R.drawable.bg_swipe_item_left | ||||
|             DRAWABLE_SWIPE_RIGHT_BACKGROUND -> if (label === LABEL_ARCHIVE || label?.type == Label.Type.TRASH) { | ||||
|                 bgRes = R.drawable.bg_swipe_item_neutral | ||||
|                 R.drawable.bg_swipe_item_neutral | ||||
|             } else { | ||||
|                 bgRes = R.drawable.bg_swipe_item_right | ||||
|                 R.drawable.bg_swipe_item_right | ||||
|             } | ||||
|         } | ||||
|         holder.itemView.setBackgroundResource(bgRes) | ||||
|     } | ||||
|             else -> R.drawable.bg_swipe_item_neutral | ||||
|         }) | ||||
|  | ||||
|     @SuppressLint("SwitchIntDef") | ||||
|     override fun onSwipeItem(holder: ViewHolder, position: Int, result: Int) = | ||||
|             when (result) { | ||||
|                 RESULT_SWIPED_RIGHT -> SwipeRightResultAction(this, position) | ||||
|                 RESULT_SWIPED_LEFT -> SwipeLeftResultAction(this, position) | ||||
|                 else -> null | ||||
|             } | ||||
|         when (result) { | ||||
|             RESULT_SWIPED_RIGHT -> SwipeRightResultAction(this, position) | ||||
|             RESULT_SWIPED_LEFT -> SwipeLeftResultAction(this, position) | ||||
|             else -> null | ||||
|         } | ||||
|  | ||||
|     override fun onSwipeItemStarted(holder: ViewHolder?, position: Int) = Unit | ||||
|  | ||||
|     fun setSelectedPosition(selectedPosition: Int) { | ||||
|         val oldPosition = this.selectedPosition | ||||
|   | ||||
| @@ -17,11 +17,7 @@ | ||||
| package ch.dissem.apps.abit.adapter | ||||
|  | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.preference.PreferenceManager | ||||
|  | ||||
| import java.util.Arrays | ||||
|  | ||||
| import ch.dissem.bitmessage.InternalContext | ||||
| import ch.dissem.bitmessage.ports.ProofOfWorkEngine | ||||
|  | ||||
| @@ -46,9 +42,7 @@ class SwitchingProofOfWorkEngine( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun setContext(context: InternalContext) { | ||||
|         listOf(option, fallback) | ||||
|                 .filterIsInstance<InternalContext.ContextHolder>() | ||||
|                 .forEach { it.setContext(context) } | ||||
|     } | ||||
|     override fun setContext(context: InternalContext) = listOf(option, fallback) | ||||
|             .filterIsInstance<InternalContext.ContextHolder>() | ||||
|             .forEach { it.setContext(context) } | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,7 @@ import org.jetbrains.anko.uiThread | ||||
|  | ||||
| class AddIdentityDialogFragment : AppCompatDialogFragment() { | ||||
|     private lateinit var bmc: BitmessageContext | ||||
|     private var parent: ViewGroup? = null | ||||
|  | ||||
|     override fun onAttach(context: Context?) { | ||||
|         super.onAttach(context) | ||||
| @@ -50,13 +51,15 @@ class AddIdentityDialogFragment : AppCompatDialogFragment() { | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||||
|         dialog.setTitle(R.string.add_identity) | ||||
|         parent = container | ||||
|         return inflater.inflate(R.layout.dialog_add_identity, container, false) | ||||
|     } | ||||
|  | ||||
|     override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         ok.setOnClickListener(View.OnClickListener { | ||||
|             val ctx = activity.baseContext | ||||
|             val ctx = activity?.baseContext ?: throw IllegalStateException("No context available") | ||||
|  | ||||
|             when (radioGroup.checkedRadioButtonId) { | ||||
|                 R.id.create_identity -> { | ||||
|                     Toast.makeText(ctx, | ||||
| @@ -84,14 +87,14 @@ class AddIdentityDialogFragment : AppCompatDialogFragment() { | ||||
|     } | ||||
|  | ||||
|     private fun addChanDialog() { | ||||
|         val activity = activity | ||||
|         val ctx = activity.baseContext | ||||
|         val dialogView = activity.layoutInflater.inflate(R.layout.dialog_input_passphrase, null) | ||||
|         val activity = activity ?: throw IllegalStateException("No activity available") | ||||
|         val ctx = activity.baseContext ?: throw IllegalStateException("No context available") | ||||
|         val dialogView = activity.layoutInflater.inflate(R.layout.dialog_input_passphrase, parent) | ||||
|         AlertDialog.Builder(activity) | ||||
|                 .setTitle(R.string.add_chan) | ||||
|                 .setView(dialogView) | ||||
|                 .setPositiveButton(R.string.ok) { _, _ -> | ||||
|                     val passphrase = dialogView.findViewById(R.id.passphrase) as TextView | ||||
|                     val passphrase = dialogView.findViewById<TextView>(R.id.passphrase) | ||||
|                     Toast.makeText(ctx, R.string.toast_long_running_operation, | ||||
|                             Toast.LENGTH_SHORT).show() | ||||
|                     val pass = passphrase.text.toString() | ||||
|   | ||||
| @@ -22,7 +22,6 @@ import android.support.v7.app.AppCompatDialogFragment | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.TextView | ||||
| import android.widget.Toast | ||||
| import ch.dissem.apps.abit.MainActivity | ||||
| import ch.dissem.apps.abit.R | ||||
| @@ -53,7 +52,7 @@ class DeterministicIdentityDialogFragment : AppCompatDialogFragment() { | ||||
|         super.onViewCreated(dialogView, savedInstanceState) | ||||
|         ok.setOnClickListener { | ||||
|             dismiss() | ||||
|             val context = activity.baseContext | ||||
|             val context = activity?.baseContext ?: throw IllegalStateException("No context available") | ||||
|             val passphraseText = passphrase.text.toString() | ||||
|  | ||||
|             Toast.makeText(context, R.string.toast_long_running_operation, Toast.LENGTH_SHORT).show() | ||||
|   | ||||
| @@ -39,12 +39,12 @@ class SelectEncodingDialogFragment : AppCompatDialogFragment() { | ||||
|     private lateinit var encoding: Plaintext.Encoding | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||||
|         encoding = (arguments.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding) ?: SIMPLE | ||||
|         encoding = (arguments?.getSerializable(EXTRA_ENCODING) as? Plaintext.Encoding) ?: SIMPLE | ||||
|         dialog.setTitle(R.string.select_encoding_title) | ||||
|         return inflater.inflate(R.layout.dialog_select_message_encoding, container, false) | ||||
|     } | ||||
|  | ||||
|     override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         when (encoding) { | ||||
|             SIMPLE -> radioGroup.check(R.id.simple) | ||||
| @@ -62,7 +62,7 @@ class SelectEncodingDialogFragment : AppCompatDialogFragment() { | ||||
|             } | ||||
|             val result = Intent() | ||||
|             result.putExtra(EXTRA_ENCODING, encoding) | ||||
|             targetFragment.onActivityResult(targetRequestCode, RESULT_OK, result) | ||||
|             targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, result) | ||||
|             dismiss() | ||||
|         }) | ||||
|         dismiss.setOnClickListener { dismiss() } | ||||
|   | ||||
| @@ -3,19 +3,16 @@ package ch.dissem.apps.abit.drawer | ||||
| import android.app.Dialog | ||||
| import android.content.Context | ||||
| import android.graphics.Point | ||||
| import android.view.Display | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.view.Window | ||||
| import android.view.WindowManager | ||||
| import android.widget.ImageView | ||||
| import android.widget.RelativeLayout | ||||
|  | ||||
| import com.mikepenz.materialdrawer.AccountHeader | ||||
| import com.mikepenz.materialdrawer.model.interfaces.IProfile | ||||
|  | ||||
| import ch.dissem.apps.abit.service.Singleton | ||||
| import ch.dissem.apps.abit.util.Drawables | ||||
| import com.mikepenz.materialdrawer.AccountHeader | ||||
| import com.mikepenz.materialdrawer.model.interfaces.IProfile | ||||
|  | ||||
| class ProfileImageListener(private val ctx: Context) : AccountHeader.OnAccountHeaderProfileImageListener { | ||||
|  | ||||
|   | ||||
| @@ -49,7 +49,5 @@ class ProfileSelectionListener( | ||||
|         return false | ||||
|     } | ||||
|  | ||||
|     private fun addIdentityDialog() { | ||||
|         AddIdentityDialogFragment().show(fragmentManager, "dialog") | ||||
|     } | ||||
|     private fun addIdentityDialog() = AddIdentityDialogFragment().show(fragmentManager, "dialog") | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ package ch.dissem.apps.abit.listener | ||||
|  * implement. This mechanism allows activities to be notified of item | ||||
|  * selections. | ||||
|  */ | ||||
| interface ListSelectionListener<T> { | ||||
| interface ListSelectionListener<in T> { | ||||
|     /** | ||||
|      * Callback for when an item has been selected. | ||||
|      */ | ||||
|   | ||||
| @@ -19,13 +19,14 @@ package ch.dissem.apps.abit.notification | ||||
| import android.app.Notification | ||||
| import android.app.NotificationManager | ||||
| import android.content.Context | ||||
| import org.jetbrains.anko.notificationManager | ||||
|  | ||||
| /** | ||||
|  * Some base class to create and handle notifications. | ||||
|  */ | ||||
| abstract class AbstractNotification(ctx: Context) { | ||||
|     protected val ctx = ctx.applicationContext | ||||
|     protected val manager = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||||
|     protected val ctx = ctx.applicationContext!! | ||||
|     private val manager = ctx.notificationManager | ||||
|     var notification: Notification? = null | ||||
|         protected set | ||||
|     protected var showing = false | ||||
|   | ||||
| @@ -18,7 +18,7 @@ package ch.dissem.apps.abit.notification | ||||
|  | ||||
| import android.content.Context | ||||
| import android.support.annotation.StringRes | ||||
| import android.support.v7.app.NotificationCompat | ||||
| import android.support.v4.app.NotificationCompat | ||||
|  | ||||
| import ch.dissem.apps.abit.R | ||||
|  | ||||
| @@ -30,7 +30,7 @@ import ch.dissem.apps.abit.R | ||||
|  */ | ||||
| class ErrorNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|  | ||||
|     private val builder = NotificationCompat.Builder(ctx) | ||||
|     private val builder = NotificationCompat.Builder(ctx, "abit.error") | ||||
|         .setContentTitle(ctx.getString(R.string.app_name)) | ||||
|         .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import android.app.PendingIntent | ||||
| import android.app.PendingIntent.FLAG_UPDATE_CURRENT | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.support.v7.app.NotificationCompat | ||||
| import android.support.v4.app.NotificationCompat | ||||
| import ch.dissem.apps.abit.MainActivity | ||||
| import ch.dissem.apps.abit.R | ||||
| import ch.dissem.apps.abit.service.BitmessageIntentService | ||||
| @@ -34,7 +34,7 @@ import kotlin.concurrent.fixedRateTimer | ||||
|  */ | ||||
| class NetworkNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|  | ||||
|     private val builder = NotificationCompat.Builder(ctx) | ||||
|     private val builder = NotificationCompat.Builder(ctx, "abit.network") | ||||
|     private var timer: Timer? = null | ||||
|  | ||||
|     init { | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import android.app.PendingIntent | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.graphics.Typeface | ||||
| import android.support.v7.app.NotificationCompat | ||||
| import android.support.v4.app.NotificationCompat | ||||
| import android.support.v4.app.NotificationCompat.BigTextStyle | ||||
| import android.support.v4.app.NotificationCompat.InboxStyle | ||||
| import android.text.Spannable | ||||
| @@ -43,7 +43,7 @@ import ch.dissem.apps.abit.util.Drawables.toBitmap | ||||
| class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|  | ||||
|     fun singleNotification(plaintext: Plaintext): NewMessageNotification { | ||||
|         val builder = NotificationCompat.Builder(ctx) | ||||
|         val builder = NotificationCompat.Builder(ctx, CHANNEL_ID) | ||||
|         val bigText = SpannableString(plaintext.subject + "\n" + plaintext.text) | ||||
|         plaintext.subject?.let { subject -> | ||||
|             bigText.setSpan(SPAN_EMPHASIS, 0, subject.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) | ||||
| @@ -83,7 +83,7 @@ class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|      * *                       {}` block | ||||
|      */ | ||||
|     fun multiNotification(unacknowledged: Collection<Plaintext>, numberOfUnacknowledgedMessages: Int): NewMessageNotification { | ||||
|         val builder = NotificationCompat.Builder(ctx) | ||||
|         val builder = NotificationCompat.Builder(ctx, CHANNEL_ID) | ||||
|         builder.setSmallIcon(R.drawable.ic_notification_new_message) | ||||
|                 .setContentTitle(ctx.getString(R.string.n_new_messages, numberOfUnacknowledgedMessages)) | ||||
|                 .setContentText(ctx.getString(R.string.app_name)) | ||||
| @@ -113,5 +113,6 @@ class NewMessageNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|     companion object { | ||||
|         private val NEW_MESSAGE_NOTIFICATION_ID = 1 | ||||
|         private val SPAN_EMPHASIS = StyleSpan(Typeface.BOLD) | ||||
|         private val CHANNEL_ID = "abit.message" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ package ch.dissem.apps.abit.notification | ||||
| import android.app.PendingIntent | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.support.v7.app.NotificationCompat | ||||
| import android.support.v4.app.NotificationCompat | ||||
|  | ||||
| import ch.dissem.apps.abit.MainActivity | ||||
| import ch.dissem.apps.abit.R | ||||
| @@ -33,7 +33,7 @@ import kotlin.concurrent.fixedRateTimer | ||||
|  */ | ||||
| class ProofOfWorkNotification(ctx: Context) : AbstractNotification(ctx) { | ||||
|  | ||||
|     private val builder = NotificationCompat.Builder(ctx) | ||||
|     private val builder = NotificationCompat.Builder(ctx, "abit.pow") | ||||
|         .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||||
|         .setUsesChronometer(true) | ||||
|         .setOngoing(true) | ||||
|   | ||||
| @@ -17,26 +17,18 @@ | ||||
| package ch.dissem.apps.abit.pow | ||||
|  | ||||
| import android.content.Context | ||||
| import android.widget.Toast | ||||
|  | ||||
| import org.slf4j.Logger | ||||
| import org.slf4j.LoggerFactory | ||||
|  | ||||
| import java.util.concurrent.ExecutorService | ||||
| import java.util.concurrent.Executors | ||||
| import java.util.concurrent.ThreadFactory | ||||
|  | ||||
| import ch.dissem.apps.abit.service.Singleton | ||||
| import ch.dissem.apps.abit.synchronization.SyncAdapter | ||||
| import ch.dissem.apps.abit.util.Preferences | ||||
| import ch.dissem.bitmessage.InternalContext | ||||
| import ch.dissem.bitmessage.entity.BitmessageAddress | ||||
| import ch.dissem.bitmessage.extensions.CryptoCustomMessage | ||||
| import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest | ||||
| import ch.dissem.bitmessage.ports.ProofOfWorkEngine | ||||
|  | ||||
| import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE | ||||
| import ch.dissem.bitmessage.ports.ProofOfWorkEngine | ||||
| import ch.dissem.bitmessage.utils.Singleton.cryptography | ||||
| import org.slf4j.LoggerFactory | ||||
| import java.util.concurrent.ExecutorService | ||||
| import java.util.concurrent.Executors | ||||
|  | ||||
| /** | ||||
|  * @author Christian Basler | ||||
| @@ -54,7 +46,7 @@ class ServerPowEngine(private val ctx: Context) : ProofOfWorkEngine, InternalCon | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) { | ||||
|     override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) = | ||||
|         pool.execute { | ||||
|             val identity = Singleton.getIdentity(ctx) ?: throw RuntimeException("No Identity for calculating POW") | ||||
|  | ||||
| @@ -80,7 +72,6 @@ class ServerPowEngine(private val ctx: Context) : ProofOfWorkEngine, InternalCon | ||||
|                 LOG.error(e.message, e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun setContext(context: InternalContext) { | ||||
|         this.context = context | ||||
|   | ||||
| @@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.BitmessageAddress | ||||
| import ch.dissem.bitmessage.entity.payload.V3Pubkey | ||||
| import ch.dissem.bitmessage.entity.payload.V4Pubkey | ||||
| import ch.dissem.bitmessage.entity.valueobject.PrivateKey | ||||
| import ch.dissem.bitmessage.exception.ApplicationException | ||||
| import ch.dissem.bitmessage.factory.Factory | ||||
| import ch.dissem.bitmessage.ports.AddressRepository | ||||
| import ch.dissem.bitmessage.utils.Encode | ||||
| @@ -149,12 +148,10 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { | ||||
|         return result | ||||
|     } | ||||
|  | ||||
|     override fun save(address: BitmessageAddress) { | ||||
|         if (exists(address)) { | ||||
|             update(address) | ||||
|         } else { | ||||
|             insert(address) | ||||
|         } | ||||
|     override fun save(address: BitmessageAddress) = if (exists(address)) { | ||||
|         update(address) | ||||
|     } else { | ||||
|         insert(address) | ||||
|     } | ||||
|  | ||||
|     private fun exists(address: BitmessageAddress): Boolean { | ||||
| @@ -219,15 +216,6 @@ class AndroidAddressRepository(private val sql: SqlHelper) : AddressRepository { | ||||
|         db.delete(TABLE_NAME, "address = ?", arrayOf(address.address)) | ||||
|     } | ||||
|  | ||||
|     fun getById(id: String): BitmessageAddress { | ||||
|         val result = find("address = '$id'") | ||||
|         return if (result.isNotEmpty()) { | ||||
|             result[0] | ||||
|         } else { | ||||
|             throw ApplicationException("Address with id $id not found.") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun getAddress(address: String) = find("address = '$address'").firstOrNull() | ||||
|  | ||||
|     companion object { | ||||
|   | ||||
| @@ -18,7 +18,6 @@ package ch.dissem.apps.abit.repository | ||||
|  | ||||
| import android.content.ContentValues | ||||
| import android.database.sqlite.SQLiteConstraintException | ||||
| import ch.dissem.apps.abit.repository.SqlHelper.Companion.join | ||||
| import ch.dissem.bitmessage.entity.ObjectMessage | ||||
| import ch.dissem.bitmessage.entity.payload.ObjectType | ||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector | ||||
|   | ||||
| @@ -48,12 +48,6 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context: | ||||
|         super.findMessages(label, offset, limit) | ||||
|     } | ||||
|  | ||||
|     fun findMessageIds(label: Label) = if (label === LABEL_ARCHIVE) { | ||||
|         findIds("id NOT IN (SELECT message_id FROM Message_Label)") | ||||
|     } else { | ||||
|         findIds("id IN (SELECT message_id FROM Message_Label WHERE label_id=${label.id})") | ||||
|     } | ||||
|  | ||||
|     public override fun findLabels(where: String): List<Label> { | ||||
|         val result = LinkedList<Label>() | ||||
|  | ||||
| @@ -203,27 +197,6 @@ class AndroidMessageRepository(private val sql: SqlHelper, private val context: | ||||
|         db.update(PARENTS_TABLE_NAME, values, where, null) | ||||
|     } | ||||
|  | ||||
|     private fun findIds(where: String): List<Long> { | ||||
|         val result = LinkedList<Long>() | ||||
|  | ||||
|         // Define a projection that specifies which columns from the database | ||||
|         // you will actually use after this query. | ||||
|         val projection = arrayOf(COLUMN_ID) | ||||
|  | ||||
|         val db = sql.readableDatabase | ||||
|         db.query( | ||||
|                 TABLE_NAME, projection, | ||||
|                 where, null, null, null, | ||||
|                 "$COLUMN_RECEIVED DESC, $COLUMN_SENT DESC" | ||||
|         ).use { c -> | ||||
|             while (c.moveToNext()) { | ||||
|                 val id = c.getLong(0) | ||||
|                 result.add(id) | ||||
|             } | ||||
|         } | ||||
|         return result | ||||
|     } | ||||
|  | ||||
|     override fun find(where: String, offset: Int, limit: Int): List<Plaintext> { | ||||
|         val result = LinkedList<Plaintext>() | ||||
|  | ||||
|   | ||||
| @@ -54,10 +54,10 @@ class AndroidNodeRegistry(private val sql: SqlHelper) : NodeRegistry { | ||||
|         statement.bindLong(1, node.stream) | ||||
|         statement.bindBlob(2, node.IPv6) | ||||
|         statement.bindLong(3, node.port.toLong()) | ||||
|         try { | ||||
|             return statement.simpleQueryForLong() | ||||
|         return try { | ||||
|             statement.simpleQueryForLong() | ||||
|         } catch (e: SQLiteDoneException) { | ||||
|             return null | ||||
|             null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -117,9 +117,8 @@ class AndroidProofOfWorkRepository(private val sql: SqlHelper) : ProofOfWorkRepo | ||||
|  | ||||
|     } | ||||
|  | ||||
|     override fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) { | ||||
|     override fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) = | ||||
|         putObject(ProofOfWorkRepository.Item(objectMessage, nonceTrialsPerByte, extraBytes)) | ||||
|     } | ||||
|  | ||||
|     override fun removeObject(initialHash: ByteArray) { | ||||
|         val db = sql.writableDatabase | ||||
|   | ||||
| @@ -29,57 +29,50 @@ import java.util.* | ||||
|  */ | ||||
| class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME, null, DATABASE_VERSION) { | ||||
|  | ||||
|     override fun onCreate(db: SQLiteDatabase) { | ||||
|         onUpgrade(db, 0, DATABASE_VERSION) | ||||
|     } | ||||
|     override fun onCreate(db: SQLiteDatabase) = onUpgrade(db, 0, DATABASE_VERSION) | ||||
|  | ||||
|     override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { | ||||
|         mapOf( | ||||
|                 0 to { | ||||
|                     executeMigration(db, "V1.0__Create_table_inventory") | ||||
|                     executeMigration(db, "V1.1__Create_table_address") | ||||
|                     executeMigration(db, "V1.2__Create_table_message") | ||||
|                 }, | ||||
|                 1 to { | ||||
|                     // executeMigration(db, "V2.0__Update_table_message"); | ||||
|                     executeMigration(db, "V2.1__Create_table_POW") | ||||
|                 }, | ||||
|                 2 to { | ||||
|                     executeMigration(db, "V3.0__Update_table_address") | ||||
|                 }, | ||||
|                 3 to { | ||||
|                     executeMigration(db, "V3.1__Update_table_POW") | ||||
|                     executeMigration(db, "V3.2__Update_table_message") | ||||
|                 }, | ||||
|                 4 to { | ||||
|                     executeMigration(db, "V3.3__Create_table_node") | ||||
|                 }, | ||||
|                 5 to { | ||||
|                     executeMigration(db, "V3.4__Add_label_outbox") | ||||
|                 }, | ||||
|                 6 to { | ||||
|                     executeMigration(db, "V4.0__Create_table_message_parent") | ||||
|                 }, | ||||
|                 7 to { | ||||
|                     setMissingConversationIds(db) | ||||
|                 } | ||||
|         ).filterKeys { it in oldVersion..(newVersion - 1) }.forEach { (_, v) -> v.invoke() } | ||||
|     } | ||||
|     override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = mapOf( | ||||
|             0 to { | ||||
|                 executeMigration(db, "V1.0__Create_table_inventory") | ||||
|                 executeMigration(db, "V1.1__Create_table_address") | ||||
|                 executeMigration(db, "V1.2__Create_table_message") | ||||
|             }, | ||||
|             1 to { | ||||
|                 // executeMigration(db, "V2.0__Update_table_message"); | ||||
|                 executeMigration(db, "V2.1__Create_table_POW") | ||||
|             }, | ||||
|             2 to { | ||||
|                 executeMigration(db, "V3.0__Update_table_address") | ||||
|             }, | ||||
|             3 to { | ||||
|                 executeMigration(db, "V3.1__Update_table_POW") | ||||
|                 executeMigration(db, "V3.2__Update_table_message") | ||||
|             }, | ||||
|             4 to { | ||||
|                 executeMigration(db, "V3.3__Create_table_node") | ||||
|             }, | ||||
|             5 to { | ||||
|                 executeMigration(db, "V3.4__Add_label_outbox") | ||||
|             }, | ||||
|             6 to { | ||||
|                 executeMigration(db, "V4.0__Create_table_message_parent") | ||||
|             }, | ||||
|             7 to { | ||||
|                 setMissingConversationIds(db) | ||||
|             } | ||||
|     ).filterKeys { it in oldVersion until newVersion }.forEach { (_, v) -> v.invoke() } | ||||
|  | ||||
|     /** | ||||
|      * Set UUIDs for all messages that have no conversation ID | ||||
|      */ | ||||
|     private fun setMissingConversationIds(db: SQLiteDatabase) { | ||||
|         db.query( | ||||
|                 "Message", arrayOf("id"), | ||||
|                 "conversation IS NULL", null, null, null, null | ||||
|         ).use { c -> | ||||
|             while (c.moveToNext()) { | ||||
|                 val id = c.getLong(0) | ||||
|                 setMissingConversationId(id, db) | ||||
|             } | ||||
|     private fun setMissingConversationIds(db: SQLiteDatabase) = db.query( | ||||
|             "Message", arrayOf("id"), | ||||
|             "conversation IS NULL", null, null, null, null | ||||
|     ).use { c -> | ||||
|         while (c.moveToNext()) { | ||||
|             val id = c.getLong(0) | ||||
|             setMissingConversationId(id, db) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private fun setMissingConversationId(id: Long, db: SQLiteDatabase) { | ||||
| @@ -98,7 +91,5 @@ class SqlHelper(private val ctx: Context) : SQLiteOpenHelper(ctx, DATABASE_NAME, | ||||
|         // If you change the database schema, you must increment the database version. | ||||
|         private val DATABASE_VERSION = 7 | ||||
|         val DATABASE_NAME = "jabit.db" | ||||
|  | ||||
|         internal fun join(vararg types: Enum<*>): String = types.joinToString(separator = "', '", prefix = "'", postfix = "'", transform = { it.name }) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -94,7 +94,7 @@ class BitmessageService : Service() { | ||||
|         @Volatile private var running = false | ||||
|  | ||||
|         val isRunning: Boolean | ||||
|             get() = running && Singleton.bitmessageContext?.isRunning() ?: false | ||||
|             get() = running && Singleton.bitmessageContext?.isRunning() == true | ||||
|  | ||||
|         val status: Property | ||||
|             get() = Singleton.bitmessageContext?.status() ?: Property("bitmessage context") | ||||
|   | ||||
| @@ -38,35 +38,45 @@ class ProofOfWorkService : Service() { | ||||
|         notification = ProofOfWorkNotification(this) | ||||
|     } | ||||
|  | ||||
|     override fun onBind(intent: Intent): IBinder? { | ||||
|         return PowBinder(this) | ||||
|     } | ||||
|     override fun onBind(intent: Intent) = PowBinder(this) | ||||
|  | ||||
|     class PowBinder internal constructor(private val service: ProofOfWorkService) : Binder() { | ||||
|         private val notification: ProofOfWorkNotification | ||||
|         private val notification = service.notification | ||||
|  | ||||
|         init { | ||||
|             this.notification = service.notification | ||||
|         } | ||||
|  | ||||
|         fun process(item: PowItem) { | ||||
|             synchronized(queue) { | ||||
|                 service.startService(Intent(service, ProofOfWorkService::class.java)) | ||||
|                 service.startForeground(ONGOING_NOTIFICATION_ID, | ||||
|                     notification.notification) | ||||
|                 if (!calculating) { | ||||
|                     calculating = true | ||||
|                     service.calculateNonce(item) | ||||
|                 } else { | ||||
|                     queue.add(item) | ||||
|                     notification.update(queue.size).show() | ||||
|                 } | ||||
|         fun process(item: PowItem) = synchronized(queue) { | ||||
|             service.startService(Intent(service, ProofOfWorkService::class.java)) | ||||
|             service.startForeground(ONGOING_NOTIFICATION_ID, | ||||
|                 notification.notification) | ||||
|             if (!calculating) { | ||||
|                 calculating = true | ||||
|                 service.calculateNonce(item) | ||||
|             } else { | ||||
|                 queue.add(item) | ||||
|                 notification.update(queue.size).show() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     data class PowItem(val initialHash: ByteArray, val targetValue: ByteArray, val callback: ProofOfWorkEngine.Callback) | ||||
|     data class PowItem(val initialHash: ByteArray, val targetValue: ByteArray, val callback: ProofOfWorkEngine.Callback) { | ||||
|         override fun equals(other: Any?): Boolean { | ||||
|             if (this === other) return true | ||||
|             if (javaClass != other?.javaClass) return false | ||||
|  | ||||
|             other as PowItem | ||||
|  | ||||
|             if (!Arrays.equals(initialHash, other.initialHash)) return false | ||||
|             if (!Arrays.equals(targetValue, other.targetValue)) return false | ||||
|  | ||||
|             return true | ||||
|         } | ||||
|  | ||||
|         override fun hashCode(): Int { | ||||
|             var result = Arrays.hashCode(initialHash) | ||||
|             result = 31 * result + Arrays.hashCode(targetValue) | ||||
|             return result | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun calculateNonce(item: PowItem) { | ||||
|         notification.start(item) | ||||
|   | ||||
| @@ -38,12 +38,10 @@ class ServicePowEngine(private val ctx: Context) : ProofOfWorkEngine { | ||||
|     private var service: PowBinder? = null | ||||
|  | ||||
|     private val connection = object : ServiceConnection { | ||||
|         override fun onServiceConnected(name: ComponentName, service: IBinder) { | ||||
|             synchronized(lock) { | ||||
|                 this@ServicePowEngine.service = service as PowBinder | ||||
|                 while (!queue.isEmpty()) { | ||||
|                     service.process(queue.poll()) | ||||
|                 } | ||||
|         override fun onServiceConnected(name: ComponentName, service: IBinder) = synchronized(lock) { | ||||
|             this@ServicePowEngine.service = service as PowBinder | ||||
|             while (!queue.isEmpty()) { | ||||
|                 service.process(queue.poll()) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -50,8 +50,8 @@ object Singleton { | ||||
|     private var powRepo: AndroidProofOfWorkRepository? = null | ||||
|     private var creatingIdentity: Boolean = false | ||||
|  | ||||
|     fun getBitmessageContext(context: Context): BitmessageContext { | ||||
|         return init({ bitmessageContext }, { bitmessageContext = it }) { | ||||
|     fun getBitmessageContext(context: Context): BitmessageContext = | ||||
|         init({ bitmessageContext }, { bitmessageContext = it }) { | ||||
|             val ctx = context.applicationContext | ||||
|             val sqlHelper = SqlHelper(ctx) | ||||
|             val powRepo = AndroidProofOfWorkRepository(sqlHelper) | ||||
| @@ -75,7 +75,6 @@ object Singleton { | ||||
|                     .doNotSendPubkeyOnIdentityCreation() | ||||
|                     .build() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun getMessageListener(ctx: Context) = init({ messageListener }, { messageListener = it }) { MessageListener(ctx) } | ||||
|  | ||||
| @@ -85,8 +84,8 @@ object Singleton { | ||||
|  | ||||
|     fun getProofOfWorkRepository(ctx: Context) = powRepo ?: getBitmessageContext(ctx).internals.proofOfWorkRepository | ||||
|  | ||||
|     fun getIdentity(ctx: Context): BitmessageAddress? { | ||||
|         return init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc -> | ||||
|     fun getIdentity(ctx: Context): BitmessageAddress? = | ||||
|         init<BitmessageAddress?>(ctx, { identity }, { identity = it }) { bmc -> | ||||
|             val identities = bmc.addresses.getIdentities() | ||||
|             if (identities.isNotEmpty()) { | ||||
|                 identities[0] | ||||
| @@ -112,7 +111,6 @@ object Singleton { | ||||
|                 null | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun setIdentity(identity: BitmessageAddress) { | ||||
|         if (identity.privateKey == null) | ||||
| @@ -122,8 +120,8 @@ object Singleton { | ||||
|  | ||||
|     fun getConversationService(ctx: Context) = init(ctx, { conversationService }, { conversationService = it }) { ConversationService(it.messages) } | ||||
|  | ||||
|     private inline fun <T> init(crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: () -> T): T { | ||||
|         return getter() ?: { | ||||
|     private inline fun <T> init(crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: () -> T): T = | ||||
|         getter() ?: { | ||||
|             synchronized(Singleton) { | ||||
|                 getter() ?: { | ||||
|                     val v = creator() | ||||
| @@ -132,10 +130,9 @@ object Singleton { | ||||
|                 }.invoke() | ||||
|             } | ||||
|         }.invoke() | ||||
|     } | ||||
|  | ||||
|     private inline fun <T> init(ctx: Context, crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: (BitmessageContext) -> T): T { | ||||
|         return getter() ?: { | ||||
|     private inline fun <T> init(ctx: Context, crossinline getter: () -> T?, crossinline setter: (T) -> Unit, crossinline creator: (BitmessageContext) -> T): T = | ||||
|         getter() ?: { | ||||
|             val bmc = getBitmessageContext(ctx) | ||||
|             synchronized(Singleton) { | ||||
|                 getter() ?: { | ||||
| @@ -145,5 +142,4 @@ object Singleton { | ||||
|                 }.invoke() | ||||
|             } | ||||
|         }.invoke() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,12 +7,14 @@ import ch.dissem.apps.abit.util.NetworkUtils | ||||
| import ch.dissem.apps.abit.util.Preferences | ||||
|  | ||||
| /** | ||||
|  * Created by chrigu on 18.08.17. | ||||
|  * Starts the Bitmessage "full node" service if conditions allow it | ||||
|  */ | ||||
| class StartServiceReceiver : BroadcastReceiver() { | ||||
|     override fun onReceive(context: Context, intent: Intent?) { | ||||
|         if (Preferences.isFullNodeActive(context)) { | ||||
|             NetworkUtils.enableNode(context, false) | ||||
|         if (intent?.action == "android.intent.action.BOOT_COMPLETED") { | ||||
|             if (Preferences.isFullNodeActive(context)) { | ||||
|                 NetworkUtils.enableNode(context, false) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,13 +24,11 @@ class StartupNodeOnWifiService : JobService() { | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|     override fun onStopJob(params: JobParameters?): Boolean { | ||||
|         if (Preferences.isWifiOnly(this)) { | ||||
|             // Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes | ||||
|             // depending on Android version. | ||||
|             return Preferences.isFullNodeActive(this) | ||||
|         } else { | ||||
|             return false | ||||
|         } | ||||
|     override fun onStopJob(params: JobParameters?) = if (Preferences.isWifiOnly(this)) { | ||||
|         // Don't actually stop the service, otherwise it will be stopped after 1 or 10 minutes | ||||
|         // depending on Android version. | ||||
|         Preferences.isFullNodeActive(this) | ||||
|     } else { | ||||
|         false | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,6 @@ package ch.dissem.apps.abit.synchronization | ||||
|  | ||||
| import android.app.Service | ||||
| import android.content.Intent | ||||
| import android.os.IBinder | ||||
|  | ||||
| /** | ||||
|  * A bound Service that instantiates the authenticator | ||||
|   | ||||
| @@ -30,14 +30,9 @@ class SyncService : Service() { | ||||
|     /** | ||||
|      * Instantiate the sync adapter object. | ||||
|      */ | ||||
|     override fun onCreate() { | ||||
|         // Create the sync adapter as a singleton. | ||||
|         // Set the sync adapter as syncable | ||||
|         // Disallow parallel syncs | ||||
|         synchronized(syncAdapterLock) { | ||||
|             if (syncAdapter == null) { | ||||
|                 syncAdapter = SyncAdapter(this, true) | ||||
|             } | ||||
|     override fun onCreate() = synchronized(syncAdapterLock) { | ||||
|         if (syncAdapter == null) { | ||||
|             syncAdapter = SyncAdapter(this, true) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -45,13 +40,7 @@ class SyncService : Service() { | ||||
|      * Return an object that allows the system to invoke | ||||
|      * the sync adapter. | ||||
|      */ | ||||
|     override fun onBind(intent: Intent): IBinder? { | ||||
|         // Get the object that allows external processes | ||||
|         // to call onPerformSync(). The object is created | ||||
|         // in the base class code when the SyncAdapter | ||||
|         // constructors call super() | ||||
|         return syncAdapter?.syncAdapterBinder | ||||
|     } | ||||
|     override fun onBind(intent: Intent) = syncAdapter?.syncAdapterBinder | ||||
|  | ||||
|     companion object { | ||||
|         // Storage for an instance of the sync adapter | ||||
|   | ||||
| @@ -47,28 +47,24 @@ object Assets { | ||||
|     } | ||||
|  | ||||
|     @DrawableRes | ||||
|     fun getStatusDrawable(status: Plaintext.Status): Int { | ||||
|         when (status) { | ||||
|             Plaintext.Status.RECEIVED -> return 0 | ||||
|             Plaintext.Status.DRAFT -> return R.drawable.draft | ||||
|             Plaintext.Status.PUBKEY_REQUESTED -> return R.drawable.public_key | ||||
|             Plaintext.Status.DOING_PROOF_OF_WORK -> return R.drawable.ic_notification_proof_of_work | ||||
|             Plaintext.Status.SENT -> return R.drawable.sent | ||||
|             Plaintext.Status.SENT_ACKNOWLEDGED -> return R.drawable.sent_acknowledged | ||||
|             else -> return 0 | ||||
|         } | ||||
|     fun getStatusDrawable(status: Plaintext.Status) = when (status) { | ||||
|         Plaintext.Status.RECEIVED -> 0 | ||||
|         Plaintext.Status.DRAFT -> R.drawable.draft | ||||
|         Plaintext.Status.PUBKEY_REQUESTED -> R.drawable.public_key | ||||
|         Plaintext.Status.DOING_PROOF_OF_WORK -> R.drawable.ic_notification_proof_of_work | ||||
|         Plaintext.Status.SENT -> R.drawable.sent | ||||
|         Plaintext.Status.SENT_ACKNOWLEDGED -> R.drawable.sent_acknowledged | ||||
|         else -> 0 | ||||
|     } | ||||
|  | ||||
|     @StringRes | ||||
|     fun getStatusString(status: Plaintext.Status): Int { | ||||
|         when (status) { | ||||
|             Plaintext.Status.RECEIVED -> return R.string.status_received | ||||
|             Plaintext.Status.DRAFT -> return R.string.status_draft | ||||
|             Plaintext.Status.PUBKEY_REQUESTED -> return R.string.status_public_key | ||||
|             Plaintext.Status.DOING_PROOF_OF_WORK -> return R.string.proof_of_work_title | ||||
|             Plaintext.Status.SENT -> return R.string.status_sent | ||||
|             Plaintext.Status.SENT_ACKNOWLEDGED -> return R.string.status_sent_acknowledged | ||||
|             else -> return 0 | ||||
|         } | ||||
|     fun getStatusString(status: Plaintext.Status) = when (status) { | ||||
|         Plaintext.Status.RECEIVED -> R.string.status_received | ||||
|         Plaintext.Status.DRAFT -> R.string.status_draft | ||||
|         Plaintext.Status.PUBKEY_REQUESTED -> R.string.status_public_key | ||||
|         Plaintext.Status.DOING_PROOF_OF_WORK -> R.string.proof_of_work_title | ||||
|         Plaintext.Status.SENT -> R.string.status_sent | ||||
|         Plaintext.Status.SENT_ACKNOWLEDGED -> R.string.status_sent_acknowledged | ||||
|         else -> 0 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -33,5 +33,5 @@ object Constants { | ||||
|  | ||||
|     const val BITMESSAGE_URL_SCHEMA = "bitmessage:" | ||||
|  | ||||
|     val BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b") | ||||
|     val BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b")!! | ||||
| } | ||||
|   | ||||
| @@ -39,9 +39,7 @@ object Labels { | ||||
|     } | ||||
|  | ||||
|     @ColorInt | ||||
|     fun getColor(label: Label): Int { | ||||
|         return if (label.type == null) { | ||||
|             label.color | ||||
|         } else 0xFF000000.toInt() | ||||
|     } | ||||
|     fun getColor(label: Label) = if (label.type == null) { | ||||
|         label.color | ||||
|     } else 0xFF000000.toInt() | ||||
| } | ||||
|   | ||||
| @@ -12,8 +12,8 @@ import java.math.BigInteger | ||||
| object PowStats { | ||||
|     private val TWO_POW_64: BigInteger = BigInteger.valueOf(2).pow(64) | ||||
|  | ||||
|     var averagePowUnitTime = 0L | ||||
|     var powCount = 0L | ||||
|     private var averagePowUnitTime = 0L | ||||
|     private var powCount = 0L | ||||
|  | ||||
|     fun getExpectedPowTimeInMilliseconds(ctx: Context, target: ByteArray): Long { | ||||
|         if (averagePowUnitTime == 0L) { | ||||
|   | ||||
| @@ -19,7 +19,6 @@ package ch.dissem.apps.abit.util | ||||
| import android.content.Context | ||||
| import android.preference.PreferenceManager | ||||
| import ch.dissem.apps.abit.R | ||||
| import ch.dissem.apps.abit.listener.WifiReceiver | ||||
| import ch.dissem.apps.abit.notification.ErrorNotification | ||||
| import ch.dissem.apps.abit.util.Constants.PREFERENCE_FULL_NODE | ||||
| import ch.dissem.apps.abit.util.Constants.PREFERENCE_REQUEST_ACK | ||||
| @@ -27,7 +26,6 @@ import ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT | ||||
| import ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE | ||||
| import ch.dissem.apps.abit.util.Constants.PREFERENCE_WIFI_ONLY | ||||
| import org.jetbrains.anko.connectivityManager | ||||
| import org.jetbrains.anko.networkStatsManager | ||||
| import org.slf4j.LoggerFactory | ||||
| import java.io.File | ||||
| import java.io.IOException | ||||
|   | ||||
| @@ -24,17 +24,15 @@ import java.util.regex.Pattern | ||||
| object Strings { | ||||
|     private val WHITESPACES = Pattern.compile("\\s+") | ||||
|  | ||||
|     private fun trim(string: CharSequence?, length: Int) = if (string == null) { | ||||
|         "" | ||||
|     } else if (string.length <= length) { | ||||
|         string | ||||
|     } else { | ||||
|         string.subSequence(0, length) | ||||
|     private fun trim(string: CharSequence?, length: Int) = when { | ||||
|         string == null -> "" | ||||
|         string.length <= length -> string | ||||
|         else -> string.subSequence(0, length) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Trim the string to 200 characters and normalizes all whitespaces by replacing any sequence | ||||
|      * of whitespace characters with a single space character. | ||||
|      */ | ||||
|     fun prepareMessageExtract(string: CharSequence?) = WHITESPACES.matcher(trim(string, 200)).replaceAll(" ") | ||||
|     fun prepareMessageExtract(string: CharSequence?): String = WHITESPACES.matcher(trim(string, 200)).replaceAll(" ") | ||||
| } | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 6.9 KiB | 
							
								
								
									
										9
									
								
								app/src/main/res/drawable/header.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/drawable/header.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <gradient | ||||
|         android:angle="315" | ||||
|         android:endColor="#ff6502" | ||||
|         android:centerColor="#ff4f02" | ||||
|         android:startColor="#ff3902" | ||||
|         android:type="linear" /> | ||||
| </shape> | ||||
| @@ -13,8 +13,8 @@ | ||||
|         android:layout_height="16dp" | ||||
|         android:layout_alignParentStart="true" | ||||
|         android:layout_alignParentTop="true" | ||||
|         app:iiv_color="@android:color/black" | ||||
|         app:iiv_icon="cmd-label" /> | ||||
|         app:ico_color="@android:color/black" | ||||
|         app:ico_icon="cmd-label" /> | ||||
|  | ||||
|     <TextView | ||||
|         android:id="@+id/label" | ||||
|   | ||||
| @@ -70,7 +70,7 @@ | ||||
|         android:layout_alignParentEnd="true" | ||||
|         android:layout_centerVertical="true" | ||||
|         android:layout_marginEnd="16dp" | ||||
|         app:iiv_color="@android:color/black" | ||||
|         app:iiv_icon="cmd-rss"/> | ||||
|         app:ico_color="@android:color/black" | ||||
|         app:ico_icon="cmd-rss"/> | ||||
|  | ||||
| </RelativeLayout> | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|       xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <item | ||||
|             android:id="@+id/send" | ||||
|             app:showAsAction="always" | ||||
|             app:showAsAction="ifRoom" | ||||
|             android:icon="@drawable/ic_action_send" | ||||
|             android:title="@string/send"/> | ||||
|     <item | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|     <string name="add_deterministic_address">Deterministic identity</string> | ||||
|     <string name="add_deterministic_address_description">Create or recreate a deterministic identity</string> | ||||
|     <string name="add_chan">Add Chan</string> | ||||
|     <string name="add_chan_description">Create or join a isChan</string> | ||||
|     <string name="add_chan_description">Create or join a Chan</string> | ||||
|     <string name="title_activity_open_bitmessage_link">Import Contact</string> | ||||
|  | ||||
|     <string name="connection_info_1">Stream #%1$d: one connection</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user