Skip to content
Snippets Groups Projects
Commit 581c9667 authored by Noah Prail's avatar Noah Prail Committed by John
Browse files

Pipelines

parent af9a3191
No related branches found
No related tags found
No related merge requests found
Showing
with 1108 additions and 7 deletions
Loading
Loading
@@ -17,8 +17,13 @@ before_script:
 
stages:
- build
- test
 
build:
stage: build
script:
- ./gradlew assembleDebug
test:
stage: test
script:
- ./gradlew testFdroidDebug
\ No newline at end of file
Loading
Loading
@@ -14,6 +14,13 @@ You should be able to build the project from Android Studio without any further
```Gradle
LABCOAT_FABRIC_KEY = FABRIC_KEY_GOES_HERE_BUT_ONLY_REALLY_NEEDED_FOR_RELEASE_BUILDS
```
To build, run the following.
```bash
./gradlew assembleDebug
```
## Libraries
The following 3rd party libraries and resources are the reason this app works. Rapid development is easily attainable thanks to these fine folks and the work they do:
 
Loading
Loading
Loading
Loading
@@ -59,6 +59,7 @@
<activity android:name=".activity.MilestoneActivity"/>
<activity android:name=".activity.AddMilestoneActivity"/>
<activity android:name=".activity.BuildActivity"/>
<activity android:name=".activity.PipelineActivity"/>
<activity
android:name=".activity.LoadSomeInfoActivity"
android:theme="@style/Activity.Translucent"/>
Loading
Loading
package com.commit451.gitlab.activity
import android.Manifest
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Parcelable
import android.support.design.widget.Snackbar
import android.support.design.widget.TabLayout
import android.support.v4.content.ContextCompat
import android.support.v4.view.ViewPager
import android.support.v7.widget.Toolbar
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.adapter.PipelineSectionsPagerAdapter
import com.commit451.gitlab.event.PipelineChangedEvent
import com.commit451.gitlab.model.Account
import com.commit451.gitlab.model.api.Pipeline
import com.commit451.gitlab.model.api.Build
import com.commit451.gitlab.model.api.Project
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.util.DownloadUtil
import org.parceler.Parcels
import com.commit451.gitlab.extension.getParcelerParcelable
import com.commit451.gitlab.extension.putParcelParcelableExtra
import butterknife.BindView
import butterknife.ButterKnife
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
/**
* Shows the details of a pipeline
*/
class PipelineActivity : BaseActivity() {
companion object {
private val REQUEST_PERMISSION_WRITE_STORAGE = 1337
private val KEY_PROJECT = "key_project"
private val KEY_PIPELINE = "key_merge_request"
fun newIntent(context: Context, project: Project, pipeline: Pipeline): Intent {
val intent = Intent(context, PipelineActivity::class.java)
intent.putParcelParcelableExtra(KEY_PROJECT, project)
intent.putParcelParcelableExtra(KEY_PIPELINE, pipeline)
return intent
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.tabs) lateinit var tabLayout: TabLayout
@BindView(R.id.pager) lateinit var viewPager: ViewPager
@BindView(R.id.progress) lateinit var progress: View
lateinit var project: Project
lateinit var pipeline: Pipeline
private val onMenuItemClickListener = Toolbar.OnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_retry -> {
progress.visibility = View.VISIBLE
App.get().gitLab.retryPipeline(project.id, pipeline.id)
.compose(this@PipelineActivity.bindToLifecycle<Pipeline>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<Pipeline>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.unable_to_retry_pipeline, Snackbar.LENGTH_LONG)
.show()
}
override fun success(pipeline: Pipeline) {
progress.visibility = View.GONE
Snackbar.make(root, R.string.pipeline_started, Snackbar.LENGTH_LONG)
.show()
App.bus().post(PipelineChangedEvent(pipeline))
}
})
return@OnMenuItemClickListener true
}
R.id.action_cancel -> {
progress.visibility = View.VISIBLE
App.get().gitLab.cancelPipeline(project.id, pipeline.id)
.compose(this@PipelineActivity.bindToLifecycle<Pipeline>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<Pipeline>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.unable_to_cancel_pipeline, Snackbar.LENGTH_LONG)
.show()
}
override fun success(pipeline: Pipeline) {
progress.visibility = View.GONE
Snackbar.make(root, R.string.pipeline_canceled, Snackbar.LENGTH_LONG)
.show()
App.bus().post(PipelineChangedEvent(pipeline))
}
})
return@OnMenuItemClickListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pipeline)
val bind = ButterKnife.bind(this)
project = intent.getParcelerParcelable<Project>(KEY_PROJECT)!!
pipeline = intent.getParcelerParcelable<Pipeline>(KEY_PIPELINE)!!
toolbar.title = String.format(getString(R.string.pipeline_number), pipeline.id)
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.subtitle = project.nameWithNamespace
toolbar.inflateMenu(R.menu.retry)
toolbar.inflateMenu(R.menu.cancel)
toolbar.setOnMenuItemClickListener(onMenuItemClickListener)
setupTabs()
}
private fun setupTabs() {
val sectionsPagerAdapter = PipelineSectionsPagerAdapter(
this,
supportFragmentManager,
project,
pipeline)
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
}
package com.commit451.gitlab.adapter
import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
import com.commit451.gitlab.model.api.Pipeline
import com.commit451.gitlab.viewHolder.PipelineViewHolder
import com.commit451.gitlab.viewHolder.LoadingFooterViewHolder
import java.util.*
/**
* Pipelines adapter
*/
class PipelineAdapter(private val listener: PipelineAdapter.Listener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private val FOOTER_COUNT = 1
private val TYPE_ITEM = 0
private val TYPE_FOOTER = 1
}
private val values: ArrayList<Pipeline> = ArrayList()
private var loading = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
TYPE_ITEM -> {
val holder = PipelineViewHolder.inflate(parent)
holder.itemView.setOnClickListener {
val position = holder.adapterPosition
listener.onPipelinesClicked(getValueAt(position))
}
return holder
}
TYPE_FOOTER -> return LoadingFooterViewHolder.inflate(parent)
}
throw IllegalStateException("No holder for view type " + viewType)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is PipelineViewHolder) {
val pipeline = getValueAt(position)
holder.bind(pipeline)
} else if (holder is LoadingFooterViewHolder) {
holder.bind(loading)
} else {
throw IllegalStateException("What is this holder?")
}
}
override fun getItemCount(): Int {
return values.size + FOOTER_COUNT
}
override fun getItemViewType(position: Int): Int {
if (position == values.size) {
return TYPE_FOOTER
} else {
return TYPE_ITEM
}
}
fun setValues(values: Collection<Pipeline>?) {
this.values.clear()
addValues(values)
}
fun addValues(values: Collection<Pipeline>?) {
if (values != null) {
this.values.addAll(values)
}
notifyDataSetChanged()
}
fun updatePipeline(pipeline: Pipeline) {
val indexToModify = values.indices.firstOrNull { values[it].id == pipeline.id }
if (indexToModify != null) {
values.removeAt(indexToModify)
values.add(indexToModify, pipeline)
notifyItemChanged(indexToModify)
}
}
fun getValueAt(position: Int): Pipeline {
return values[position]
}
fun setLoading(loading: Boolean) {
this.loading = loading
notifyItemChanged(values.size)
}
interface Listener {
fun onPipelinesClicked(pipeline: Pipeline)
}
}
package com.commit451.gitlab.adapter
import android.content.Context
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
import com.commit451.gitlab.R
import com.commit451.gitlab.fragment.PipelineDescriptionFragment
import com.commit451.gitlab.model.api.Pipeline
import com.commit451.gitlab.model.api.Project
/**
* Pipeline sections
*/
class PipelineSectionsPagerAdapter(context: Context, fm: FragmentManager, private val project: Project, private val pipeline: Pipeline) : FragmentPagerAdapter(fm) {
private val titles: Array<String> = context.resources.getStringArray(R.array.pipeline_tabs)
override fun getItem(position: Int): Fragment {
when (position) {
0 -> return PipelineDescriptionFragment.newInstance(project, pipeline)
//1 -> return PipelineDescriptionFragment.newInstance(project, pipeline)
}
throw IllegalStateException("Position exceeded on view pager")
}
override fun getCount(): Int {
return titles.size
}
override fun getPageTitle(position: Int): CharSequence {
return titles[position]
}
}
Loading
Loading
@@ -20,12 +20,13 @@ class ProjectSectionsPagerAdapter(context: ProjectActivity, fm: FragmentManager)
val ACTIVITY_POS = 1
val FILES_POS = 2
val COMMITS_POS = 3
val BUILDS_POS = 4
val MILESTONES_POS = 5
val ISSUES_POS = 6
val MERGE_REQUESTS_POS = 7
val PROJECT_MEMBERS_POS = 8
val SNIPPETS_POS = 9
val PIPELINES_POS = 4
val BUILDS_POS = 5
val MILESTONES_POS = 6
val ISSUES_POS = 7
val MERGE_REQUESTS_POS = 8
val PROJECT_MEMBERS_POS = 9
val SNIPPETS_POS = 10
}
 
private val project: Project = context.project!!
Loading
Loading
@@ -38,6 +39,7 @@ class ProjectSectionsPagerAdapter(context: ProjectActivity, fm: FragmentManager)
if (!project!!.isBuildEnabled) {
Timber.d("Builds are disabled")
disabledSections.add(BUILDS_POS)
disabledSections.add(PIPELINES_POS)
}
if (!project.isIssuesEnabled) {
Timber.d("Issues are disabled")
Loading
Loading
@@ -77,6 +79,7 @@ class ProjectSectionsPagerAdapter(context: ProjectActivity, fm: FragmentManager)
ACTIVITY_POS -> return FeedFragment.newInstance(project.feedUrl)
FILES_POS -> return FilesFragment.newInstance()
COMMITS_POS -> return CommitsFragment.newInstance()
PIPELINES_POS -> return PipelinesFragment.newInstance()
BUILDS_POS -> return BuildsFragment.newInstance()
MILESTONES_POS -> return MilestonesFragment.newInstance()
ISSUES_POS -> return IssuesFragment.newInstance()
Loading
Loading
Loading
Loading
@@ -376,6 +376,35 @@ interface GitLabService {
fun cancelBuild(@Path("id") projectId: Long,
@Path("build_id") buildId: Long): Single<Build>
 
/* --- Pipelines --- */
@GET(API_VERSION + "/projects/{id}/pipelines")
fun getPipelines(@Path("id") projectId: Long,
@Query("scope") scope: String?): Single<Response<List<Pipeline>>>
@GET
fun getPipelines(@Url url: String,
@Query("scope") state: String?): Single<Response<List<Pipeline>>>
@GET(API_VERSION + "/projects/{id}/pipelines/{pipeline_id}/jobs")
fun getPipelineJobs(@Path("id") projectId: Long, @Path("pipeline_id") pipelineId: Long,
@Query("scope") scope: String?): Single<Response<List<Pipeline>>>
@GET
fun getPipelineJobs(@Url url: String,
@Query("scope") state: String?): Single<Response<List<Pipeline>>>
@GET(API_VERSION + "/projects/{id}/pipelines/{pipeline_id}")
fun getPipeline(@Path("id") projectId: Long,
@Path("pipeline_id") pipelineId: Long): Single<Pipeline>
@POST(API_VERSION + "/projects/{id}/pipelines/{pipeline_id}/retry")
fun retryPipeline(@Path("id") projectId: Long,
@Path("pipeline_id") pipelineId: Long): Single<Pipeline>
@POST(API_VERSION + "/projects/{id}/pipelines/{pipeline_id}/cancel")
fun cancelPipeline(@Path("id") projectId: Long,
@Path("pipeline_id") pipelineId: Long): Single<Pipeline>
/* --- SNIPPETS --- */
@GET(API_VERSION + "/projects/{id}/snippets")
fun getSnippets(@Path("id") projectId: Long): Single<Response<List<Snippet>>>
Loading
Loading
package com.commit451.gitlab.event
import com.commit451.gitlab.model.api.Pipeline
/**
* A pipeline changed
*/
class PipelineChangedEvent(val pipeline: Pipeline)
package com.commit451.gitlab.fragment
import android.os.Bundle
import android.os.Parcelable
import android.support.design.widget.Snackbar
import android.support.v4.widget.SwipeRefreshLayout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import butterknife.BindView
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.event.PipelineChangedEvent
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.*
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.util.DateUtil
import com.trello.rxlifecycle2.android.FragmentEvent
import org.greenrobot.eventbus.Subscribe
import org.parceler.Parcels
import timber.log.Timber
import java.util.*
/**
* Shows the details of a pipeline
*/
class PipelineDescriptionFragment : ButterKnifeFragment() {
companion object {
private val KEY_PROJECT = "project"
private val KEY_PIPELINE = "pipeline"
fun newInstance(project: Project, pipeline: Pipeline): PipelineDescriptionFragment {
val fragment = PipelineDescriptionFragment()
val args = Bundle()
args.putParcelable(KEY_PROJECT, Parcels.wrap(project))
args.putParcelable(KEY_PIPELINE, Parcels.wrap(pipeline))
fragment.arguments = args
return fragment
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.text_number) lateinit var textName: TextView
@BindView(R.id.text_status) lateinit var textStatus: TextView
@BindView(R.id.text_duration) lateinit var textDuration: TextView
@BindView(R.id.text_created) lateinit var textCreated: TextView
@BindView(R.id.text_finished) lateinit var textFinished: TextView
@BindView(R.id.text_ref) lateinit var textRef: TextView
@BindView(R.id.text_sha) lateinit var textSha: TextView
@BindView(R.id.text_author) lateinit var textAuthor: TextView
@BindView(R.id.text_message) lateinit var textMessage: TextView
lateinit var project: Project
lateinit var pipeline: Pipeline
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
project = Parcels.unwrap<Project>(arguments.getParcelable<Parcelable>(KEY_PROJECT))
pipeline = Parcels.unwrap<Pipeline>(arguments.getParcelable<Parcelable>(KEY_PIPELINE))
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_pipeline_description, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
swipeRefreshLayout.setOnRefreshListener { load() }
bindPipeline(pipeline)
App.bus().register(this)
}
override fun onDestroyView() {
App.bus().unregister(this)
super.onDestroyView()
}
fun load() {
App.get().gitLab.getPipeline(project.id, pipeline.id)
.setup(bindUntilEvent(FragmentEvent.DESTROY_VIEW))
.subscribe(object : CustomSingleObserver<Pipeline>() {
override fun error(t: Throwable) {
Timber.e(t)
Snackbar.make(root, R.string.unable_to_load_pipeline, Snackbar.LENGTH_LONG)
.show()
}
override fun success(pipeline: Pipeline) {
swipeRefreshLayout.isRefreshing = false
this@PipelineDescriptionFragment.pipeline = pipeline
bindPipeline(pipeline)
App.bus().post(PipelineChangedEvent(pipeline))
}
})
}
fun bindPipeline(pipeline: Pipeline) {
var finishedTime: Date? = pipeline.finishedAt
if (finishedTime == null) {
finishedTime = Date()
}
var startedTime: Date? = pipeline.startedAt
if (startedTime == null) {
startedTime = Date()
}
val status = String.format(getString(R.string.pipeline_status), pipeline.status)
textStatus.text = status
val name = String.format(getString(R.string.pipeline_name), pipeline.id)
textName.text = name
val created = String.format(getString(R.string.build_created), DateUtil.getRelativeTimeSpanString(activity, pipeline.createdAt))
textCreated.text = created
val finished = String.format(getString(R.string.pipeline_finished), pipeline.finishedAt)
textFinished.text = finished
val timeTaken = DateUtil.getTimeTaken(startedTime, finishedTime)
val duration = String.format(getString(R.string.pipeline_duration), timeTaken)
textDuration.text = duration
val ref = String.format(getString(R.string.pipeline_ref), pipeline.ref)
textRef.text = ref
val sha = String.format(getString(R.string.pipeline_sha), pipeline.sha)
textSha.text = sha
if (pipeline.finishedAt != null) {
val finished = String.format(getString(R.string.pipeline_finished), DateUtil.getRelativeTimeSpanString(activity, pipeline.finishedAt))
textFinished.text = finished
textFinished.visibility = View.VISIBLE
} else {
textFinished.visibility = View.GONE
}
if (pipeline.user != null) {
bindUser(pipeline.user)
}
}
fun bindUser(user: CommitUser) {
val authorText = String.format(getString(R.string.pipeline_commit_author), user.name)
textAuthor.text = authorText
}
@Subscribe
fun onPipelineChangedEvent(event: PipelineChangedEvent) {
if (pipeline.id == event.pipeline.id) {
pipeline = event.pipeline
bindPipeline(pipeline)
}
}
}
package com.commit451.gitlab.fragment
import android.os.Bundle
import android.os.Parcelable
import android.support.design.widget.Snackbar
import android.support.v4.widget.SwipeRefreshLayout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import butterknife.BindView
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.event.PipelineChangedEvent
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.*
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.util.DateUtil
import com.trello.rxlifecycle2.android.FragmentEvent
import org.greenrobot.eventbus.Subscribe
import org.parceler.Parcels
import timber.log.Timber
import java.util.*
/**
* Shows the details of a pipeline
*/
class PipelineJobsFragment : ButterKnifeFragment() {
companion object {
private val KEY_PROJECT = "project"
private val KEY_PIPELINE = "pipeline"
fun newInstance(project: Project, pipeline: Pipeline): PipelineJobsFragment {
val fragment = PipelineJobsFragment()
val args = Bundle()
args.putParcelable(KEY_PROJECT, Parcels.wrap(project))
args.putParcelable(KEY_PIPELINE, Parcels.wrap(pipeline))
fragment.arguments = args
return fragment
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.text_number) lateinit var textName: TextView
@BindView(R.id.text_status) lateinit var textStatus: TextView
@BindView(R.id.text_duration) lateinit var textDuration: TextView
@BindView(R.id.text_created) lateinit var textCreated: TextView
@BindView(R.id.text_finished) lateinit var textFinished: TextView
@BindView(R.id.text_ref) lateinit var textRef: TextView
@BindView(R.id.text_sha) lateinit var textSha: TextView
@BindView(R.id.text_author) lateinit var textAuthor: TextView
@BindView(R.id.text_message) lateinit var textMessage: TextView
lateinit var project: Project
lateinit var pipeline: Pipeline
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
project = Parcels.unwrap<Project>(arguments.getParcelable<Parcelable>(KEY_PROJECT))
pipeline = Parcels.unwrap<Pipeline>(arguments.getParcelable<Parcelable>(KEY_PIPELINE))
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_pipeline_description, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
swipeRefreshLayout.setOnRefreshListener { load() }
bindPipeline(pipeline)
App.bus().register(this)
}
override fun onDestroyView() {
App.bus().unregister(this)
super.onDestroyView()
}
fun load() {
App.get().gitLab.getPipeline(project.id, pipeline.id)
.setup(bindUntilEvent(FragmentEvent.DESTROY_VIEW))
.subscribe(object : CustomSingleObserver<Pipeline>() {
override fun error(t: Throwable) {
Timber.e(t)
Snackbar.make(root, R.string.unable_to_load_pipeline, Snackbar.LENGTH_LONG)
.show()
}
override fun success(pipeline: Pipeline) {
swipeRefreshLayout.isRefreshing = false
this@PipelineJobsFragment.pipeline = pipeline
bindPipeline(pipeline)
App.bus().post(PipelineChangedEvent(pipeline))
}
})
}
fun bindPipeline(pipeline: Pipeline) {
var finishedTime: Date? = pipeline.finishedAt
if (finishedTime == null) {
finishedTime = Date()
}
var startedTime: Date? = pipeline.startedAt
if (startedTime == null) {
startedTime = Date()
}
val status = String.format(getString(R.string.pipeline_status), pipeline.status)
textStatus.text = status
val name = String.format(getString(R.string.pipeline_name), pipeline.id)
textName.text = name
val created = String.format(getString(R.string.build_created), DateUtil.getRelativeTimeSpanString(activity, pipeline.createdAt))
textCreated.text = created
val finished = String.format(getString(R.string.pipeline_finished), pipeline.finishedAt)
textFinished.text = finished
val timeTaken = DateUtil.getTimeTaken(startedTime, finishedTime)
val duration = String.format(getString(R.string.pipeline_duration), timeTaken)
textDuration.text = duration
val ref = String.format(getString(R.string.pipeline_ref), pipeline.ref)
textRef.text = ref
val sha = String.format(getString(R.string.pipeline_sha), pipeline.sha)
textSha.text = sha
if (pipeline.finishedAt != null) {
val finished = String.format(getString(R.string.pipeline_finished), DateUtil.getRelativeTimeSpanString(activity, pipeline.finishedAt))
textFinished.text = finished
textFinished.visibility = View.VISIBLE
} else {
textFinished.visibility = View.GONE
}
if (pipeline.user != null) {
bindUser(pipeline.user)
}
}
fun bindUser(user: CommitUser) {
val authorText = String.format(getString(R.string.pipeline_commit_author), user.name)
textAuthor.text = authorText
}
@Subscribe
fun onPipelineChangedEvent(event: PipelineChangedEvent) {
if (pipeline.id == event.pipeline.id) {
pipeline = event.pipeline
bindPipeline(pipeline)
}
}
}
package com.commit451.gitlab.fragment
import android.net.Uri
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import butterknife.BindView
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.activity.ProjectActivity
import com.commit451.gitlab.adapter.PipelineAdapter
import com.commit451.gitlab.adapter.DividerItemDecoration
import com.commit451.gitlab.event.PipelineChangedEvent
import com.commit451.gitlab.event.ProjectReloadEvent
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.Pipeline
import com.commit451.gitlab.model.api.Project
import com.commit451.gitlab.navigation.Navigator
import com.commit451.gitlab.rx.CustomResponseSingleObserver
import com.commit451.gitlab.util.LinkHeaderParser
import com.trello.rxlifecycle2.android.FragmentEvent
import org.greenrobot.eventbus.Subscribe
import timber.log.Timber
/**
* Shows the pipelines of a project
*/
class PipelinesFragment : ButterKnifeFragment() {
companion object {
fun newInstance(): PipelinesFragment {
return PipelinesFragment()
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.list) lateinit var listPipelines: RecyclerView
@BindView(R.id.message_text) lateinit var textMessage: TextView
@BindView(R.id.issue_spinner) lateinit var spinnerIssue: Spinner
lateinit var adapterPipelines: PipelineAdapter
lateinit var layoutManagerPipelines: LinearLayoutManager
lateinit var scopes: Array<String>
var scope: String? = null
var project: Project? = null
var nextPageUrl: Uri? = null
var loading: Boolean = false
val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = layoutManagerPipelines.childCount
val totalItemCount = layoutManagerPipelines.itemCount
val firstVisibleItem = layoutManagerPipelines.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount && !loading && nextPageUrl != null) {
loadMore()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scopes = resources.getStringArray(R.array.pipeline_scope_values)
scope = scopes[0]
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_pipelines, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
App.bus().register(this)
adapterPipelines = PipelineAdapter(object : PipelineAdapter.Listener {
override fun onPipelinesClicked(pipeline: Pipeline) {
if (project != null) {
Navigator.navigateToPipeline(activity, project!!, pipeline)
} else {
Snackbar.make(root, getString(R.string.wait_for_project_to_load), Snackbar.LENGTH_SHORT)
.show()
}
}
})
layoutManagerPipelines = LinearLayoutManager(activity)
listPipelines.layoutManager = layoutManagerPipelines
listPipelines.addItemDecoration(DividerItemDecoration(activity))
listPipelines.adapter = adapterPipelines
listPipelines.addOnScrollListener(onScrollListener)
spinnerIssue.adapter = ArrayAdapter(activity, android.R.layout.simple_list_item_1,
android.R.id.text1, resources.getStringArray(R.array.pipeline_scope_names))
spinnerIssue.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
scope = scopes[position]
loadData()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
swipeRefreshLayout.setOnRefreshListener { loadData() }
if (activity is ProjectActivity) {
project = (activity as ProjectActivity).project
loadData()
} else {
throw IllegalStateException("Incorrect parent activity")
}
}
override fun onDestroyView() {
App.bus().unregister(this)
super.onDestroyView()
}
override fun loadData() {
textMessage.visibility = View.GONE
swipeRefreshLayout.isRefreshing = true
nextPageUrl = null
loading = true
App.get().gitLab.getPipelines(project!!.id, scope)
.setup(bindUntilEvent(FragmentEvent.DESTROY_VIEW))
.subscribe(object : CustomResponseSingleObserver<List<Pipeline>>() {
override fun error(e: Throwable) {
loading = false
Timber.e(e)
swipeRefreshLayout.isRefreshing = false
textMessage.visibility = View.VISIBLE
textMessage.setText(R.string.failed_to_load_pipelines)
adapterPipelines.setValues(null)
nextPageUrl = null
}
override fun responseNonNullSuccess(pipelines: List<Pipeline>) {
loading = false
swipeRefreshLayout.isRefreshing = false
if (pipelines.isEmpty()) {
textMessage.visibility = View.VISIBLE
textMessage.setText(R.string.no_pipelines)
}
adapterPipelines.setValues(pipelines)
nextPageUrl = LinkHeaderParser.parse(response()).next
Timber.d("Next page url %s", nextPageUrl)
}
})
}
fun loadMore() {
if (nextPageUrl == null) {
return
}
adapterPipelines.setLoading(true)
loading = true
Timber.d("loadMore called for %s", nextPageUrl)
App.get().gitLab.getPipelines(nextPageUrl!!.toString(), scope)
.setup(bindUntilEvent(FragmentEvent.DESTROY_VIEW))
.subscribe(object : CustomResponseSingleObserver<List<Pipeline>>() {
override fun error(e: Throwable) {
Timber.e(e)
loading = false
adapterPipelines.setLoading(false)
}
override fun responseNonNullSuccess(pipelines: List<Pipeline>) {
loading = false
adapterPipelines.setLoading(false)
nextPageUrl = LinkHeaderParser.parse(response()).next
adapterPipelines.addValues(pipelines)
}
})
}
@Subscribe
fun onEvent(event: ProjectReloadEvent) {
project = event.project
loadData()
}
@Subscribe
fun onEvent(event: PipelineChangedEvent) {
adapterPipelines.updatePipeline(event.pipeline)
}
}
\ No newline at end of file
package com.commit451.gitlab.model.api;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.commit451.gitlab.util.ObjectUtil;
import org.parceler.Parcel;
import java.util.Date;
@Parcel
@JsonObject
public class CommitUser {
@JsonField(name = "id")
String id;
@JsonField(name = "name")
String name;
@JsonField(name = "username")
String username;
@JsonField(name = "state")
String state;
@JsonField(name = "avatar_url")
String avatarUrl;
@JsonField(name = "web_url")
String webUrl;
public CommitUser() {}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getUsername() {
return username;
}
public String getState() {
return state;
}
public String getAvatarUrl() {
return avatarUrl;
}
public String getWebUrl() {
return webUrl;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof CommitUser)) {
return false;
}
CommitUser commit = (CommitUser) o;
return ObjectUtil.INSTANCE.equals(id, commit.id);
}
@Override
public int hashCode() {
return ObjectUtil.INSTANCE.hash(id);
}
}
Loading
Loading
@@ -5,6 +5,8 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
 
import org.parceler.Parcel;
 
import java.util.Date;
/**
* A pipeline.
*/
Loading
Loading
@@ -12,6 +14,8 @@ import org.parceler.Parcel;
@JsonObject
public class Pipeline {
 
@JsonField(name = "user")
CommitUser user;
@JsonField(name = "id")
long id;
@JsonField(name = "sha")
Loading
Loading
@@ -20,7 +24,16 @@ public class Pipeline {
String ref;
@JsonField(name = "status")
String status;
@JsonField(name = "created_at")
Date createdAt;
@JsonField(name = "started_at")
Date startedAt;
@JsonField(name = "finished_at")
Date finishedAt;
public CommitUser getUser() {
return user;
}
public long getId() {
return id;
}
Loading
Loading
@@ -36,4 +49,12 @@ public class Pipeline {
public String getStatus() {
return status;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getStartedAt() { return startedAt; }
public Date getFinishedAt() { return finishedAt; }
}
package com.commit451.gitlab.model.api;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import java.util.Date;
/**
* Represents a pipeline
*/
@Parcel
@JsonObject
public class Pipelines {
@JsonField(name = "sha")
String sha;
@JsonField(name = "id")
long id;
@JsonField(name = "ref")
String ref;
@JsonField(name = "status")
String status;
public long getId() {
return id;
}
public String getSha() {
return sha;
}
public String getRef() {
return ref;
}
public String getStatus() {
return status;
}
}
Loading
Loading
@@ -196,6 +196,10 @@ object Navigator {
val intent = BuildActivity.newIntent(activity, project, build)
activity.startActivity(intent)
}
fun navigateToPipeline(activity: Activity, project: Project, pipeline: com.commit451.gitlab.model.api.Pipeline) {
val intent = PipelineActivity.newIntent(activity, project, pipeline)
activity.startActivity(intent)
}
 
fun navigateToAttach(activity: Activity, project: Project, requestCode: Int) {
val intent = AttachActivity.newIntent(activity, project)
Loading
Loading
package com.commit451.gitlab.viewHolder
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import butterknife.BindView
import butterknife.ButterKnife
import com.commit451.gitlab.R
import com.commit451.gitlab.model.api.Pipeline
import com.commit451.gitlab.util.DateUtil
import java.util.*
/**
* Pipelines, woot
*/
class PipelineViewHolder(view: View) : RecyclerView.ViewHolder(view) {
companion object {
fun inflate(parent: ViewGroup): PipelineViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_pipeline, parent, false)
return PipelineViewHolder(view)
}
}
@BindView(R.id.sha) lateinit var textSha: TextView
@BindView(R.id.number) lateinit var textPipelineNumber: TextView
@BindView(R.id.status) lateinit var textStatus: TextView
@BindView(R.id.ref) lateinit var textRef: TextView
init {
ButterKnife.bind(this, view)
}
fun bind(pipeline: Pipeline) {
val pipeSha = pipeline.sha.substring(0, 8)
val pipelineShaText = String.format(itemView.resources.getString(R.string.pipeline_sha), pipeSha)
textSha.text = pipelineShaText
val pipelineNumberText = String.format(itemView.resources.getString(R.string.pipeline_number), pipeline.id)
textPipelineNumber.text = pipelineNumberText
val statusText = String.format(itemView.resources.getString(R.string.pipeline_status), pipeline.status)
textStatus.text = statusText
val refText = String.format(itemView.resources.getString(R.string.pipeline_ref), pipeline.ref)
textRef.text = refText
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13L7,13v-2h10v2z"/>
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/progress_fullscreen"/>
</FrameLayout>
</LinearLayout>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment