diff --git a/res/layout/activity_linked_devices.xml b/res/layout/activity_linked_devices.xml index c816e18295..7f77d77647 100644 --- a/res/layout/activity_linked_devices.xml +++ b/res/layout/activity_linked_devices.xml @@ -11,6 +11,7 @@ android:layout_height="match_parent" /> + + + + + + + \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt index 7b7c56aea2..abc2aca483 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt @@ -2,37 +2,67 @@ package org.thoughtcrime.securesms.loki.redesign.activities import android.os.AsyncTask import android.os.Bundle +import android.support.v4.app.LoaderManager +import android.support.v4.content.Loader +import android.support.v7.app.AlertDialog import android.support.v7.widget.LinearLayoutManager import android.view.Menu import android.view.MenuItem +import android.view.View import kotlinx.android.synthetic.main.activity_linked_devices.* import network.loki.messenger.R import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.devicelist.Device import org.thoughtcrime.securesms.loki.redesign.dialogs.LinkDeviceMasterModeDialog import org.thoughtcrime.securesms.loki.redesign.dialogs.LinkDeviceMasterModeDialogDelegate import org.thoughtcrime.securesms.loki.signAndSendPairingAuthorisationMessage import org.thoughtcrime.securesms.util.Util import org.whispersystems.signalservice.loki.api.PairingAuthorisation -class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LinkDeviceMasterModeDialogDelegate { +class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks>, LinkDeviceMasterModeDialogDelegate { + private val linkedDevicesAdapter = LinkedDevicesAdapter(this) + private var devices = listOf() + set(value) { field = value; linkedDevicesAdapter.devices = value } + // region Lifecycle constructor() : super() override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) setContentView(R.layout.activity_linked_devices) supportActionBar!!.title = "Linked Devices" -// val homeAdapter = LinkedDevicesAdapter(this, cursor) -// recyclerView.adapter = homeAdapter + recyclerView.adapter = linkedDevicesAdapter recyclerView.layoutManager = LinearLayoutManager(this) linkDeviceButton.setOnClickListener { linkDevice() } + LoaderManager.getInstance(this).initLoader(0, null, this) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_linked_devices, menu) return true } + // endregion + // region Updating + override fun onCreateLoader(id: Int, bundle: Bundle?): Loader> { + return LinkedDevicesLoader(this) + } + + override fun onLoadFinished(loader: Loader>, devices: List?) { + update(devices ?: listOf()) + } + + override fun onLoaderReset(loader: Loader>) { + update(listOf()) + } + + private fun update(devices: List) { + this.devices = devices + emptyStateContainer.visibility = if (devices.isEmpty()) View.VISIBLE else View.GONE + } + // endregion + + // region Interaction override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId when (id) { @@ -43,9 +73,17 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LinkDeviceMas } private fun linkDevice() { - val linkDeviceDialog = LinkDeviceMasterModeDialog() - linkDeviceDialog.delegate = this - linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") + if (devices.isEmpty()) { + val linkDeviceDialog = LinkDeviceMasterModeDialog() + linkDeviceDialog.delegate = this + linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") + } else { + val builder = AlertDialog.Builder(this) + builder.setTitle("Multi Device Limit Reached") + builder.setMessage("It's currently not allowed to link more than one device.") + builder.setPositiveButton("OK", { dialog, _ -> dialog.dismiss() }) + builder.create().show() + } } override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) { @@ -60,4 +98,5 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LinkDeviceMas override fun onDeviceLinkCanceled() { // Do nothing } + // endregion } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesAdapter.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesAdapter.kt new file mode 100644 index 0000000000..646c7cd41d --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesAdapter.kt @@ -0,0 +1,28 @@ +package org.thoughtcrime.securesms.loki.redesign.activities + +import android.content.Context +import android.support.v7.widget.RecyclerView +import android.view.ViewGroup +import org.thoughtcrime.securesms.devicelist.Device +import org.thoughtcrime.securesms.loki.redesign.views.DeviceView + +class LinkedDevicesAdapter(private val context: Context) : RecyclerView.Adapter() { + var devices = listOf() + set(value) { field = value; notifyDataSetChanged() } + + class ViewHolder(val view: DeviceView) : RecyclerView.ViewHolder(view) + + override fun getItemCount(): Int { + return devices.size + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = DeviceView(context) + return ViewHolder(view) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + val device = devices[position] + viewHolder.view.bind(device) + } +} diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt new file mode 100644 index 0000000000..7edca96d3d --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt @@ -0,0 +1,33 @@ +package org.thoughtcrime.securesms.loki.redesign.activities + +import android.content.Context +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.devicelist.Device +import org.thoughtcrime.securesms.loki.MnemonicUtilities +import org.thoughtcrime.securesms.util.AsyncLoader +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.loki.api.LokiStorageAPI +import org.whispersystems.signalservice.loki.crypto.MnemonicCodec +import java.io.File + +class LinkedDevicesLoader(context: Context) : AsyncLoader>(context) { + + private val mnemonicCodec by lazy { + val languageFileDirectory = File(context.applicationInfo.dataDir) + MnemonicCodec(languageFileDirectory) + } + + override fun loadInBackground(): List? { + try { + val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) + val slaveDeviceHexEncodedPublicKeys = LokiStorageAPI.shared.getSecondaryDevicePublicKeys(userHexEncodedPublicKey).get() + return slaveDeviceHexEncodedPublicKeys.map { hexEncodedPublicKey -> + val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey) + val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey) + Device(hexEncodedPublicKey, shortID, name) + }.sortedBy { it.name } + } catch (e: Exception) { + return null + } + } +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/DeviceView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/DeviceView.kt new file mode 100644 index 0000000000..80ee8a4d2a --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/redesign/views/DeviceView.kt @@ -0,0 +1,44 @@ +package org.thoughtcrime.securesms.loki.redesign.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import kotlinx.android.synthetic.main.view_device.view.* +import network.loki.messenger.R +import org.thoughtcrime.securesms.devicelist.Device + +class DeviceView : LinearLayout { + var device: Device? = null + + // region Lifecycle + constructor(context: Context) : super(context) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setUpViewHierarchy() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + setUpViewHierarchy() + } + + private fun setUpViewHierarchy() { + val inflater = context.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val contentView = inflater.inflate(R.layout.view_device, null) + addView(contentView) + } + // endregion + + // region Updating + fun bind(device: Device) { + titleTextView.text = if (!device.name.isNullOrBlank()) device.name else "Unnamed Device" + subtitleTextView.text = device.shortId + } + // endregion +} \ No newline at end of file