-
Notifications
You must be signed in to change notification settings - Fork 104
Copy and reveal credentials #124
Changes from all commits
f2a2beb
19ad225
acb1a05
d335f17
0af60e5
3fdc0e3
60aeada
3f2de55
d472c3f
49b3d66
ce5a1e3
6d6156a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package mozilla.lockbox.action | ||
|
||
import mozilla.lockbox.flux.Action | ||
|
||
sealed class ClipboardAction : Action { | ||
data class CopyUsername(val username: String) : ClipboardAction() | ||
data class CopyPassword(val password: String) : ClipboardAction() | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,11 @@ | |
|
||
package mozilla.lockbox.presenter | ||
|
||
import android.support.annotation.StringRes | ||
import io.reactivex.Observable | ||
import io.reactivex.rxkotlin.addTo | ||
import mozilla.lockbox.R | ||
import mozilla.lockbox.action.ClipboardAction | ||
import mozilla.lockbox.flux.Dispatcher | ||
import mozilla.lockbox.flux.Presenter | ||
import mozilla.lockbox.model.ItemDetailViewModel | ||
|
@@ -16,13 +20,60 @@ import mozilla.lockbox.store.DataStore | |
interface ItemDetailView { | ||
var itemId: String? | ||
fun updateItem(item: ItemDetailViewModel) | ||
fun showToastNotification(@StringRes strId: Int) | ||
|
||
val usernameCopyClicks: Observable<Unit> | ||
val passwordCopyClicks: Observable<Unit> | ||
val togglePasswordClicks: Observable<Unit> | ||
|
||
var isPasswordVisible: Boolean | ||
} | ||
|
||
class ItemDetailPresenter( | ||
private val view: ItemDetailView, | ||
private val dispatcher: Dispatcher = Dispatcher.shared, | ||
private val dataStore: DataStore = DataStore.shared | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: whitespace. |
||
) : Presenter() { | ||
|
||
override fun onViewReady() { | ||
this.view.usernameCopyClicks | ||
.subscribe { | ||
view.itemId?.let { | ||
dataStore.get(it) | ||
.subscribe { | ||
if (it!!.username!!.isNotEmpty()) { | ||
dispatcher.dispatch(ClipboardAction.CopyUsername(it.username!!)) | ||
view.showToastNotification(R.string.toast_username_copied) | ||
} | ||
} | ||
.addTo(compositeDisposable) | ||
} | ||
} | ||
.addTo(compositeDisposable) | ||
|
||
this.view.passwordCopyClicks | ||
.subscribe { | ||
view.itemId?.let { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract this statement into a method, parameterized for At some point, it would be good to not hit the database each time, and stash the |
||
dataStore.get(it) | ||
.subscribe { | ||
if (it!!.password.isNotEmpty()) { | ||
dispatcher.dispatch(ClipboardAction.CopyPassword(it.password)) | ||
view.showToastNotification(R.string.toast_password_copied) | ||
} | ||
} | ||
.addTo(compositeDisposable) | ||
} | ||
} | ||
.addTo(compositeDisposable) | ||
|
||
this.view.togglePasswordClicks | ||
.subscribe { | ||
view.isPasswordVisible = view.isPasswordVisible.not() | ||
} | ||
.addTo(compositeDisposable) | ||
} | ||
|
||
override fun onResume() { | ||
super.onResume() | ||
val itemId = view.itemId ?: return | ||
|
@@ -32,5 +83,7 @@ class ItemDetailPresenter( | |
} | ||
.subscribe(view::updateItem) | ||
.addTo(compositeDisposable) | ||
|
||
view.isPasswordVisible = false | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package mozilla.lockbox.store | ||
|
||
import android.content.ClipData | ||
import android.content.ClipboardManager | ||
import io.reactivex.disposables.CompositeDisposable | ||
import io.reactivex.rxkotlin.addTo | ||
import mozilla.lockbox.action.ClipboardAction | ||
import mozilla.lockbox.extensions.filterByType | ||
import mozilla.lockbox.flux.Dispatcher | ||
|
||
open class ClipboardStore( | ||
val dispatcher: Dispatcher = Dispatcher.shared | ||
) { | ||
internal val compositeDisposable = CompositeDisposable() | ||
companion object { | ||
val shared = ClipboardStore() | ||
} | ||
|
||
private lateinit var clipboardManager: ClipboardManager | ||
|
||
init { | ||
dispatcher.register | ||
.filterByType(ClipboardAction::class.java) | ||
.subscribe { | ||
// unpack the action, including adding new Clips to the Clipboard. | ||
when (it) { | ||
is ClipboardAction.CopyUsername -> { | ||
addToClipboard("username", it.username) | ||
} | ||
is ClipboardAction.CopyPassword -> { | ||
addToClipboard("password", it.password) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good. Consider making a |
||
} | ||
} | ||
.addTo(compositeDisposable) | ||
} | ||
mihainisipeanusv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
fun apply(manager: ClipboardManager) { | ||
clipboardManager = manager | ||
} | ||
|
||
mihainisipeanusv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private fun addToClipboard(label: String, str: String) { | ||
val clip = ClipData.newPlainText(label, str) | ||
clipboardManager.primaryClip = clip | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,14 +7,19 @@ | |
package mozilla.lockbox.view | ||
|
||
import android.os.Bundle | ||
import android.support.annotation.StringRes | ||
import android.text.method.PasswordTransformationMethod | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.EditText | ||
import android.widget.TextView | ||
import android.widget.Toast | ||
import com.jakewharton.rxbinding2.view.clicks | ||
import io.reactivex.Observable | ||
import kotlinx.android.synthetic.main.fragment_item_detail.* | ||
import kotlinx.android.synthetic.main.fragment_item_detail.view.* | ||
import kotlinx.android.synthetic.main.include_backable.* | ||
import kotlinx.android.synthetic.main.include_backable.view.* | ||
import mozilla.lockbox.R | ||
import mozilla.lockbox.model.ItemDetailViewModel | ||
import mozilla.lockbox.presenter.ItemDetailPresenter | ||
|
@@ -35,6 +40,27 @@ class ItemDetailFragment : BackableFragment(), ItemDetailView { | |
return view | ||
} | ||
|
||
override val usernameCopyClicks: Observable<Unit> | ||
get() = view!!.btnUsernameCopy.clicks() | ||
|
||
override val passwordCopyClicks: Observable<Unit> | ||
get() = view!!.btnPasswordCopy.clicks() | ||
|
||
override val togglePasswordClicks: Observable<Unit> | ||
get() = view!!.btnPasswordToggle.clicks() | ||
|
||
override var isPasswordVisible: Boolean = false | ||
set(value) { | ||
field = value | ||
if (value) { | ||
inputPassword.transformationMethod = null | ||
btnPasswordToggle.setImageResource(R.drawable.ic_icon_hide) | ||
} else { | ||
inputPassword.transformationMethod = PasswordTransformationMethod.getInstance() | ||
btnPasswordToggle.setImageResource(R.drawable.ic_icon_show) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. |
||
|
||
override fun updateItem(item: ItemDetailViewModel) { | ||
toolbar.title = item.title | ||
|
||
|
@@ -50,6 +76,10 @@ class ItemDetailFragment : BackableFragment(), ItemDetailView { | |
inputUsername.setText(item.username, TextView.BufferType.NORMAL) | ||
inputPassword.setText(item.password, TextView.BufferType.NORMAL) | ||
} | ||
|
||
override fun showToastNotification(@StringRes strId: Int) { | ||
Toast.makeText(activity, getString(strId), Toast.LENGTH_SHORT).show() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
} | ||
|
||
var EditText.readOnly: Boolean | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:width="18dp" | ||
android:height="18dp" | ||
android:viewportWidth="18" | ||
android:viewportHeight="18"> | ||
|
||
<path | ||
android:pathData="M16.545375,9.329625 L13.170375,5.954625 C12.9594457,5.74363195 12.6733442,5.62506372 12.375,5.625 L11.25,5.625 L11.25,4.5 C11.2499363,4.20165583 11.131368,3.91555432 10.920375,3.704625 L7.545375,0.329625 C7.33444568,0.118631953 7.04834417,6.37170812e-05 6.75,0 L3.375,0 C2.13235931,-7.6089797e-17 1.125,1.00735931 1.125,2.25 L1.125,10.125 C1.125,11.3676407 2.13235931,12.375 3.375,12.375 L6.75,12.375 L6.75,15.75 C6.75,16.9926407 7.75735931,18 9,18 L14.625,18 C15.8676407,18 16.875,16.9926407 16.875,15.75 L16.875,10.125 C16.8749363,9.82665583 16.756368,9.54055432 16.545375,9.329625 Z M14.15925,10.125 L12.375,10.125 L12.375,8.34075 L14.15925,10.125 Z M8.53425,4.5 L6.75,4.5 L6.75,2.71575 L8.53425,4.5 Z M6.75,7.875 L6.75,10.125 L3.375,10.125 L3.375,2.25 L5.625,2.25 L5.625,5.0625 C5.625,5.37316017 5.87683983,5.625 6.1875,5.625 L9,5.625 C7.75735931,5.625 6.75,6.63235931 6.75,7.875 Z M9,15.75 L9,7.875 L11.25,7.875 L11.25,10.6875 C11.25,10.9981602 11.5018398,11.25 11.8125,11.25 L14.625,11.25 L14.625,15.75 L9,15.75 Z" | ||
android:strokeWidth="1" | ||
android:fillColor="#4A4A4F" | ||
android:fillType="evenOdd" | ||
android:strokeColor="#00000000"/> | ||
</vector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:width="18dp" | ||
android:height="18dp" | ||
android:viewportWidth="18" | ||
android:viewportHeight="18"> | ||
<path | ||
android:pathData="M13.5001378,7.875 L9.00013784,12.375 C11.4854192,12.375 13.5001378,10.3602814 13.5001378,7.875 Z M17.9495128,8.6625 C18.018604,8.88219601 18.018604,9.11780399 17.9495128,9.3375 C16.6702783,13.1878327 13.0573283,15.7766216 9.00013784,15.75 C8.01052596,15.7487738 7.02713331,15.5935412 6.08526284,15.289875 L7.95388784,13.42125 C8.30030917,13.4724565 8.64995453,13.4987739 9.00013784,13.5 C11.9447269,13.5263468 14.6019241,11.7378916 15.6860128,9 C15.3985795,8.24211122 14.9720031,7.54463215 14.4282628,6.9435 L16.0145128,5.35725 C16.8945093,6.30345955 17.5551757,7.43196981 17.9495128,8.6625 Z M16.5455128,1.454625 C16.9846925,1.89393733 16.9846925,2.60606267 16.5455128,3.045375 L3.04551284,16.545375 C2.76305406,16.8378262 2.34477521,16.9551142 1.9514421,16.8521593 C1.55810899,16.7492044 1.25093341,16.4420289 1.14797853,16.0486957 C1.04502366,15.6553626 1.16231168,15.2370838 1.45476284,14.954625 L2.89926284,13.50675 C1.56431174,12.4209648 0.574503528,10.9698716 0.0507628361,9.33075 C-0.0169209454,9.11314152 -0.0169209454,8.88010848 0.0507628361,8.6625 C0.915942709,6.08251245 2.85677943,4.00409597 5.37156688,2.96452103 C7.88635433,1.92494608 10.728255,2.02625156 13.1626378,3.24225 L14.9547628,1.454625 C15.3940752,1.01544532 16.1062005,1.01544532 16.5455128,1.454625 Z M9.56263784,5.625 C8.63065732,5.625 7.87513784,6.38051948 7.87513784,7.3125 C7.87809346,7.64390747 7.98123585,7.96666865 8.17101284,8.238375 L10.4885128,5.920875 C10.2168065,5.73109801 9.89404531,5.62795562 9.56263784,5.625 Z M2.31426284,9 C2.75531187,10.1563686 3.50944026,11.1671966 4.49226284,11.919375 L5.60151284,10.810125 C4.33541627,9.36765229 4.14366735,7.27340871 5.12676284,5.625 C3.84259111,6.40890234 2.85374236,7.59552083 2.31426284,9 Z" | ||
android:strokeWidth="1" | ||
android:fillColor="#4A4A4F" | ||
android:fillType="evenOdd" | ||
android:strokeColor="#00000000"/> | ||
</vector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:width="18dp" | ||
android:height="18dp" | ||
android:viewportWidth="18" | ||
android:viewportHeight="18"> | ||
<path | ||
android:pathData="M17.9495128,6.6625 C16.6702783,2.81216726 13.0573283,0.223378405 9.00013784,0.25 C4.94294738,0.223378405 1.32999732,2.81216726 0.0507628361,6.6625 C-0.0169209454,6.88010848 -0.0169209454,7.11314152 0.0507628361,7.33075 C1.32764302,11.1837517 4.94114947,13.7756647 9.00013784,13.75 C13.0573283,13.7766216 16.6702783,11.1878327 17.9495128,7.3375 C18.018604,7.11780399 18.018604,6.88219601 17.9495128,6.6625 Z M9.56263784,3.625 C10.4946184,3.625 11.2501378,4.38051948 11.2501378,5.3125 C11.2501378,6.24448052 10.4946184,7 9.56263784,7 C8.63065732,7 7.87513784,6.24448052 7.87513784,5.3125 C7.87513784,4.38051948 8.63065732,3.625 9.56263784,3.625 Z M9.00013784,11.5 C6.05554876,11.5263468 3.39835155,9.73789164 2.31426284,7 C2.85374236,5.59552083 3.84259111,4.40890234 5.12676284,3.625 C4.72125214,4.30581733 4.50492353,5.08258075 4.50013784,5.875 C4.50013787,8.36028135 6.51485649,10.3749999 9.00013784,10.3749999 C11.4854192,10.3749999 13.5001378,8.36028135 13.5001378,5.875 C13.4960332,5.08278883 13.2804853,4.30603972 12.8757628,3.625 C14.1599346,4.40890234 15.1487833,5.59552083 15.6882628,7 C14.603878,9.73866412 11.9455457,11.5272813 9.00013784,11.5 Z" | ||
android:strokeWidth="1" | ||
android:fillColor="#4A4A4F" | ||
android:fillType="evenOdd" | ||
android:strokeColor="#00000000"/> | ||
</vector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider
CopyUsername
,CopyPassword
.