Add Activity to view the Git commit log (#1056)
This commit is contained in:
parent
88b1de2b50
commit
0f0d1994e5
18 changed files with 342 additions and 52 deletions
|
@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
- Allow sorting by recently used
|
- Allow sorting by recently used
|
||||||
- Add [Bromite](https://www.bromite.org/) and [Ungoogled Chromium](https://git.droidware.info/wchen342/ungoogled-chromium-android) to supported browsers list for Autofill
|
- Add [Bromite](https://www.bromite.org/) and [Ungoogled Chromium](https://git.droidware.info/wchen342/ungoogled-chromium-android) to supported browsers list for Autofill
|
||||||
|
- Add ability to view the Git commit log
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -17,7 +18,6 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Password creation UI will scroll if it does not fit on the screen
|
|
||||||
- Git server protocol and authentication mode are only updated when explicitly saved
|
- Git server protocol and authentication mode are only updated when explicitly saved
|
||||||
- Remember HTTPS password during a sync operation
|
- Remember HTTPS password during a sync operation
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,10 @@
|
||||||
android:label="@string/title_activity_git_config"
|
android:label="@string/title_activity_git_config"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".git.log.GitLogActivity"
|
||||||
|
android:label="@string/title_activity_git_log" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".UserPreference"
|
android:name=".UserPreference"
|
||||||
android:label="@string/action_settings"
|
android:label="@string/action_settings"
|
||||||
|
|
|
@ -48,6 +48,7 @@ import com.zeapo.pwdstore.crypto.BasePgpActivity.Companion.getLongName
|
||||||
import com.zeapo.pwdstore.crypto.DecryptActivity
|
import com.zeapo.pwdstore.crypto.DecryptActivity
|
||||||
import com.zeapo.pwdstore.crypto.PasswordCreationActivity
|
import com.zeapo.pwdstore.crypto.PasswordCreationActivity
|
||||||
import com.zeapo.pwdstore.git.BaseGitActivity
|
import com.zeapo.pwdstore.git.BaseGitActivity
|
||||||
|
import com.zeapo.pwdstore.git.log.GitLogActivity
|
||||||
import com.zeapo.pwdstore.git.GitOperationActivity
|
import com.zeapo.pwdstore.git.GitOperationActivity
|
||||||
import com.zeapo.pwdstore.git.GitServerConfigActivity
|
import com.zeapo.pwdstore.git.GitServerConfigActivity
|
||||||
import com.zeapo.pwdstore.git.config.AuthMode
|
import com.zeapo.pwdstore.git.config.AuthMode
|
||||||
|
|
|
@ -4,20 +4,24 @@
|
||||||
*/
|
*/
|
||||||
package com.zeapo.pwdstore.git
|
package com.zeapo.pwdstore.git
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import androidx.core.os.postDelayed
|
import androidx.core.os.postDelayed
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.ajalt.timberkt.e
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.zeapo.pwdstore.R
|
import com.zeapo.pwdstore.R
|
||||||
import com.zeapo.pwdstore.databinding.ActivityGitConfigBinding
|
import com.zeapo.pwdstore.databinding.ActivityGitConfigBinding
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
|
import com.zeapo.pwdstore.git.log.GitLogActivity
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository
|
import com.zeapo.pwdstore.utils.PasswordRepository
|
||||||
import com.zeapo.pwdstore.utils.viewBinding
|
import com.zeapo.pwdstore.utils.viewBinding
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.eclipse.jgit.lib.Constants
|
import org.eclipse.jgit.lib.Constants
|
||||||
|
import org.eclipse.jgit.lib.Repository
|
||||||
|
|
||||||
class GitConfigActivity : BaseGitActivity() {
|
class GitConfigActivity : BaseGitActivity() {
|
||||||
|
|
||||||
|
@ -33,23 +37,7 @@ class GitConfigActivity : BaseGitActivity() {
|
||||||
else
|
else
|
||||||
binding.gitUserName.setText(GitSettings.authorName)
|
binding.gitUserName.setText(GitSettings.authorName)
|
||||||
binding.gitUserEmail.setText(GitSettings.authorEmail)
|
binding.gitUserEmail.setText(GitSettings.authorEmail)
|
||||||
val repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory())
|
setupTools()
|
||||||
if (repo != null) {
|
|
||||||
try {
|
|
||||||
val objectId = repo.resolve(Constants.HEAD)
|
|
||||||
val ref = repo.getRef("refs/heads/${GitSettings.branch}")
|
|
||||||
val head = if (ref.objectId.equals(objectId)) ref.name else "DETACHED"
|
|
||||||
binding.gitCommitHash.text = String.format("%s (%s)", objectId.abbreviate(8).name(), head)
|
|
||||||
|
|
||||||
// enable the abort button only if we're rebasing
|
|
||||||
val isRebasing = repo.repositoryState.isRebasing
|
|
||||||
binding.gitAbortRebase.isEnabled = isRebasing
|
|
||||||
binding.gitAbortRebase.alpha = if (isRebasing) 1.0f else 0.5f
|
|
||||||
} catch (ignored: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.gitAbortRebase.setOnClickListener { lifecycleScope.launch { launchGitOperation(BREAK_OUT_OF_DETACHED) } }
|
|
||||||
binding.gitResetToRemote.setOnClickListener { lifecycleScope.launch { launchGitOperation(REQUEST_RESET) } }
|
|
||||||
binding.saveButton.setOnClickListener {
|
binding.saveButton.setOnClickListener {
|
||||||
val email = binding.gitUserEmail.text.toString().trim()
|
val email = binding.gitUserEmail.text.toString().trim()
|
||||||
val name = binding.gitUserName.text.toString().trim()
|
val name = binding.gitUserName.text.toString().trim()
|
||||||
|
@ -66,4 +54,50 @@ class GitConfigActivity : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the UI components of the tools section.
|
||||||
|
*/
|
||||||
|
private fun setupTools() {
|
||||||
|
val repo = PasswordRepository.getRepository(null)
|
||||||
|
if (repo != null) {
|
||||||
|
binding.gitHeadStatus.text = headStatusMsg(repo)
|
||||||
|
// enable the abort button only if we're rebasing
|
||||||
|
val isRebasing = repo.repositoryState.isRebasing
|
||||||
|
binding.gitAbortRebase.isEnabled = isRebasing
|
||||||
|
binding.gitAbortRebase.alpha = if (isRebasing) 1.0f else 0.5f
|
||||||
|
}
|
||||||
|
binding.gitLog.setOnClickListener {
|
||||||
|
try {
|
||||||
|
intent = Intent(this, GitLogActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
e(ex) { "Failed to start GitLogActivity" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.gitAbortRebase.setOnClickListener { lifecycleScope.launch { launchGitOperation(BREAK_OUT_OF_DETACHED) } }
|
||||||
|
binding.gitResetToRemote.setOnClickListener { lifecycleScope.launch { launchGitOperation(REQUEST_RESET) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a user-friendly message about the current state of HEAD.
|
||||||
|
*
|
||||||
|
* The state is recognized to be either pointing to a branch or detached.
|
||||||
|
*/
|
||||||
|
private fun headStatusMsg(repo: Repository): String {
|
||||||
|
return try {
|
||||||
|
val headRef = repo.getRef(Constants.HEAD)
|
||||||
|
if (headRef.isSymbolic) {
|
||||||
|
val branchName = headRef.target.name
|
||||||
|
val shortBranchName = Repository.shortenRefName(branchName)
|
||||||
|
getString(R.string.git_head_on_branch, shortBranchName)
|
||||||
|
} else {
|
||||||
|
val commitHash = headRef.objectId.abbreviate(8).name()
|
||||||
|
getString(R.string.git_head_detached, commitHash)
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
e(ex) { "Error getting HEAD reference" }
|
||||||
|
getString(R.string.git_head_missing)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
18
app/src/main/java/com/zeapo/pwdstore/git/log/GitCommit.kt
Normal file
18
app/src/main/java/com/zeapo/pwdstore/git/log/GitCommit.kt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zeapo.pwdstore.git.log
|
||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic information about a git commit.
|
||||||
|
*
|
||||||
|
* @property hash full-length hash of the commit object.
|
||||||
|
* @property shortMessage the commit's short message (i.e. title line).
|
||||||
|
* @property authorName name of the commit's author without email address.
|
||||||
|
* @property time time when the commit was created.
|
||||||
|
*/
|
||||||
|
data class GitCommit(val hash: String, val shortMessage: String, val authorName: String, val time: Date)
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zeapo.pwdstore.git.log
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.zeapo.pwdstore.databinding.ActivityGitLogBinding
|
||||||
|
import com.zeapo.pwdstore.git.BaseGitActivity
|
||||||
|
import com.zeapo.pwdstore.utils.viewBinding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the repository's git commits in git-log fashion.
|
||||||
|
*
|
||||||
|
* It provides basic information about each commit by way of a non-interactive RecyclerView.
|
||||||
|
*/
|
||||||
|
class GitLogActivity : BaseGitActivity() {
|
||||||
|
|
||||||
|
private val binding by viewBinding(ActivityGitLogBinding::inflate)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(binding.root)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
createRecyclerView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRecyclerView() {
|
||||||
|
binding.gitLogRecyclerView.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL))
|
||||||
|
adapter = GitLogAdapter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zeapo.pwdstore.git.log
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.ajalt.timberkt.e
|
||||||
|
import com.zeapo.pwdstore.databinding.GitLogRowLayoutBinding
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
private fun shortHash(hash: String): String {
|
||||||
|
return hash.substring(0 until 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stringFrom(date: Date): String {
|
||||||
|
return DateFormat.getDateTimeInstance().format(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GitLogActivity
|
||||||
|
*/
|
||||||
|
class GitLogAdapter : RecyclerView.Adapter<GitLogAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
private val model = GitLogModel()
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
val binding = GitLogRowLayoutBinding.inflate(inflater, parent, false)
|
||||||
|
return ViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
||||||
|
val commit = model.get(position)
|
||||||
|
if (commit == null) {
|
||||||
|
e { "There is no git commit for view holder at position $position." }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewHolder.bind(commit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = model.size
|
||||||
|
|
||||||
|
class ViewHolder(private val binding: GitLogRowLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
fun bind(commit: GitCommit) = with(binding) {
|
||||||
|
gitLogRowMessage.text = commit.shortMessage
|
||||||
|
gitLogRowHash.text = shortHash(commit.hash)
|
||||||
|
gitLogRowTime.text = stringFrom(commit.time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
app/src/main/java/com/zeapo/pwdstore/git/log/GitLogModel.kt
Normal file
53
app/src/main/java/com/zeapo/pwdstore/git/log/GitLogModel.kt
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zeapo.pwdstore.git.log
|
||||||
|
|
||||||
|
import com.github.ajalt.timberkt.e
|
||||||
|
import com.zeapo.pwdstore.utils.PasswordRepository
|
||||||
|
import com.zeapo.pwdstore.utils.hash
|
||||||
|
import com.zeapo.pwdstore.utils.time
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
|
private fun commits(): Iterable<RevCommit> {
|
||||||
|
val repo = PasswordRepository.getRepository(null)
|
||||||
|
if (repo == null) {
|
||||||
|
e { "Could not access git repository" }
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
Git(repo).log().call()
|
||||||
|
} catch (exc: Exception) {
|
||||||
|
e(exc) { "Failed to obtain git commits" }
|
||||||
|
listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides [GitCommit]s from a git-log of the password git repository.
|
||||||
|
*
|
||||||
|
* All commits are acquired on the first request to this object.
|
||||||
|
*/
|
||||||
|
class GitLogModel {
|
||||||
|
|
||||||
|
// All commits are acquired here at once. Acquiring the commits in batches would not have been
|
||||||
|
// entirely sensible because the amount of computation required to obtain commit number n from
|
||||||
|
// the log includes the amount of computation required to obtain commit number n-1 from the log.
|
||||||
|
// This is because the commit graph is walked from HEAD to the last commit to obtain.
|
||||||
|
// Additionally, tests with 1000 commits in the log have not produced a significant delay in the
|
||||||
|
// user experience.
|
||||||
|
private val cache: MutableList<GitCommit> by lazy {
|
||||||
|
commits().map {
|
||||||
|
GitCommit(it.hash, it.shortMessage, it.authorIdent.name, it.time)
|
||||||
|
}.toMutableList()
|
||||||
|
}
|
||||||
|
val size = cache.size
|
||||||
|
|
||||||
|
fun get(index: Int): GitCommit? {
|
||||||
|
if (index >= size) e { "Cannot get git commit with index $index. There are only $size." }
|
||||||
|
return cache.getOrNull(index)
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,9 @@ import com.zeapo.pwdstore.git.GitCommandExecutor
|
||||||
import com.zeapo.pwdstore.git.operation.GitOperation
|
import com.zeapo.pwdstore.git.operation.GitOperation
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
|
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Date
|
||||||
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
|
const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
|
||||||
|
|
||||||
|
@ -162,3 +165,23 @@ val Context.autofillManager: AutofillManager?
|
||||||
fun File.isInsideRepository(): Boolean {
|
fun File.isInsideRepository(): Boolean {
|
||||||
return canonicalPath.contains(getRepositoryDirectory().canonicalPath)
|
return canonicalPath.contains(getRepositoryDirectory().canonicalPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique SHA-1 hash of this commit as hexadecimal string.
|
||||||
|
*
|
||||||
|
* @see RevCommit.id
|
||||||
|
*/
|
||||||
|
val RevCommit.hash: String
|
||||||
|
get() = ObjectId.toString(id)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time this commit was made with second precision.
|
||||||
|
*
|
||||||
|
* @see RevCommit.commitTime
|
||||||
|
*/
|
||||||
|
val RevCommit.time: Date
|
||||||
|
get() {
|
||||||
|
val epochSeconds = commitTime.toLong()
|
||||||
|
val epochMilliseconds = epochSeconds * 1000
|
||||||
|
return Date(epochMilliseconds)
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
android:id="@+id/username_input_layout"
|
android:id="@+id/username_input_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:hint="@string/git_user_name_hint"
|
android:hint="@string/git_user_name_hint"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
android:id="@+id/email_input_layout"
|
android:id="@+id/email_input_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:hint="@string/git_user_email"
|
android:hint="@string/git_user_email"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/username_input_layout">
|
app:layout_constraintTop_toBottomOf="@id/username_input_layout">
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
android:id="@+id/save_button"
|
android:id="@+id/save_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:text="@string/crypto_save"
|
android:text="@string/crypto_save"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/email_input_layout" />
|
app:layout_constraintTop_toBottomOf="@id/email_input_layout" />
|
||||||
|
@ -62,45 +62,43 @@
|
||||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:text="@string/hackish_tools"
|
android:text="@string/git_tools"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/save_button" />
|
app:layout_constraintTop_toBottomOf="@id/save_button" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/commit_hash_label"
|
android:id="@+id/git_head_status"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:text="@string/commit_hash"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/git_tools_title" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/git_commit_hash"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/commit_hash_label"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/git_tools_title"
|
app:layout_constraintTop_toBottomOf="@id/git_tools_title"
|
||||||
tools:text="HASH" />
|
tools:text="HEAD status" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/git_log"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/normal_margin"
|
||||||
|
android:text="@string/git_log"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/git_head_status" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/git_abort_rebase"
|
android:id="@+id/git_abort_rebase"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:text="@string/abort_rebase"
|
android:text="@string/abort_rebase"
|
||||||
app:layout_constraintTop_toBottomOf="@id/commit_hash_label" />
|
app:layout_constraintTop_toBottomOf="@+id/git_log" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/git_reset_to_remote"
|
android:id="@+id/git_reset_to_remote"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="@dimen/normal_margin"
|
||||||
android:text="@string/reset_to_remote"
|
android:text="@string/reset_to_remote"
|
||||||
app:layout_constraintTop_toBottomOf="@id/git_abort_rebase" />
|
app:layout_constraintTop_toBottomOf="@id/git_abort_rebase" />
|
||||||
|
|
||||||
|
|
22
app/src/main/res/layout/activity_git_log.xml
Normal file
22
app/src/main/res/layout/activity_git_log.xml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
~ SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/git_log_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:context="com.zeapo.pwdstore.git.log.GitLogActivity"
|
||||||
|
tools:listitem="@layout/git_log_row_layout" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
45
app/src/main/res/layout/git_log_row_layout.xml
Normal file
45
app/src/main/res/layout/git_log_row_layout.xml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
~ SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
-->
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingVertical="@dimen/activity_horizontal_margin">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/git_log_row_message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/normal_margin"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/git_log_row_hash"
|
||||||
|
tools:text="Commit message" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/git_log_row_hash"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?attr/colorSecondary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Hash" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/git_log_row_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.5"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Time" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -11,6 +11,7 @@
|
||||||
<string name="dialog_delete">Ordner löschen</string>
|
<string name="dialog_delete">Ordner löschen</string>
|
||||||
<string name="dialog_do_not_delete">Abbruch</string>
|
<string name="dialog_do_not_delete">Abbruch</string>
|
||||||
<string name="title_activity_git_clone">Repository Informationen</string>
|
<string name="title_activity_git_clone">Repository Informationen</string>
|
||||||
|
<string name="title_activity_git_log">Commit-Log</string>
|
||||||
<!-- Password Store -->
|
<!-- Password Store -->
|
||||||
<string name="creation_dialog_text">Bitte klone oder erstelle ein neues Repository, bevor du versuchst ein Passwort hinzuzufügen oder jegliche Synchronisation-Operation durchführst.</string>
|
<string name="creation_dialog_text">Bitte klone oder erstelle ein neues Repository, bevor du versuchst ein Passwort hinzuzufügen oder jegliche Synchronisation-Operation durchführst.</string>
|
||||||
<string name="move">Verschieben</string>
|
<string name="move">Verschieben</string>
|
||||||
|
@ -121,6 +122,7 @@
|
||||||
<string name="git_sync">Synchronisiere Repository</string>
|
<string name="git_sync">Synchronisiere Repository</string>
|
||||||
<string name="git_pull">Git Pull</string>
|
<string name="git_pull">Git Pull</string>
|
||||||
<string name="git_push">Git Push</string>
|
<string name="git_push">Git Push</string>
|
||||||
|
<string name="git_log">Commit-Log anzeigen</string>
|
||||||
<string name="show_password_pref_title">Zeige das Password</string>
|
<string name="show_password_pref_title">Zeige das Password</string>
|
||||||
<string name="show_password_pref_summary">Soll das entschlüsselte Passwort sichtbar sein? Dies deaktiviert nicht das Kopieren.</string>
|
<string name="show_password_pref_summary">Soll das entschlüsselte Passwort sichtbar sein? Dies deaktiviert nicht das Kopieren.</string>
|
||||||
<string name="show_extra_content_pref_title">Zeige weiteren Inhalt</string>
|
<string name="show_extra_content_pref_title">Zeige weiteren Inhalt</string>
|
||||||
|
|
|
@ -178,7 +178,5 @@
|
||||||
<string name="git_push_generic_error">El envío fue rechazado por el servidor, la razón:</string>
|
<string name="git_push_generic_error">El envío fue rechazado por el servidor, la razón:</string>
|
||||||
<string name="jgit_error_push_dialog_text">Ocurrió un error durante el envío:</string>
|
<string name="jgit_error_push_dialog_text">Ocurrió un error durante el envío:</string>
|
||||||
<string name="git_operation_remember_passphrase">Recordar contraseñagit (inseguro)</string>
|
<string name="git_operation_remember_passphrase">Recordar contraseñagit (inseguro)</string>
|
||||||
<string name="hackish_tools">Hackish tools</string>
|
|
||||||
<string name="abort_rebase">Abortar rebase</string>
|
<string name="abort_rebase">Abortar rebase</string>
|
||||||
<string name="commit_hash">Hash del commit</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -178,6 +178,4 @@
|
||||||
<string name="git_push_other_error">Pousser au dépôt distant sans avance rapide rejetée. Vérifiez la variable receive.denyNonFastForwards dans le fichier de configuration du répertoire de destination.</string>
|
<string name="git_push_other_error">Pousser au dépôt distant sans avance rapide rejetée. Vérifiez la variable receive.denyNonFastForwards dans le fichier de configuration du répertoire de destination.</string>
|
||||||
<string name="jgit_error_push_dialog_text">Une erreur s\'est produite lors de l\'opération de poussée:</string>
|
<string name="jgit_error_push_dialog_text">Une erreur s\'est produite lors de l\'opération de poussée:</string>
|
||||||
<string name="git_operation_remember_passphrase">Se rappeler de la phrase secrète dans la configuration de l\'application (peu sûr)</string>
|
<string name="git_operation_remember_passphrase">Se rappeler de la phrase secrète dans la configuration de l\'application (peu sûr)</string>
|
||||||
<string name="hackish_tools">Outils de hack</string>
|
|
||||||
<string name="commit_hash">Commettre la clé</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -248,10 +248,8 @@
|
||||||
<string name="clear_saved_passphrase_ssh">Limpar a frase secreta salva para chave SSH local</string>
|
<string name="clear_saved_passphrase_ssh">Limpar a frase secreta salva para chave SSH local</string>
|
||||||
<string name="clear_saved_passphrase_https">Limpar senha HTTPS salva</string>
|
<string name="clear_saved_passphrase_https">Limpar senha HTTPS salva</string>
|
||||||
<string name="git_operation_remember_passphrase">Lembrar senha da chave</string>
|
<string name="git_operation_remember_passphrase">Lembrar senha da chave</string>
|
||||||
<string name="hackish_tools">Ferramentas de hackers</string>
|
|
||||||
<string name="abort_rebase">Abortar rebase e realizar push do novo branch</string>
|
<string name="abort_rebase">Abortar rebase e realizar push do novo branch</string>
|
||||||
<string name="reset_to_remote">Hard reset no branch remoto</string>
|
<string name="reset_to_remote">Hard reset no branch remoto</string>
|
||||||
<string name="commit_hash">Commit hash</string>
|
|
||||||
<string name="openkeychain_ssh_api_connect_fail">Falha ao conectar ao serviço de API SSH do OpenKeychain.</string>
|
<string name="openkeychain_ssh_api_connect_fail">Falha ao conectar ao serviço de API SSH do OpenKeychain.</string>
|
||||||
<string name="no_ssh_api_provider">Nenhum provedor de API SSH encontrado. O OpenKeychain está instalado?</string>
|
<string name="no_ssh_api_provider">Nenhum provedor de API SSH encontrado. O OpenKeychain está instalado?</string>
|
||||||
<string name="ssh_api_pending_intent_failed">SSH API pendente falhou</string>
|
<string name="ssh_api_pending_intent_failed">SSH API pendente falhou</string>
|
||||||
|
|
|
@ -233,10 +233,8 @@
|
||||||
<string name="git_push_other_error">Удаленный репозиторий отклонил запись изменений без быстрой перемотки вперед. Проверьте переменную receive.denyNonFastForwards в файле конфигурации репозитория назначения.</string>
|
<string name="git_push_other_error">Удаленный репозиторий отклонил запись изменений без быстрой перемотки вперед. Проверьте переменную receive.denyNonFastForwards в файле конфигурации репозитория назначения.</string>
|
||||||
<string name="jgit_error_push_dialog_text">В хоте операции записи изменений возникла ошибка:</string>
|
<string name="jgit_error_push_dialog_text">В хоте операции записи изменений возникла ошибка:</string>
|
||||||
<string name="git_operation_remember_passphrase">Заполнить парольную фразу в конфигурации приложнеия (небезопасно)</string>
|
<string name="git_operation_remember_passphrase">Заполнить парольную фразу в конфигурации приложнеия (небезопасно)</string>
|
||||||
<string name="hackish_tools">Костыльные инструменты</string>
|
|
||||||
<string name="abort_rebase">Прервать перебазирование и записать изменения в новую ветку</string>
|
<string name="abort_rebase">Прервать перебазирование и записать изменения в новую ветку</string>
|
||||||
<string name="reset_to_remote">Полный сброс до состояния удаленной ветки</string>
|
<string name="reset_to_remote">Полный сброс до состояния удаленной ветки</string>
|
||||||
<string name="commit_hash">Хэш-сумма изменений</string>
|
|
||||||
<string name="openkeychain_ssh_api_connect_fail">Ошибка при подключении к сервису OpenKeychain SSH API</string>
|
<string name="openkeychain_ssh_api_connect_fail">Ошибка при подключении к сервису OpenKeychain SSH API</string>
|
||||||
<string name="no_ssh_api_provider">Не найдено SSH API провайдеров. OpenKeychain установлен?</string>
|
<string name="no_ssh_api_provider">Не найдено SSH API провайдеров. OpenKeychain установлен?</string>
|
||||||
<string name="ssh_api_pending_intent_failed">Ожидаемое намерение SSH API не удалось</string>
|
<string name="ssh_api_pending_intent_failed">Ожидаемое намерение SSH API не удалось</string>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<string name="dialog_do_not_delete">Cancel</string>
|
<string name="dialog_do_not_delete">Cancel</string>
|
||||||
<string name="title_activity_git_clone">Repository information</string>
|
<string name="title_activity_git_clone">Repository information</string>
|
||||||
<string name="title_activity_git_config" translatable="false">Git configuration</string>
|
<string name="title_activity_git_config" translatable="false">Git configuration</string>
|
||||||
|
<string name="title_activity_git_log">Commit log</string>
|
||||||
|
|
||||||
<!-- Password Store -->
|
<!-- Password Store -->
|
||||||
<string name="creation_dialog_text">Please clone or create a new repository below before trying to add a password or running any synchronization operation.</string>
|
<string name="creation_dialog_text">Please clone or create a new repository below before trying to add a password or running any synchronization operation.</string>
|
||||||
|
@ -117,8 +118,8 @@
|
||||||
|
|
||||||
<!-- Preferences -->
|
<!-- Preferences -->
|
||||||
<string name="pref_repository_title">Repository</string>
|
<string name="pref_repository_title">Repository</string>
|
||||||
<string name="pref_edit_server_info">Edit git server settings</string>
|
<string name="pref_edit_server_info">Edit Git server settings</string>
|
||||||
<string name="pref_edit_git_config">Git utils</string>
|
<string name="pref_edit_git_config">Local Git config & utilities</string>
|
||||||
<string name="pref_ssh_title">Import SSH key</string>
|
<string name="pref_ssh_title">Import SSH key</string>
|
||||||
<string name="pref_ssh_keygen_title">Generate SSH key pair</string>
|
<string name="pref_ssh_keygen_title">Generate SSH key pair</string>
|
||||||
<string name="pref_ssh_see_key_title">View generated public SSH key</string>
|
<string name="pref_ssh_see_key_title">View generated public SSH key</string>
|
||||||
|
@ -217,6 +218,7 @@
|
||||||
<string name="git_pull">Pull from remote</string>
|
<string name="git_pull">Pull from remote</string>
|
||||||
<string name="git_push">Push to remote</string>
|
<string name="git_push">Push to remote</string>
|
||||||
<string name="git_push_up_to_date">Everything up-to-date</string>
|
<string name="git_push_up_to_date">Everything up-to-date</string>
|
||||||
|
<string name="git_log">Show commit log</string>
|
||||||
<string name="show_password_pref_title">Show the password</string>
|
<string name="show_password_pref_title">Show the password</string>
|
||||||
<string name="show_password_pref_summary">Control the visibility of the passwords once decrypted. This does not disable copying to clipboard.</string>
|
<string name="show_password_pref_summary">Control the visibility of the passwords once decrypted. This does not disable copying to clipboard.</string>
|
||||||
<string name="show_extra_content_pref_title">Show extra content</string>
|
<string name="show_extra_content_pref_title">Show extra content</string>
|
||||||
|
@ -281,10 +283,12 @@
|
||||||
<string name="clear_saved_passphrase_ssh">Clear saved passphrase for local SSH key</string>
|
<string name="clear_saved_passphrase_ssh">Clear saved passphrase for local SSH key</string>
|
||||||
<string name="clear_saved_passphrase_https">Clear saved HTTPS password</string>
|
<string name="clear_saved_passphrase_https">Clear saved HTTPS password</string>
|
||||||
<string name="git_operation_remember_passphrase">Remember key passphrase</string>
|
<string name="git_operation_remember_passphrase">Remember key passphrase</string>
|
||||||
<string name="hackish_tools">Hackish tools</string>
|
<string name="git_tools">Utilities</string>
|
||||||
<string name="abort_rebase">Abort rebase and push new branch</string>
|
<string name="abort_rebase">Abort rebase and push new branch</string>
|
||||||
<string name="reset_to_remote">Hard reset to remote branch</string>
|
<string name="reset_to_remote">Hard reset to remote branch</string>
|
||||||
<string name="commit_hash">Commit hash</string>
|
<string name="git_head_on_branch">On branch %1$s</string>
|
||||||
|
<string name="git_head_detached">HEAD detached at %1$s</string>
|
||||||
|
<string name="git_head_missing">Unable to locate HEAD</string>
|
||||||
<string name="openkeychain_ssh_api_connect_fail">Failed to connect to OpenKeychain SSH API service.</string>
|
<string name="openkeychain_ssh_api_connect_fail">Failed to connect to OpenKeychain SSH API service.</string>
|
||||||
<string name="no_ssh_api_provider">No SSH API provider found. Is OpenKeychain installed?</string>
|
<string name="no_ssh_api_provider">No SSH API provider found. Is OpenKeychain installed?</string>
|
||||||
<string name="ssh_api_pending_intent_failed">SSH API pending intent failed</string>
|
<string name="ssh_api_pending_intent_failed">SSH API pending intent failed</string>
|
||||||
|
@ -393,6 +397,6 @@
|
||||||
<!-- GPG key selection in folder creation -->
|
<!-- GPG key selection in folder creation -->
|
||||||
<string name="folder_creation_err_file_exists">A file by that name already exists</string>
|
<string name="folder_creation_err_file_exists">A file by that name already exists</string>
|
||||||
<string name="folder_creation_err_folder_exists">A folder by that name already exists</string>
|
<string name="folder_creation_err_folder_exists">A folder by that name already exists</string>
|
||||||
<string name="xkpwgen_extrachars_label" >Digits/Symbols (d/s)</string>
|
<string name="xkpwgen_extrachars_label">Digits/Symbols (d/s)</string>
|
||||||
<string name="xk_numbers_symbols_append_default">ds</string>
|
<string name="xk_numbers_symbols_append_default">ds</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue