Skip to content
Snippets Groups Projects
Commit af79125c authored by John Carlson's avatar John Carlson
Browse files

Merged branch develop into master

parents 5212b54f 747021e0
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 1810 additions and 2067 deletions
package com.commit451.gitlab.activity;
import android.animation.Animator;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.model.api.FileUploadResponse;
import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.rx.CustomSingleObserver;
import com.commit451.gitlab.rx.FileObservableFactory;
import org.parceler.Parcels;
import java.io.File;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.codetail.animation.ViewAnimationUtils;
import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MultipartBody;
import pl.aprilapps.easyphotopicker.DefaultCallback;
import pl.aprilapps.easyphotopicker.EasyImage;
import timber.log.Timber;
/**
* Attaches files
*/
public class AttachActivity extends BaseActivity {
public static final String KEY_FILE_UPLOAD_RESPONSE = "response";
private static final String KEY_PROJECT = "project";
public static Intent newIntent(Context context, Project project) {
Intent intent = new Intent(context, AttachActivity.class);
intent.putExtra(KEY_PROJECT, Parcels.wrap(project));
return intent;
}
@BindView(R.id.root_buttons)
ViewGroup rootButtons;
@BindView(R.id.progress)
View progress;
@BindView(R.id.attachCard)
View card;
Project project;
@OnClick(R.id.root)
void onRootClicked() {
onBackPressed();
}
@OnClick(R.id.button_choose_photo)
void onChoosePhotoClicked() {
EasyImage.openGallery(this, 0, false);
}
@OnClick(R.id.button_take_photo)
void onTakePhotoClicked() {
EasyImage.openCamera(this, 0);
}
@OnClick(R.id.button_choose_file)
void onChooseFileClicked() {
EasyImage.openChooserWithDocuments(this, "Choose file", 0);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_attach);
ButterKnife.bind(this);
//Run the runnable after the view has been measured
card.post(new Runnable() {
@Override
public void run() {
//we need the radius of the animation circle, which is the diagonal of the view
float finalRadius = (float) Math.hypot(card.getWidth(), card.getHeight());
//it's using a 3rd-party ViewAnimationUtils class for compat reasons (up to API 14)
Animator animator = ViewAnimationUtils
.createCircularReveal(card, 0, card.getHeight(), 0, finalRadius);
animator.setDuration(500);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
});
project = Parcels.unwrap(getIntent().getParcelableExtra(KEY_PROJECT));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
EasyImage.handleActivityResult(requestCode, resultCode, data, this, new DefaultCallback() {
@Override
public void onImagePickerError(Exception e, EasyImage.ImageSource source, int type) {
//Some error handling
}
@Override
public void onImagesPicked(List<File> imageFiles, EasyImage.ImageSource source, int type) {
onPhotoReturned(imageFiles.get(0));
}
@Override
public void onCanceled(EasyImage.ImageSource source, int type) {
//Cancel handling, you might wanna remove taken photo if it was canceled
if (source == EasyImage.ImageSource.CAMERA) {
File photoFile = EasyImage.lastlyTakenButCanceledPhoto(AttachActivity.this);
if (photoFile != null) {
photoFile.delete();
}
}
}
});
}
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.do_nothing, R.anim.fade_out);
}
private void onPhotoReturned(File photo) {
progress.setVisibility(View.VISIBLE);
rootButtons.setVisibility(View.INVISIBLE);
FileObservableFactory.toPart(photo)
.flatMap(new Function<MultipartBody.Part, SingleSource<FileUploadResponse>>() {
@Override
public SingleSource<FileUploadResponse> apply(MultipartBody.Part part) throws Exception {
return App.get().getGitLab().uploadFile(project.getId(), part);
}
})
.compose(this.<FileUploadResponse>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<FileUploadResponse>() {
@Override
public void success(@NonNull FileUploadResponse fileUploadResponse) {
Intent data = new Intent();
data.putExtra(KEY_FILE_UPLOAD_RESPONSE, Parcels.wrap(fileUploadResponse));
setResult(RESULT_OK, data);
finish();
}
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
finish();
}
});
}
}
package com.commit451.gitlab.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.FileUploadResponse
import com.commit451.gitlab.model.api.Project
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.rx.FileObservableFactory
import io.codetail.animation.ViewAnimationUtils
import org.parceler.Parcels
import pl.aprilapps.easyphotopicker.DefaultCallback
import pl.aprilapps.easyphotopicker.EasyImage
import timber.log.Timber
import java.io.File
/**
* Attaches files
*/
class AttachActivity : BaseActivity() {
companion object {
val KEY_FILE_UPLOAD_RESPONSE = "response"
private val KEY_PROJECT = "project"
fun newIntent(context: Context, project: Project): Intent {
val intent = Intent(context, AttachActivity::class.java)
intent.putExtra(KEY_PROJECT, Parcels.wrap(project))
return intent
}
}
@BindView(R.id.root_buttons) lateinit var rootButtons: ViewGroup
@BindView(R.id.progress) lateinit var progress: View
@BindView(R.id.attachCard) lateinit var card: View
var project: Project?= null
@OnClick(R.id.root)
fun onRootClicked() {
onBackPressed()
}
@OnClick(R.id.button_choose_photo)
fun onChoosePhotoClicked() {
EasyImage.openGallery(this, 0, false)
}
@OnClick(R.id.button_take_photo)
fun onTakePhotoClicked() {
EasyImage.openCamera(this, 0)
}
@OnClick(R.id.button_choose_file)
fun onChooseFileClicked() {
EasyImage.openChooserWithDocuments(this, "Choose file", 0)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_attach)
ButterKnife.bind(this)
//Run the runnable after the view has been measured
card.post {
//we need the radius of the animation circle, which is the diagonal of the view
val finalRadius = Math.hypot(card.width.toDouble(), card.height.toDouble()).toFloat()
//it's using a 3rd-party ViewAnimationUtils class for compat reasons (up to API 14)
val animator = ViewAnimationUtils
.createCircularReveal(card, 0, card.height, 0f, finalRadius)
animator.duration = 500
animator.interpolator = AccelerateDecelerateInterpolator()
animator.start()
}
project = Parcels.unwrap<Project>(intent.getParcelableExtra<Parcelable>(KEY_PROJECT))
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
EasyImage.handleActivityResult(requestCode, resultCode, data, this, object : DefaultCallback() {
override fun onImagePickerError(e: Exception?, source: EasyImage.ImageSource?, type: Int) {
//Some error handling
}
override fun onImagesPicked(imageFiles: List<File>, source: EasyImage.ImageSource, type: Int) {
onPhotoReturned(imageFiles[0])
}
override fun onCanceled(source: EasyImage.ImageSource?, type: Int) {
//Cancel handling, you might wanna remove taken photo if it was canceled
if (source == EasyImage.ImageSource.CAMERA) {
val photoFile = EasyImage.lastlyTakenButCanceledPhoto(this@AttachActivity)
photoFile?.delete()
}
}
})
}
override fun finish() {
super.finish()
overridePendingTransition(R.anim.do_nothing, R.anim.fade_out)
}
fun onPhotoReturned(photo: File) {
progress.visibility = View.VISIBLE
rootButtons.visibility = View.INVISIBLE
FileObservableFactory.toPart(photo)
.flatMap { part -> App.get().gitLab.uploadFile(project!!.id, part) }
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<FileUploadResponse>() {
override fun success(fileUploadResponse: FileUploadResponse) {
val data = Intent()
data.putExtra(KEY_FILE_UPLOAD_RESPONSE, Parcels.wrap(fileUploadResponse))
setResult(Activity.RESULT_OK, data)
finish()
}
override fun error(t: Throwable) {
Timber.e(t)
finish()
}
})
}
}
package com.commit451.gitlab.activity;
import android.support.design.widget.TextInputLayout;
import android.text.TextUtils;
import com.commit451.gitlab.R;
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
/**
* Base activity for others to derive from
*/
public class BaseActivity extends RxAppCompatActivity {
public boolean hasEmptyFields(TextInputLayout... textInputLayouts) {
boolean hasEmptyField = false;
for (TextInputLayout textInputLayout : textInputLayouts) {
if (TextUtils.isEmpty(textInputLayout.getEditText().getText())) {
textInputLayout.setError(getString(R.string.required_field));
hasEmptyField = true;
} else {
textInputLayout.setError(null);
}
}
return hasEmptyField;
}
}
package com.commit451.gitlab.activity
import com.novoda.simplechromecustomtabs.SimpleChromeCustomTabs
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
/**
* Base activity for others to derive from
*/
open class BaseActivity : RxAppCompatActivity() {
open fun hasBrowsableLinks(): Boolean {
return false
}
override fun onResume() {
super.onResume()
if (hasBrowsableLinks()) {
SimpleChromeCustomTabs.getInstance().connectTo(this)
}
}
override fun onPause() {
if (hasBrowsableLinks() && SimpleChromeCustomTabs.getInstance().isConnected) {
SimpleChromeCustomTabs.getInstance().disconnectFrom(this)
}
super.onPause()
}
}
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.support.annotation.NonNull;
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.BuildSectionsPagerAdapter;
import com.commit451.gitlab.event.BuildChangedEvent;
import com.commit451.gitlab.model.Account;
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.BuildUtil;
import com.commit451.gitlab.util.DownloadUtil;
import org.parceler.Parcels;
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 merge request
*/
public class BuildActivity extends BaseActivity {
private static final int REQUEST_PERMISSION_WRITE_STORAGE = 1337;
private static final String KEY_PROJECT = "key_project";
private static final String KEY_BUILD = "key_merge_request";
public static Intent newIntent(Context context, Project project, Build build) {
Intent intent = new Intent(context, BuildActivity.class);
intent.putExtra(KEY_PROJECT, Parcels.wrap(project));
intent.putExtra(KEY_BUILD, Parcels.wrap(build));
return intent;
}
@BindView(R.id.root)
ViewGroup root;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.tabs)
TabLayout tabLayout;
@BindView(R.id.pager)
ViewPager viewPager;
@BindView(R.id.progress)
View progress;
MenuItem menuItemDownload;
Project project;
Build build;
private final Toolbar.OnMenuItemClickListener onMenuItemClickListener = new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_retry:
progress.setVisibility(View.VISIBLE);
App.get().getGitLab().retryBuild(project.getId(), build.getId())
.compose(BuildActivity.this.<Build>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Build>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.unable_to_retry_build, Snackbar.LENGTH_LONG)
.show();
}
@Override
public void success(@NonNull Build build) {
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.build_started, Snackbar.LENGTH_LONG)
.show();
App.bus().post(new BuildChangedEvent(build));
}
});
return true;
case R.id.action_erase:
progress.setVisibility(View.VISIBLE);
App.get().getGitLab().eraseBuild(project.getId(), build.getId())
.compose(BuildActivity.this.<Build>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Build>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.unable_to_erase_build, Snackbar.LENGTH_LONG)
.show();
}
@Override
public void success(@NonNull Build build) {
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.build_erased, Snackbar.LENGTH_LONG)
.show();
App.bus().post(new BuildChangedEvent(build));
}
});
return true;
case R.id.action_cancel:
progress.setVisibility(View.VISIBLE);
App.get().getGitLab().cancelBuild(project.getId(), build.getId())
.compose(BuildActivity.this.<Build>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Build>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.unable_to_cancel_build, Snackbar.LENGTH_LONG)
.show();
}
@Override
public void success(@NonNull Build build) {
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.build_canceled, Snackbar.LENGTH_LONG)
.show();
App.bus().post(new BuildChangedEvent(build));
}
});
return true;
case R.id.action_download:
checkDownloadBuild();
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_build);
ButterKnife.bind(this);
project = Parcels.unwrap(getIntent().getParcelableExtra(KEY_PROJECT));
build = Parcels.unwrap(getIntent().getParcelableExtra(KEY_BUILD));
toolbar.setTitle(getString(R.string.build_number) + build.getId());
toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
toolbar.setSubtitle(project.getNameWithNamespace());
toolbar.inflateMenu(R.menu.menu_build);
toolbar.setOnMenuItemClickListener(onMenuItemClickListener);
menuItemDownload = toolbar.getMenu().findItem(R.id.action_download);
menuItemDownload.setVisible(build.getArtifactsFile() != null);
setupTabs();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_WRITE_STORAGE: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadBuild();
}
}
}
}
private void setupTabs() {
BuildSectionsPagerAdapter sectionsPagerAdapter = new BuildSectionsPagerAdapter(
this,
getSupportFragmentManager(),
project,
build);
viewPager.setAdapter(sectionsPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
@TargetApi(23)
private void checkDownloadBuild() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
downloadBuild();
} else {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_WRITE_STORAGE);
}
}
private void downloadBuild() {
Account account = App.get().getAccount();
String downloadUrl = BuildUtil.getDownloadBuildUrl(App.get().getAccount().getServerUrl(), project, build);
Timber.d("Downloading build: " + downloadUrl);
DownloadUtil.download(BuildActivity.this, account, downloadUrl, build.getArtifactsFile().getFileName());
}
}
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.BuildSectionsPagerAdapter
import com.commit451.gitlab.event.BuildChangedEvent
import com.commit451.gitlab.model.Account
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 butterknife.BindView
import butterknife.ButterKnife
import com.commit451.gitlab.extension.getDownloadBuildUrl
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
/**
* Shows the details of a merge request
*/
class BuildActivity : BaseActivity() {
companion object {
private val REQUEST_PERMISSION_WRITE_STORAGE = 1337
private val KEY_PROJECT = "key_project"
private val KEY_BUILD = "key_merge_request"
fun newIntent(context: Context, project: Project, build: Build): Intent {
val intent = Intent(context, BuildActivity::class.java)
intent.putExtra(KEY_PROJECT, Parcels.wrap(project))
intent.putExtra(KEY_BUILD, Parcels.wrap(build))
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 menuItemDownload: MenuItem
lateinit var project: Project
lateinit var build: Build
private val onMenuItemClickListener = Toolbar.OnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_retry -> {
progress.visibility = View.VISIBLE
App.get().gitLab.retryBuild(project.id, build.id)
.compose(this@BuildActivity.bindToLifecycle<Build>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<Build>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.unable_to_retry_build, Snackbar.LENGTH_LONG)
.show()
}
override fun success(build: Build) {
progress.visibility = View.GONE
Snackbar.make(root, R.string.build_started, Snackbar.LENGTH_LONG)
.show()
App.bus().post(BuildChangedEvent(build))
}
})
return@OnMenuItemClickListener true
}
R.id.action_erase -> {
progress.visibility = View.VISIBLE
App.get().gitLab.eraseBuild(project.id, build.id)
.compose(this@BuildActivity.bindToLifecycle<Build>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<Build>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.unable_to_erase_build, Snackbar.LENGTH_LONG)
.show()
}
override fun success(build: Build) {
progress.visibility = View.GONE
Snackbar.make(root, R.string.build_erased, Snackbar.LENGTH_LONG)
.show()
App.bus().post(BuildChangedEvent(build))
}
})
return@OnMenuItemClickListener true
}
R.id.action_cancel -> {
progress.visibility = View.VISIBLE
App.get().gitLab.cancelBuild(project.id, build.id)
.compose(this@BuildActivity.bindToLifecycle<Build>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<Build>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.unable_to_cancel_build, Snackbar.LENGTH_LONG)
.show()
}
override fun success(build: Build) {
progress.visibility = View.GONE
Snackbar.make(root, R.string.build_canceled, Snackbar.LENGTH_LONG)
.show()
App.bus().post(BuildChangedEvent(build))
}
})
return@OnMenuItemClickListener true
}
R.id.action_download -> {
checkDownloadBuild()
return@OnMenuItemClickListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_build)
ButterKnife.bind(this)
project = Parcels.unwrap<Project>(intent.getParcelableExtra<Parcelable>(KEY_PROJECT))
build = Parcels.unwrap<Build>(intent.getParcelableExtra<Parcelable>(KEY_BUILD))
toolbar.title = getString(R.string.build_number) + build.id
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.subtitle = project.nameWithNamespace
toolbar.inflateMenu(R.menu.retry)
toolbar.inflateMenu(R.menu.erase)
toolbar.inflateMenu(R.menu.cancel)
toolbar.inflateMenu(R.menu.download)
toolbar.setOnMenuItemClickListener(onMenuItemClickListener)
menuItemDownload = toolbar.menu.findItem(R.id.action_download)
menuItemDownload.isVisible = build.artifactsFile != null
setupTabs()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_PERMISSION_WRITE_STORAGE -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadBuild()
}
}
}
}
private fun setupTabs() {
val sectionsPagerAdapter = BuildSectionsPagerAdapter(
this,
supportFragmentManager,
project,
build)
viewPager.adapter = sectionsPagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
@TargetApi(23)
private fun checkDownloadBuild() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
downloadBuild()
} else {
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERMISSION_WRITE_STORAGE)
}
}
private fun downloadBuild() {
val account = App.get().getAccount()
val downloadUrl = build.getDownloadBuildUrl(App.get().getAccount().serverUrl, project)
Timber.d("Downloading build: " + downloadUrl)
DownloadUtil.download(this@BuildActivity, account, downloadUrl, build.artifactsFile.fileName)
}
}
package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.DiffAdapter;
import com.commit451.gitlab.model.api.Diff;
import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.model.api.RepositoryCommit;
import com.commit451.gitlab.rx.CustomSingleObserver;
import org.parceler.Parcels;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* Shows the lines of a commit aka the diff
*/
public class DiffActivity extends BaseActivity {
private static final String EXTRA_PROJECT = "extra_project";
private static final String EXTRA_COMMIT = "extra_commit";
public static Intent newIntent(Context context, Project project, RepositoryCommit commit) {
Intent intent = new Intent(context, DiffActivity.class);
intent.putExtra(EXTRA_PROJECT, Parcels.wrap(project));
intent.putExtra(EXTRA_COMMIT, Parcels.wrap(commit));
return intent;
}
@BindView(R.id.root)
ViewGroup root;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.swipe_layout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.list)
RecyclerView listDiff;
@BindView(R.id.message_text)
TextView textMessage;
DiffAdapter adapterDiff;
private Project project;
private RepositoryCommit commit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_diff);
ButterKnife.bind(this);
project = Parcels.unwrap(getIntent().getParcelableExtra(EXTRA_PROJECT));
commit = Parcels.unwrap(getIntent().getParcelableExtra(EXTRA_COMMIT));
toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
toolbar.setTitle(commit.getShortId());
adapterDiff = new DiffAdapter(commit, new DiffAdapter.Listener() {
@Override
public void onDiffClicked(Diff diff) {
}
});
listDiff.setAdapter(adapterDiff);
listDiff.setLayoutManager(new LinearLayoutManager(this));
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
loadData();
}
});
loadData();
}
private void loadData() {
textMessage.setVisibility(View.GONE);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
}
});
App.get().getGitLab().getCommitDiff(project.getId(), commit.getId())
.compose(this.<List<Diff>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<List<Diff>>() {
@Override
public void error(@NonNull Throwable t) {
swipeRefreshLayout.setRefreshing(false);
Timber.e(t);
textMessage.setText(R.string.connection_error);
textMessage.setVisibility(View.VISIBLE);
}
@Override
public void success(@NonNull List<Diff> diffs) {
swipeRefreshLayout.setRefreshing(false);
adapterDiff.setData(diffs);
}
});
}
}
\ No newline at end of file
package com.commit451.gitlab.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import butterknife.BindView
import butterknife.ButterKnife
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.adapter.DiffAdapter
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.Diff
import com.commit451.gitlab.model.api.Project
import com.commit451.gitlab.model.api.RepositoryCommit
import com.commit451.gitlab.rx.CustomSingleObserver
import org.parceler.Parcels
import timber.log.Timber
/**
* Shows the lines of a commit aka the diff
*/
class DiffActivity : BaseActivity() {
companion object {
private val EXTRA_PROJECT = "extra_project"
private val EXTRA_COMMIT = "extra_commit"
fun newIntent(context: Context, project: Project, commit: RepositoryCommit): Intent {
val intent = Intent(context, DiffActivity::class.java)
intent.putExtra(EXTRA_PROJECT, Parcels.wrap(project))
intent.putExtra(EXTRA_COMMIT, Parcels.wrap(commit))
return intent
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.list) lateinit var listDiff: RecyclerView
@BindView(R.id.message_text) lateinit var textMessage: TextView
lateinit var adapterDiff: DiffAdapter
lateinit var project: Project
lateinit var commit: RepositoryCommit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_diff)
ButterKnife.bind(this)
project = Parcels.unwrap<Project>(intent.getParcelableExtra<Parcelable>(EXTRA_PROJECT))
commit = Parcels.unwrap<RepositoryCommit>(intent.getParcelableExtra<Parcelable>(EXTRA_COMMIT))
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.title = commit.shortId
adapterDiff = DiffAdapter(commit, object : DiffAdapter.Listener {
override fun onDiffClicked(diff: Diff) {
}
})
listDiff.adapter = adapterDiff
listDiff.layoutManager = LinearLayoutManager(this)
swipeRefreshLayout.setOnRefreshListener { loadData() }
loadData()
}
fun loadData() {
textMessage.visibility = View.GONE
swipeRefreshLayout.isRefreshing = true
App.get().gitLab.getCommitDiff(project.id, commit.id)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<List<Diff>>() {
override fun error(t: Throwable) {
swipeRefreshLayout.isRefreshing = false
Timber.e(t)
textMessage.setText(R.string.connection_error)
textMessage.visibility = View.VISIBLE
}
override fun success(diffs: List<Diff>) {
swipeRefreshLayout.isRefreshing = false
adapterDiff.setData(diffs)
}
})
}
}
\ No newline at end of file
package com.commit451.gitlab.activity;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.model.api.RepositoryFile;
import com.commit451.gitlab.rx.CustomSingleObserver;
import com.commit451.gitlab.rx.DecodeObservableFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.Charset;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
public class FileActivity extends BaseActivity {
private static final int REQUEST_PERMISSION_WRITE_STORAGE = 1337;
private static final long MAX_FILE_SIZE = 1024 * 1024;
private static final String EXTRA_PROJECT_ID = "extra_project_id";
private static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_REF = "extra_ref";
@Retention(RetentionPolicy.SOURCE)
@IntDef({OPTION_SAVE, OPTION_OPEN})
public @interface Option {
}
public static final int OPTION_SAVE = 0;
public static final int OPTION_OPEN = 1;
public static Intent newIntent(Context context, long projectId, String path, String ref) {
Intent intent = new Intent(context, FileActivity.class);
intent.putExtra(EXTRA_PROJECT_ID, projectId);
intent.putExtra(EXTRA_PATH, path);
intent.putExtra(EXTRA_REF, ref);
return intent;
}
@BindView(R.id.root)
ViewGroup root;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.file_blob)
WebView webViewFileBlob;
@BindView(R.id.progress)
View progress;
private long projectId;
private String path;
private String ref;
private RepositoryFile repositoryFile;
private String fileName;
private byte[] blob;
@Option
private int option;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
ButterKnife.bind(this);
projectId = getIntent().getLongExtra(EXTRA_PROJECT_ID, -1);
path = getIntent().getStringExtra(EXTRA_PATH);
ref = getIntent().getStringExtra(EXTRA_REF);
toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_open:
option = OPTION_OPEN;
checkAccountPermission();
return true;
case R.id.action_save:
option = OPTION_SAVE;
checkAccountPermission();
return true;
}
return false;
}
});
loadData();
}
private void loadData() {
progress.setVisibility(View.VISIBLE);
App.get().getGitLab().getFile(projectId, path, ref)
.compose(this.<RepositoryFile>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<RepositoryFile>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, R.string.file_load_error, Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull RepositoryFile repositoryFile) {
progress.setVisibility(View.GONE);
bindFile(repositoryFile);
}
});
}
private void bindFile(RepositoryFile repositoryFile) {
this.repositoryFile = repositoryFile;
fileName = repositoryFile.getFileName();
toolbar.setTitle(fileName);
if (repositoryFile.getSize() > MAX_FILE_SIZE) {
Snackbar.make(root, R.string.file_too_big, Snackbar.LENGTH_SHORT)
.show();
} else {
loadBlob(repositoryFile);
}
}
private void loadBlob(RepositoryFile repositoryFile) {
DecodeObservableFactory.newDecode(repositoryFile.getContent())
.compose(this.<byte[]>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<byte[]>() {
@Override
public void error(@NonNull Throwable t) {
Snackbar.make(root, R.string.failed_to_load, Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull byte[] bytes) {
bindBlob(bytes);
}
});
}
private void bindBlob(byte[] blob) {
this.blob = blob;
String content;
String mimeType = null;
String extension = fileExt(fileName);
if (extension != null) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mimeType != null) {
mimeType = mimeType.toLowerCase();
}
}
if (mimeType != null && mimeType.startsWith("image/")) {
String imageURL = "data:" + mimeType + ";base64," + repositoryFile.getContent();
content = "<!DOCTYPE html>" +
"<html>" +
"<body>" +
"<img style=\"width: 100%;\" src=\"" + imageURL + "\">" +
"</body>" +
"</html>";
} else {
String text = new String(this.blob, Charset.forName("UTF-8"));
content = "<!DOCTYPE html>" +
"<html>" +
"<head>" +
"<link href=\"github.css\" rel=\"stylesheet\" />" +
"</head>" +
"<body>" +
"<pre><code>" +
Html.escapeHtml(text) +
"</code></pre>" +
"<script src=\"highlight.pack.js\"></script>" +
"<script>hljs.initHighlightingOnLoad();</script>" +
"</body>" +
"</html>";
}
webViewFileBlob.loadDataWithBaseURL("file:///android_asset/", content, "text/html", "utf8", null);
toolbar.inflateMenu(R.menu.menu_file);
}
@TargetApi(23)
private void checkAccountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if (option == OPTION_SAVE) {
saveBlob();
} else {
openFile();
}
} else {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSION_WRITE_STORAGE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_WRITE_STORAGE: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (option == OPTION_SAVE) {
saveBlob();
} else {
openFile();
}
}
}
}
}
private File saveBlob() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) && blob != null) {
File targetFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(targetFile);
outputStream.write(blob);
Snackbar.make(root, getString(R.string.file_saved), Snackbar.LENGTH_SHORT)
.show();
return targetFile;
} catch (IOException e) {
Timber.e(e);
Snackbar.make(root, getString(R.string.save_error), Snackbar.LENGTH_SHORT)
.show();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
Timber.e(e);
}
}
}
} else {
Snackbar.make(root, getString(R.string.save_error), Snackbar.LENGTH_SHORT)
.show();
}
return null;
}
private void openFile() {
File file = saveBlob();
if (file == null) {
Snackbar.make(root, getString(R.string.open_error), Snackbar.LENGTH_SHORT)
.show();
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromFile(file));
String extension = fileExt(file.getName());
if (extension != null) {
intent.setTypeAndNormalize(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension));
}
try {
startActivity(intent);
} catch (ActivityNotFoundException | SecurityException e) {
Timber.e(e);
Snackbar.make(root, getString(R.string.open_error), Snackbar.LENGTH_SHORT)
.show();
}
}
private static String fileExt(String filename) {
int extStart = filename.lastIndexOf(".") + 1;
if (extStart < 1) {
return null;
}
return filename.substring(extStart);
}
}
package com.commit451.gitlab.activity
import android.Manifest
import android.annotation.TargetApi
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.support.annotation.IntDef
import android.support.design.widget.Snackbar
import android.support.v4.content.ContextCompat
import android.support.v7.widget.Toolbar
import android.text.Html
import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import android.webkit.WebView
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.model.api.RepositoryFile
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.rx.DecodeObservableFactory
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.nio.charset.Charset
import butterknife.BindView
import butterknife.ButterKnife
import com.commit451.gitlab.extension.setup
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
class FileActivity : BaseActivity() {
companion object {
private val REQUEST_PERMISSION_WRITE_STORAGE = 1337
private val MAX_FILE_SIZE = (1024 * 1024).toLong()
private val EXTRA_PROJECT_ID = "extra_project_id"
private val EXTRA_PATH = "extra_path"
private val EXTRA_REF = "extra_ref"
const val OPTION_SAVE = 0
const val OPTION_OPEN = 1
@Retention(AnnotationRetention.SOURCE)
@IntDef(OPTION_SAVE.toLong(), OPTION_OPEN.toLong())
annotation class Option
fun newIntent(context: Context, projectId: Long, path: String, ref: String): Intent {
val intent = Intent(context, FileActivity::class.java)
intent.putExtra(EXTRA_PROJECT_ID, projectId)
intent.putExtra(EXTRA_PATH, path)
intent.putExtra(EXTRA_REF, ref)
return intent
}
fun fileExtension(filename: String): String? {
val extStart = filename.lastIndexOf(".") + 1
if (extStart < 1) {
return null
}
return filename.substring(extStart)
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.file_blob) lateinit var webViewFileBlob: WebView
@BindView(R.id.progress) lateinit var progress: View
var projectId: Long = 0
var path: String? = null
var ref: String? = null
var repositoryFile: RepositoryFile? = null
var fileName: String? = null
var blob: ByteArray? = null
@Option
private var option: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_file)
ButterKnife.bind(this)
projectId = intent.getLongExtra(EXTRA_PROJECT_ID, -1)
path = intent.getStringExtra(EXTRA_PATH)
ref = intent.getStringExtra(EXTRA_REF)
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.setOnMenuItemClickListener(Toolbar.OnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_open -> {
option = OPTION_OPEN
checkAccountPermission()
return@OnMenuItemClickListener true
}
R.id.action_save -> {
option = OPTION_SAVE
checkAccountPermission()
return@OnMenuItemClickListener true
}
}
false
})
loadData()
}
private fun loadData() {
progress.visibility = View.VISIBLE
App.get().gitLab.getFile(projectId, path!!, ref!!)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<RepositoryFile>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, R.string.file_load_error, Snackbar.LENGTH_SHORT)
.show()
}
override fun success(repositoryFile: RepositoryFile) {
progress.visibility = View.GONE
bindFile(repositoryFile)
}
})
}
private fun bindFile(repositoryFile: RepositoryFile) {
this.repositoryFile = repositoryFile
fileName = repositoryFile.fileName
toolbar.title = fileName
if (repositoryFile.size > MAX_FILE_SIZE) {
Snackbar.make(root, R.string.file_too_big, Snackbar.LENGTH_SHORT)
.show()
} else {
loadBlob(repositoryFile)
}
}
private fun loadBlob(repositoryFile: RepositoryFile) {
DecodeObservableFactory.newDecode(repositoryFile.content)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<ByteArray>() {
override fun error(t: Throwable) {
Snackbar.make(root, R.string.failed_to_load, Snackbar.LENGTH_SHORT)
.show()
}
override fun success(bytes: ByteArray) {
bindBlob(bytes)
}
})
}
private fun bindBlob(blob: ByteArray) {
this.blob = blob
val content: String
var mimeType: String? = null
val extension = fileExtension(fileName!!)
if (extension != null) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
if (mimeType != null) {
mimeType = mimeType.toLowerCase()
}
}
if (mimeType != null && mimeType.startsWith("image/")) {
val imageURL = "data:" + mimeType + ";base64," + repositoryFile!!.content
content = "<!DOCTYPE html>" +
"<html>" +
"<body>" +
"<img style=\"width: 100%;\" src=\"" + imageURL + "\">" +
"</body>" +
"</html>"
} else {
val text = String(this.blob!!, Charset.forName("UTF-8"))
content = "<!DOCTYPE html>" +
"<html>" +
"<head>" +
"<link href=\"github.css\" rel=\"stylesheet\" />" +
"</head>" +
"<body>" +
"<pre><code>" +
Html.escapeHtml(text) +
"</code></pre>" +
"<script src=\"highlight.pack.js\"></script>" +
"<script>hljs.initHighlightingOnLoad();</script>" +
"</body>" +
"</html>"
}
webViewFileBlob.loadDataWithBaseURL("file:///android_asset/", content, "text/html", "utf8", null)
toolbar.inflateMenu(R.menu.menu_file)
}
@TargetApi(23)
private fun checkAccountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if (option == OPTION_SAVE) {
saveBlob()
} else {
openFile()
}
} else {
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERMISSION_WRITE_STORAGE)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_PERMISSION_WRITE_STORAGE -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (option == OPTION_SAVE) {
saveBlob()
} else {
openFile()
}
}
}
}
}
private fun saveBlob(): File? {
val state = Environment.getExternalStorageState()
if (Environment.MEDIA_MOUNTED == state && blob != null) {
val targetFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName!!)
var outputStream: FileOutputStream? = null
try {
outputStream = FileOutputStream(targetFile)
outputStream.write(blob!!)
Snackbar.make(root, getString(R.string.file_saved), Snackbar.LENGTH_SHORT)
.show()
return targetFile
} catch (e: IOException) {
Timber.e(e)
Snackbar.make(root, getString(R.string.save_error), Snackbar.LENGTH_SHORT)
.show()
} finally {
if (outputStream != null) {
try {
outputStream.close()
} catch (e: IOException) {
Timber.e(e)
}
}
}
} else {
Snackbar.make(root, getString(R.string.save_error), Snackbar.LENGTH_SHORT)
.show()
}
return null
}
private fun openFile() {
val file = saveBlob()
if (file == null) {
Snackbar.make(root, getString(R.string.open_error), Snackbar.LENGTH_SHORT)
.show()
return
}
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.fromFile(file)
val extension = fileExtension(file.name)
if (extension != null) {
intent.setTypeAndNormalize(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension))
}
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Timber.e(e)
Snackbar.make(root, getString(R.string.open_error), Snackbar.LENGTH_SHORT)
.show()
} catch (e: SecurityException) {
Timber.e(e)
Snackbar.make(root, getString(R.string.open_error), Snackbar.LENGTH_SHORT).show()
}
}
}
package com.commit451.gitlab.activity;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import com.commit451.alakazam.Alakazam;
import com.commit451.easel.Easel;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.GroupPagerAdapter;
import com.commit451.gitlab.model.api.Group;
import com.commit451.gitlab.model.api.GroupDetail;
import com.commit451.gitlab.rx.CustomSingleObserver;
import com.commit451.gitlab.transformation.PaletteTransformation;
import org.parceler.Parcels;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* See the things about the group
*/
public class GroupActivity extends BaseActivity {
private static final String KEY_GROUP = "key_group";
private static final String KEY_GROUP_ID = "key_group_id";
public static Intent newIntent(Context context, Group group) {
Intent intent = new Intent(context, GroupActivity.class);
intent.putExtra(KEY_GROUP, Parcels.wrap(group));
return intent;
}
public static Intent newIntent(Context context, long groupId) {
Intent intent = new Intent(context, GroupActivity.class);
intent.putExtra(KEY_GROUP_ID, groupId);
return intent;
}
@BindView(R.id.root)
View root;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.collapsing_toolbar)
CollapsingToolbarLayout collapsingToolbarLayout;
@BindView(R.id.viewpager)
ViewPager viewPager;
@BindView(R.id.tabs)
TabLayout tabLayout;
@BindView(R.id.backdrop)
ImageView backdrop;
@BindView(R.id.progress)
View progress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group);
ButterKnife.bind(this);
// Default content and scrim colors
toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
if (getIntent().hasExtra(KEY_GROUP)) {
Group group = Parcels.unwrap(getIntent().getParcelableExtra(KEY_GROUP));
bind(group);
} else {
progress.setVisibility(View.VISIBLE);
long groupId = getIntent().getLongExtra(KEY_GROUP_ID, -1);
App.get().getGitLab().getGroup(groupId)
.compose(this.<GroupDetail>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<GroupDetail>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
showError();
}
@Override
public void success(@NonNull GroupDetail groupDetail) {
progress.setVisibility(View.GONE);
bind(groupDetail);
}
});
}
}
@Override
public void onBackPressed() {
supportFinishAfterTransition();
}
private void bind(Group group) {
App.get().getPicasso()
.load(group.getAvatarUrl())
.transform(PaletteTransformation.instance())
.into(backdrop, new PaletteTransformation.PaletteCallback(backdrop) {
@Override
protected void onSuccess(Palette palette) {
bindPalette(palette);
}
@Override
public void onError() {
}
});
viewPager.setAdapter(new GroupPagerAdapter(this, getSupportFragmentManager(), group));
tabLayout.setupWithViewPager(viewPager);
}
private void bindPalette(Palette palette) {
int animationTime = 1000;
int vibrantColor = palette.getVibrantColor(Easel.getThemeAttrColor(this, R.attr.colorAccent));
int darkerColor = Easel.getDarkerColor(vibrantColor);
if (Build.VERSION.SDK_INT >= 21) {
Alakazam.navigationBarColorAnimator(getWindow(), darkerColor)
.setDuration(animationTime)
.start();
}
ObjectAnimator.ofObject(collapsingToolbarLayout, "contentScrimColor", new ArgbEvaluator(),
Easel.getThemeAttrColor(this, R.attr.colorPrimary), vibrantColor)
.setDuration(animationTime)
.start();
ObjectAnimator.ofObject(collapsingToolbarLayout, "statusBarScrimColor", new ArgbEvaluator(),
Easel.getThemeAttrColor(this, R.attr.colorPrimaryDark), darkerColor)
.setDuration(animationTime)
.start();
ObjectAnimator.ofObject(toolbar, "titleTextColor", new ArgbEvaluator(),
Color.WHITE, palette.getDarkMutedColor(Color.BLACK))
.setDuration(animationTime)
.start();
}
private void showError() {
Snackbar.make(root, R.string.connection_error, Snackbar.LENGTH_SHORT)
.show();
}
}
package com.commit451.gitlab.activity
import android.animation.ArgbEvaluator
import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.support.design.widget.CollapsingToolbarLayout
import android.support.design.widget.Snackbar
import android.support.design.widget.TabLayout
import android.support.v4.view.ViewPager
import android.support.v7.graphics.Palette
import android.support.v7.widget.Toolbar
import android.view.View
import android.widget.ImageView
import butterknife.BindView
import butterknife.ButterKnife
import com.commit451.alakazam.Alakazam
import com.commit451.easel.Easel
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.adapter.GroupPagerAdapter
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.Group
import com.commit451.gitlab.model.api.GroupDetail
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.transformation.PaletteTransformation
import com.squareup.picasso.Picasso
import org.parceler.Parcels
import timber.log.Timber
/**
* See the things about the group
*/
class GroupActivity : BaseActivity() {
companion object {
private val KEY_GROUP = "key_group"
private val KEY_GROUP_ID = "key_group_id"
fun newIntent(context: Context, group: Group): Intent {
val intent = Intent(context, GroupActivity::class.java)
intent.putExtra(KEY_GROUP, Parcels.wrap(group))
return intent
}
fun newIntent(context: Context, groupId: Long): Intent {
val intent = Intent(context, GroupActivity::class.java)
intent.putExtra(KEY_GROUP_ID, groupId)
return intent
}
}
@BindView(R.id.root) lateinit var root: View
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.collapsing_toolbar) lateinit var collapsingToolbarLayout: CollapsingToolbarLayout
@BindView(R.id.viewpager) lateinit var viewPager: ViewPager
@BindView(R.id.tabs) lateinit var tabLayout: TabLayout
@BindView(R.id.backdrop) lateinit var backdrop: ImageView
@BindView(R.id.progress) lateinit var progress: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_group)
ButterKnife.bind(this)
// Default content and scrim colors
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
if (intent.hasExtra(KEY_GROUP)) {
val group = Parcels.unwrap<Group>(intent.getParcelableExtra<Parcelable>(KEY_GROUP))
bind(group)
} else {
progress.visibility = View.VISIBLE
val groupId = intent.getLongExtra(KEY_GROUP_ID, -1)
App.get().gitLab.getGroup(groupId)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<GroupDetail>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
showError()
}
override fun success(groupDetail: GroupDetail) {
progress.visibility = View.GONE
bind(groupDetail)
}
})
}
}
override fun onBackPressed() {
supportFinishAfterTransition()
}
override fun hasBrowsableLinks(): Boolean {
return true
}
fun bind(group: Group) {
Picasso.with(this)
.load(group.avatarUrl)
.transform(PaletteTransformation.instance())
.into(backdrop, object : PaletteTransformation.PaletteCallback(backdrop) {
override fun onSuccess(palette: Palette?) {
bindPalette(palette!!)
}
override fun onError() {}
})
viewPager.adapter = GroupPagerAdapter(this, supportFragmentManager, group)
tabLayout.setupWithViewPager(viewPager)
}
fun bindPalette(palette: Palette) {
val animationTime = 1000
val vibrantColor = palette.getVibrantColor(Easel.getThemeAttrColor(this, R.attr.colorAccent))
val darkerColor = Easel.getDarkerColor(vibrantColor)
if (Build.VERSION.SDK_INT >= 21) {
Alakazam.navigationBarColorAnimator(window, darkerColor)
.setDuration(animationTime.toLong())
.start()
}
ObjectAnimator.ofObject(collapsingToolbarLayout, "contentScrimColor", ArgbEvaluator(),
Easel.getThemeAttrColor(this, R.attr.colorPrimary), vibrantColor)
.setDuration(animationTime.toLong())
.start()
ObjectAnimator.ofObject(collapsingToolbarLayout, "statusBarScrimColor", ArgbEvaluator(),
Easel.getThemeAttrColor(this, R.attr.colorPrimaryDark), darkerColor)
.setDuration(animationTime.toLong())
.start()
ObjectAnimator.ofObject(toolbar, "titleTextColor", ArgbEvaluator(),
Color.WHITE, palette.getDarkMutedColor(Color.BLACK))
.setDuration(animationTime.toLong())
.start()
}
fun showError() {
Snackbar.make(root, R.string.connection_error, Snackbar.LENGTH_SHORT)
.show()
}
}
package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
import com.alexgwyn.recyclerviewsquire.DynamicGridLayoutManager;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.GroupAdapter;
import com.commit451.gitlab.data.Prefs;
import com.commit451.gitlab.event.CloseDrawerEvent;
import com.commit451.gitlab.event.ReloadDataEvent;
import com.commit451.gitlab.model.api.Group;
import com.commit451.gitlab.navigation.Navigator;
import com.commit451.gitlab.rx.CustomResponseSingleObserver;
import com.commit451.gitlab.util.LinkHeaderParser;
import com.commit451.gitlab.viewHolder.GroupViewHolder;
import org.greenrobot.eventbus.Subscribe;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Response;
import timber.log.Timber;
/**
* Displays the groups of the current user
*/
public class GroupsActivity extends BaseActivity {
public static Intent newIntent(Context context) {
Intent intent = new Intent(context, GroupsActivity.class);
return intent;
}
@BindView(R.id.drawer_layout)
DrawerLayout drawerLayout;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.swipe_layout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.list)
RecyclerView listGroups;
@BindView(R.id.message_text)
TextView textMessage;
GroupAdapter adapterGroup;
DynamicGridLayoutManager layoutManager;
private Uri nextPageUrl;
private boolean loading = false;
private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (firstVisibleItem + visibleItemCount >= totalItemCount && !loading && nextPageUrl != null) {
loadMore();
}
}
};
private final GroupAdapter.Listener groupAdapterListener = new GroupAdapter.Listener() {
@Override
public void onGroupClicked(Group group, GroupViewHolder groupViewHolder) {
Navigator.navigateToGroup(GroupsActivity.this, groupViewHolder.image, group);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.get().getPrefs().setStartingView(Prefs.STARTING_VIEW_GROUPS);
setContentView(R.layout.activity_groups);
ButterKnife.bind(this);
App.bus().register(this);
toolbar.setTitle(R.string.nav_groups);
toolbar.setNavigationIcon(R.drawable.ic_menu_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.openDrawer(GravityCompat.START);
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
load();
}
});
textMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
load();
}
});
layoutManager = new DynamicGridLayoutManager(this);
layoutManager.setMinimumWidthDimension(R.dimen.user_list_image_size);
listGroups.setLayoutManager(layoutManager);
adapterGroup = new GroupAdapter(groupAdapterListener);
listGroups.setAdapter(adapterGroup);
listGroups.addOnScrollListener(onScrollListener);
load();
}
@Override
protected void onDestroy() {
super.onDestroy();
App.bus().unregister(this);
}
private void load() {
textMessage.setVisibility(View.GONE);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
}
});
nextPageUrl = null;
loading = true;
App.get().getGitLab().getGroups()
.compose(this.<Response<List<Group>>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomResponseSingleObserver<List<Group>>() {
@Override
public void error(@NonNull Throwable e) {
Timber.e(e);
swipeRefreshLayout.setRefreshing(false);
loading = false;
textMessage.setVisibility(View.VISIBLE);
textMessage.setText(R.string.connection_error);
}
@Override
public void responseSuccess(@NonNull List<Group> groups) {
loading = false;
swipeRefreshLayout.setRefreshing(false);
if (groups.isEmpty()) {
textMessage.setText(R.string.no_groups);
textMessage.setVisibility(View.VISIBLE);
listGroups.setVisibility(View.GONE);
} else {
adapterGroup.setGroups(groups);
textMessage.setVisibility(View.GONE);
listGroups.setVisibility(View.VISIBLE);
nextPageUrl = LinkHeaderParser.parse(response()).getNext();
}
}
});
}
private void loadMore() {
if (nextPageUrl == null) {
return;
}
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
}
});
loading = true;
Timber.d("loadMore called for %s", nextPageUrl);
App.get().getGitLab().getGroups(nextPageUrl.toString())
.compose(this.<Response<List<Group>>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomResponseSingleObserver<List<Group>>() {
@Override
public void error(@NonNull Throwable e) {
Timber.e(e);
loading = false;
}
@Override
public void responseSuccess(@NonNull List<Group> groups) {
loading = false;
adapterGroup.addGroups(groups);
nextPageUrl = LinkHeaderParser.parse(response()).getNext();
}
});
}
@Subscribe
public void onCloseDrawerEvent(CloseDrawerEvent event) {
drawerLayout.closeDrawers();
}
@Subscribe
public void onReloadData(ReloadDataEvent event) {
load();
}
}
package com.commit451.gitlab.activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.support.v4.view.GravityCompat
import android.support.v4.widget.DrawerLayout
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.Toolbar
import android.view.View
import android.widget.TextView
import butterknife.BindView
import butterknife.ButterKnife
import com.alexgwyn.recyclerviewsquire.DynamicGridLayoutManager
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.adapter.GroupAdapter
import com.commit451.gitlab.data.Prefs
import com.commit451.gitlab.event.CloseDrawerEvent
import com.commit451.gitlab.event.ReloadDataEvent
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.Group
import com.commit451.gitlab.navigation.Navigator
import com.commit451.gitlab.rx.CustomResponseSingleObserver
import com.commit451.gitlab.util.LinkHeaderParser
import com.commit451.gitlab.viewHolder.GroupViewHolder
import org.greenrobot.eventbus.Subscribe
import timber.log.Timber
/**
* Displays the groups of the current user
*/
class GroupsActivity : BaseActivity() {
companion object {
fun newIntent(context: Context): Intent {
val intent = Intent(context, GroupsActivity::class.java)
return intent
}
}
@BindView(R.id.drawer_layout) lateinit var drawerLayout: DrawerLayout
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.list) lateinit var listGroups: RecyclerView
@BindView(R.id.message_text) lateinit var textMessage: TextView
lateinit var adapterGroup: GroupAdapter
lateinit var layoutManager: DynamicGridLayoutManager
var nextPageUrl: Uri? = null
var loading = false
val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount && !loading && nextPageUrl != null) {
loadMore()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Prefs.startingView = Prefs.STARTING_VIEW_GROUPS
setContentView(R.layout.activity_groups)
ButterKnife.bind(this)
App.bus().register(this)
toolbar.setTitle(R.string.nav_groups)
toolbar.setNavigationIcon(R.drawable.ic_menu_24dp)
toolbar.setNavigationOnClickListener { drawerLayout.openDrawer(GravityCompat.START) }
swipeRefreshLayout.setOnRefreshListener { load() }
textMessage.setOnClickListener { load() }
layoutManager = DynamicGridLayoutManager(this)
layoutManager.setMinimumWidthDimension(R.dimen.user_list_image_size)
listGroups.layoutManager = layoutManager
adapterGroup = GroupAdapter(object : GroupAdapter.Listener {
override fun onGroupClicked(group: Group, groupViewHolder: GroupViewHolder) {
Navigator.navigateToGroup(this@GroupsActivity, groupViewHolder.image, group)
}
})
listGroups.adapter = adapterGroup
listGroups.addOnScrollListener(onScrollListener)
load()
}
override fun onDestroy() {
super.onDestroy()
App.bus().unregister(this)
}
fun load() {
textMessage.visibility = View.GONE
swipeRefreshLayout.isRefreshing = true
nextPageUrl = null
loading = true
App.get().gitLab.getGroups()
.setup(bindToLifecycle())
.subscribe(object : CustomResponseSingleObserver<List<Group>>() {
override fun error(e: Throwable) {
Timber.e(e)
swipeRefreshLayout.isRefreshing = false
loading = false
textMessage.visibility = View.VISIBLE
textMessage.setText(R.string.connection_error)
}
override fun responseSuccess(groups: List<Group>) {
loading = false
swipeRefreshLayout.isRefreshing = false
if (groups.isEmpty()) {
textMessage.setText(R.string.no_groups)
textMessage.visibility = View.VISIBLE
listGroups.visibility = View.GONE
} else {
adapterGroup.setGroups(groups)
textMessage.visibility = View.GONE
listGroups.visibility = View.VISIBLE
nextPageUrl = LinkHeaderParser.parse(response()).next
}
}
})
}
fun loadMore() {
if (nextPageUrl == null) {
return
}
swipeRefreshLayout.isRefreshing = true
loading = true
Timber.d("loadMore called for %s", nextPageUrl)
App.get().gitLab.getGroups(nextPageUrl!!.toString())
.setup(bindToLifecycle())
.subscribe(object : CustomResponseSingleObserver<List<Group>>() {
override fun error(e: Throwable) {
Timber.e(e)
loading = false
}
override fun responseSuccess(groups: List<Group>) {
loading = false
adapterGroup.addGroups(groups)
nextPageUrl = LinkHeaderParser.parse(response()).next
}
})
}
@Subscribe
fun onEvent(event: CloseDrawerEvent) {
drawerLayout.closeDrawers()
}
@Subscribe
fun onEvent(event: ReloadDataEvent) {
load()
}
}
package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
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.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.IssueDetailsAdapter;
import com.commit451.gitlab.event.IssueChangedEvent;
import com.commit451.gitlab.event.IssueReloadEvent;
import com.commit451.gitlab.model.api.FileUploadResponse;
import com.commit451.gitlab.model.api.Issue;
import com.commit451.gitlab.model.api.Note;
import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.navigation.Navigator;
import com.commit451.gitlab.rx.CustomResponseSingleObserver;
import com.commit451.gitlab.rx.CustomSingleObserver;
import com.commit451.gitlab.util.IntentUtil;
import com.commit451.gitlab.util.LinkHeaderParser;
import com.commit451.gitlab.view.SendMessageView;
import com.commit451.teleprinter.Teleprinter;
import org.greenrobot.eventbus.Subscribe;
import org.parceler.Parcels;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Response;
import timber.log.Timber;
/**
* Shows off an issue like a bar of gold
*/
public class IssueActivity extends BaseActivity {
private static final String EXTRA_PROJECT = "extra_project";
private static final String EXTRA_SELECTED_ISSUE = "extra_selected_issue";
private static final String EXTRA_PROJECT_NAMESPACE = "project_namespace";
private static final String EXTRA_PROJECT_NAME = "project_name";
private static final String EXTRA_ISSUE_IID = "extra_issue_iid";
private static final int REQUEST_ATTACH = 1;
public static Intent newIntent(Context context, Project project, Issue issue) {
Intent intent = new Intent(context, IssueActivity.class);
intent.putExtra(EXTRA_PROJECT, Parcels.wrap(project));
intent.putExtra(EXTRA_SELECTED_ISSUE, Parcels.wrap(issue));
return intent;
}
public static Intent newIntent(Context context, String namespace, String projectName, String issueIid) {
Intent intent = new Intent(context, IssueActivity.class);
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_ISSUE_IID, issueIid);
return intent;
}
@BindView(R.id.root)
ViewGroup root;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.issue_title)
TextView textTitle;
@BindView(R.id.swipe_layout)
SwipeRefreshLayout swipeRefreshLayout;
@BindView(R.id.list)
RecyclerView listNotes;
@BindView(R.id.send_message_view)
SendMessageView sendMessageView;
@BindView(R.id.progress)
View progress;
MenuItem menuItemOpenClose;
IssueDetailsAdapter adapterIssueDetails;
LinearLayoutManager layoutManagerNotes;
Project project;
Issue issue;
String issueIid;
boolean loading;
Uri nextPageUrl;
Teleprinter teleprinter;
private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManagerNotes.getChildCount();
int totalItemCount = layoutManagerNotes.getItemCount();
int firstVisibleItem = layoutManagerNotes.findFirstVisibleItemPosition();
if (firstVisibleItem + visibleItemCount >= totalItemCount && !loading && nextPageUrl != null) {
loadMoreNotes();
}
}
};
private final Toolbar.OnMenuItemClickListener onMenuItemClickListener = new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_share:
IntentUtil.share(root, issue.getUrl(project));
return true;
case R.id.action_close:
closeOrOpenIssue();
return true;
case R.id.action_delete:
App.get().getGitLab().deleteIssue(project.getId(), issue.getId())
.compose(IssueActivity.this.<String>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<String>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
Snackbar.make(root, getString(R.string.failed_to_delete_issue), Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull String s) {
App.bus().post(new IssueReloadEvent());
Toast.makeText(IssueActivity.this, R.string.issue_deleted, Toast.LENGTH_SHORT)
.show();
finish();
}
});
return true;
}
return false;
}
};
@OnClick(R.id.fab_edit_issue)
public void onEditIssueClick(View fab) {
Navigator.navigateToEditIssue(IssueActivity.this, fab, project, issue);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_issue);
ButterKnife.bind(this);
teleprinter = new Teleprinter(this);
App.bus().register(this);
toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
toolbar.inflateMenu(R.menu.menu_issue);
menuItemOpenClose = toolbar.getMenu().findItem(R.id.action_close);
toolbar.setOnMenuItemClickListener(onMenuItemClickListener);
layoutManagerNotes = new LinearLayoutManager(this);
listNotes.setLayoutManager(layoutManagerNotes);
listNotes.addOnScrollListener(onScrollListener);
sendMessageView.setCallbacks(new SendMessageView.Callbacks() {
@Override
public void onSendClicked(String message) {
postNote(message);
}
@Override
public void onAttachmentClicked() {
Navigator.navigateToAttach(IssueActivity.this, project, REQUEST_ATTACH);
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
loadNotes();
}
});
if (getIntent().hasExtra(EXTRA_SELECTED_ISSUE)) {
project = Parcels.unwrap(getIntent().getParcelableExtra(EXTRA_PROJECT));
issue = Parcels.unwrap(getIntent().getParcelableExtra(EXTRA_SELECTED_ISSUE));
adapterIssueDetails = new IssueDetailsAdapter(IssueActivity.this, issue, project);
listNotes.setAdapter(adapterIssueDetails);
bindIssue();
bindProject();
loadNotes();
} else if (getIntent().hasExtra(EXTRA_ISSUE_IID)) {
issueIid = getIntent().getStringExtra(EXTRA_ISSUE_IID);
String projectNamespace = getIntent().getStringExtra(EXTRA_PROJECT_NAMESPACE);
String projectName = getIntent().getStringExtra(EXTRA_PROJECT_NAME);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
}
});
App.get().getGitLab().getProject(projectNamespace, projectName)
.flatMap(new Function<Project, SingleSource<List<Issue>>>() {
@Override
public SingleSource<List<Issue>> apply(Project project) throws Exception {
IssueActivity.this.project = project;
return App.get().getGitLab().getIssuesByIid(project.getId(), issueIid);
}
})
.compose(this.<List<Issue>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<List<Issue>>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
swipeRefreshLayout.setRefreshing(false);
Snackbar.make(root, getString(R.string.failed_to_load), Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull List<Issue> issues) {
if (issues.isEmpty()) {
swipeRefreshLayout.setRefreshing(false);
Snackbar.make(root, getString(R.string.failed_to_load), Snackbar.LENGTH_SHORT)
.show();
} else {
issue = issues.get(0);
adapterIssueDetails = new IssueDetailsAdapter(IssueActivity.this, issue, project);
listNotes.setAdapter(adapterIssueDetails);
bindIssue();
bindProject();
loadNotes();
}
}
});
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ATTACH:
if (resultCode == RESULT_OK) {
FileUploadResponse response = Parcels.unwrap(data.getParcelableExtra(AttachActivity.KEY_FILE_UPLOAD_RESPONSE));
progress.setVisibility(View.GONE);
sendMessageView.appendText(response.getMarkdown());
} else {
Snackbar.make(root, R.string.failed_to_upload_file, Snackbar.LENGTH_LONG)
.show();
}
break;
}
}
@Override
protected void onDestroy() {
App.bus().unregister(this);
super.onDestroy();
}
private void bindProject() {
toolbar.setSubtitle(project.getNameWithNamespace());
}
private void bindIssue() {
toolbar.setTitle(getString(R.string.issue_number) + issue.getIid());
setOpenCloseMenuStatus();
textTitle.setText(issue.getTitle());
adapterIssueDetails.updateIssue(issue);
}
private void loadNotes() {
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
}
});
loading = true;
App.get().getGitLab().getIssueNotes(project.getId(), issue.getId())
.compose(this.<Response<List<Note>>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomResponseSingleObserver<List<Note>>() {
@Override
public void error(@NonNull Throwable t) {
loading = false;
Timber.e(t);
swipeRefreshLayout.setRefreshing(false);
Snackbar.make(root, getString(R.string.connection_error), Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void responseSuccess(@NonNull List<Note> notes) {
loading = false;
swipeRefreshLayout.setRefreshing(false);
nextPageUrl = LinkHeaderParser.parse(response()).getNext();
adapterIssueDetails.setNotes(notes);
}
});
}
private void loadMoreNotes() {
loading = true;
adapterIssueDetails.setLoading(true);
App.get().getGitLab().getIssueNotes(nextPageUrl.toString())
.compose(this.<Response<List<Note>>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomResponseSingleObserver<List<Note>>() {
@Override
public void error(@NonNull Throwable t) {
loading = false;
Timber.e(t);
adapterIssueDetails.setLoading(false);
}
@Override
public void responseSuccess(@NonNull List<Note> notes) {
loading = false;
adapterIssueDetails.setLoading(false);
nextPageUrl = LinkHeaderParser.parse(response()).getNext();
adapterIssueDetails.addNotes(notes);
}
});
}
private void postNote(String message) {
if (message.length() < 1) {
return;
}
progress.setVisibility(View.VISIBLE);
progress.setAlpha(0.0f);
progress.animate().alpha(1.0f);
// Clear text & collapse keyboard
teleprinter.hideKeyboard();
sendMessageView.clearText();
App.get().getGitLab().addIssueNote(project.getId(), issue.getId(), message)
.compose(this.<Note>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Note>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, getString(R.string.connection_error), Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull Note note) {
progress.setVisibility(View.GONE);
adapterIssueDetails.addNote(note);
listNotes.smoothScrollToPosition(IssueDetailsAdapter.getHeaderCount());
}
});
}
private void closeOrOpenIssue() {
progress.setVisibility(View.VISIBLE);
if (issue.getState().equals(Issue.STATE_CLOSED)) {
updateIssueStatus(App.get().getGitLab().updateIssueStatus(project.getId(), issue.getId(), Issue.STATE_REOPEN));
} else {
updateIssueStatus(App.get().getGitLab().updateIssueStatus(project.getId(), issue.getId(), Issue.STATE_CLOSE));
}
}
private void updateIssueStatus(Single<Issue> observable) {
observable
.compose(this.<Issue>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Issue>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
progress.setVisibility(View.GONE);
Snackbar.make(root, getString(R.string.error_changing_issue), Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void success(@NonNull Issue issue) {
progress.setVisibility(View.GONE);
IssueActivity.this.issue = issue;
App.bus().post(new IssueChangedEvent(IssueActivity.this.issue));
App.bus().post(new IssueReloadEvent());
setOpenCloseMenuStatus();
loadNotes();
}
});
}
private void setOpenCloseMenuStatus() {
menuItemOpenClose.setTitle(issue.getState().equals(Issue.STATE_CLOSED) ? R.string.reopen : R.string.close);
}
@Subscribe
public void onIssueChanged(IssueChangedEvent event) {
if (issue.getId() == event.issue.getId()) {
issue = event.issue;
bindIssue();
loadNotes();
}
}
}
package com.commit451.gitlab.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
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.support.v7.widget.Toolbar
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.adapter.IssueDetailsAdapter
import com.commit451.gitlab.event.IssueChangedEvent
import com.commit451.gitlab.event.IssueReloadEvent
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.FileUploadResponse
import com.commit451.gitlab.model.api.Issue
import com.commit451.gitlab.model.api.Note
import com.commit451.gitlab.model.api.Project
import com.commit451.gitlab.navigation.Navigator
import com.commit451.gitlab.rx.CustomResponseSingleObserver
import com.commit451.gitlab.rx.CustomSingleObserver
import com.commit451.gitlab.util.IntentUtil
import com.commit451.gitlab.util.LinkHeaderParser
import com.commit451.gitlab.view.SendMessageView
import com.commit451.teleprinter.Teleprinter
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.Subscribe
import org.parceler.Parcels
import retrofit2.Response
import timber.log.Timber
/**
* Shows off an issue like a bar of gold
*/
class IssueActivity : BaseActivity() {
companion object {
private val EXTRA_PROJECT = "extra_project"
private val EXTRA_SELECTED_ISSUE = "extra_selected_issue"
private val EXTRA_PROJECT_NAMESPACE = "project_namespace"
private val EXTRA_PROJECT_NAME = "project_name"
private val EXTRA_ISSUE_IID = "extra_issue_iid"
private val REQUEST_ATTACH = 1
fun newIntent(context: Context, project: Project, issue: Issue): Intent {
val intent = Intent(context, IssueActivity::class.java)
intent.putExtra(EXTRA_PROJECT, Parcels.wrap(project))
intent.putExtra(EXTRA_SELECTED_ISSUE, Parcels.wrap(issue))
return intent
}
fun newIntent(context: Context, namespace: String, projectName: String, issueIid: String): Intent {
val intent = Intent(context, IssueActivity::class.java)
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace)
intent.putExtra(EXTRA_PROJECT_NAME, projectName)
intent.putExtra(EXTRA_ISSUE_IID, issueIid)
return intent
}
}
@BindView(R.id.root) lateinit var root: ViewGroup
@BindView(R.id.toolbar) lateinit var toolbar: Toolbar
@BindView(R.id.issue_title) lateinit var textTitle: TextView
@BindView(R.id.swipe_layout) lateinit var swipeRefreshLayout: SwipeRefreshLayout
@BindView(R.id.list) lateinit var listNotes: RecyclerView
@BindView(R.id.send_message_view) lateinit var sendMessageView: SendMessageView
@BindView(R.id.progress) lateinit var progress: View
lateinit var menuItemOpenClose: MenuItem
lateinit var adapterIssueDetails: IssueDetailsAdapter
lateinit var layoutManagerNotes: LinearLayoutManager
lateinit var teleprinter: Teleprinter
var project: Project? = null
var issue: Issue? = null
var issueIid: String? = null
var loading: Boolean = false
var nextPageUrl: Uri? = null
val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = layoutManagerNotes.childCount
val totalItemCount = layoutManagerNotes.itemCount
val firstVisibleItem = layoutManagerNotes.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount && !loading && nextPageUrl != null) {
loadMoreNotes()
}
}
}
val onMenuItemClickListener = Toolbar.OnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_share -> {
IntentUtil.share(root, issue!!.getUrl(project))
return@OnMenuItemClickListener true
}
R.id.action_close -> {
closeOrOpenIssue()
return@OnMenuItemClickListener true
}
R.id.action_delete -> {
App.get().gitLab.deleteIssue(project!!.id, issue!!.id)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<String>() {
override fun error(t: Throwable) {
Timber.e(t)
Snackbar.make(root, getString(R.string.failed_to_delete_issue), Snackbar.LENGTH_SHORT)
.show()
}
override fun success(s: String) {
App.bus().post(IssueReloadEvent())
Toast.makeText(this@IssueActivity, R.string.issue_deleted, Toast.LENGTH_SHORT)
.show()
finish()
}
})
return@OnMenuItemClickListener true
}
}
false
}
@OnClick(R.id.fab_edit_issue)
fun onEditIssueClick(fab: View) {
Navigator.navigateToEditIssue(this@IssueActivity, fab, project!!, issue!!)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_issue)
ButterKnife.bind(this)
teleprinter = Teleprinter(this)
App.bus().register(this)
toolbar.setNavigationIcon(R.drawable.ic_back_24dp)
toolbar.setNavigationOnClickListener { onBackPressed() }
toolbar.inflateMenu(R.menu.share)
toolbar.inflateMenu(R.menu.close)
toolbar.inflateMenu(R.menu.delete)
menuItemOpenClose = toolbar.menu.findItem(R.id.action_close)
toolbar.setOnMenuItemClickListener(onMenuItemClickListener)
layoutManagerNotes = LinearLayoutManager(this)
listNotes.layoutManager = layoutManagerNotes
listNotes.addOnScrollListener(onScrollListener)
sendMessageView.callback = object : SendMessageView.Callback {
override fun onSendClicked(message: String) {
postNote(message)
}
override fun onAttachmentClicked() {
Navigator.navigateToAttach(this@IssueActivity, project!!, REQUEST_ATTACH)
}
}
swipeRefreshLayout.setOnRefreshListener { loadNotes() }
if (intent.hasExtra(EXTRA_SELECTED_ISSUE)) {
project = Parcels.unwrap<Project>(intent.getParcelableExtra<Parcelable>(EXTRA_PROJECT))
issue = Parcels.unwrap<Issue>(intent.getParcelableExtra<Parcelable>(EXTRA_SELECTED_ISSUE))
adapterIssueDetails = IssueDetailsAdapter(this@IssueActivity, issue, project!!)
listNotes.adapter = adapterIssueDetails
bindIssue()
bindProject()
loadNotes()
} else if (intent.hasExtra(EXTRA_ISSUE_IID)) {
issueIid = intent.getStringExtra(EXTRA_ISSUE_IID)
val projectNamespace = intent.getStringExtra(EXTRA_PROJECT_NAMESPACE)
val projectName = intent.getStringExtra(EXTRA_PROJECT_NAME)
swipeRefreshLayout.isRefreshing = true
App.get().gitLab.getProject(projectNamespace, projectName)
.flatMap { project ->
this@IssueActivity.project = project
App.get().gitLab.getIssuesByIid(project.id, issueIid!!)
}
.compose(this.bindToLifecycle<List<Issue>>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomSingleObserver<List<Issue>>() {
override fun error(t: Throwable) {
Timber.e(t)
swipeRefreshLayout.isRefreshing = false
Snackbar.make(root, getString(R.string.failed_to_load), Snackbar.LENGTH_SHORT)
.show()
}
override fun success(issues: List<Issue>) {
if (issues.isEmpty()) {
swipeRefreshLayout.isRefreshing = false
Snackbar.make(root, getString(R.string.failed_to_load), Snackbar.LENGTH_SHORT)
.show()
} else {
issue = issues[0]
adapterIssueDetails = IssueDetailsAdapter(this@IssueActivity, issue, project!!)
listNotes.adapter = adapterIssueDetails
bindIssue()
bindProject()
loadNotes()
}
}
})
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_ATTACH -> if (resultCode == Activity.RESULT_OK) {
val response = Parcels.unwrap<FileUploadResponse>(data?.getParcelableExtra<Parcelable>(AttachActivity.KEY_FILE_UPLOAD_RESPONSE))
progress.visibility = View.GONE
sendMessageView.appendText(response.markdown)
} else {
Snackbar.make(root, R.string.failed_to_upload_file, Snackbar.LENGTH_LONG)
.show()
}
}
}
override fun onDestroy() {
App.bus().unregister(this)
super.onDestroy()
}
fun bindProject() {
toolbar.subtitle = project?.nameWithNamespace
}
fun bindIssue() {
toolbar.title = getString(R.string.issue_number) + issue?.iid
setOpenCloseMenuStatus()
textTitle.text = issue?.title
adapterIssueDetails.updateIssue(issue!!)
}
fun loadNotes() {
swipeRefreshLayout.isRefreshing = true
loading = true
App.get().gitLab.getIssueNotes(project!!.id, issue!!.id)
.compose(this.bindToLifecycle<Response<List<Note>>>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomResponseSingleObserver<List<Note>>() {
override fun error(t: Throwable) {
loading = false
Timber.e(t)
swipeRefreshLayout.isRefreshing = false
Snackbar.make(root, getString(R.string.connection_error), Snackbar.LENGTH_SHORT)
.show()
}
override fun responseSuccess(notes: List<Note>) {
loading = false
swipeRefreshLayout.isRefreshing = false
nextPageUrl = LinkHeaderParser.parse(response()).next
adapterIssueDetails.setNotes(notes)
}
})
}
fun loadMoreNotes() {
loading = true
adapterIssueDetails.setLoading(true)
App.get().gitLab.getIssueNotes(nextPageUrl!!.toString())
.compose(this.bindToLifecycle<Response<List<Note>>>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CustomResponseSingleObserver<List<Note>>() {
override fun error(t: Throwable) {
loading = false
Timber.e(t)
adapterIssueDetails.setLoading(false)
}
override fun responseSuccess(notes: List<Note>) {
loading = false
adapterIssueDetails.setLoading(false)
nextPageUrl = LinkHeaderParser.parse(response()).next
adapterIssueDetails.addNotes(notes)
}
})
}
fun postNote(message: String) {
if (message.isEmpty()) {
return
}
progress.visibility = View.VISIBLE
progress.alpha = 0.0f
progress.animate().alpha(1.0f)
// Clear text & collapse keyboard
teleprinter.hideKeyboard()
sendMessageView.clearText()
App.get().gitLab.addIssueNote(project!!.id, issue!!.id, message)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<Note>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, getString(R.string.connection_error), Snackbar.LENGTH_SHORT)
.show()
}
override fun success(note: Note) {
progress.visibility = View.GONE
adapterIssueDetails.addNote(note)
listNotes.smoothScrollToPosition(IssueDetailsAdapter.headerCount)
}
})
}
fun closeOrOpenIssue() {
progress.visibility = View.VISIBLE
if (issue!!.state == Issue.STATE_CLOSED) {
updateIssueStatus(App.get().gitLab.updateIssueStatus(project!!.id, issue!!.id, Issue.STATE_REOPEN))
} else {
updateIssueStatus(App.get().gitLab.updateIssueStatus(project!!.id, issue!!.id, Issue.STATE_CLOSE))
}
}
fun updateIssueStatus(observable: Single<Issue>) {
observable
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<Issue>() {
override fun error(t: Throwable) {
Timber.e(t)
progress.visibility = View.GONE
Snackbar.make(root, getString(R.string.error_changing_issue), Snackbar.LENGTH_SHORT)
.show()
}
override fun success(issue: Issue) {
progress.visibility = View.GONE
this@IssueActivity.issue = issue
App.bus().post(IssueChangedEvent(this@IssueActivity.issue!!))
App.bus().post(IssueReloadEvent())
setOpenCloseMenuStatus()
loadNotes()
}
})
}
fun setOpenCloseMenuStatus() {
menuItemOpenClose.setTitle(if (issue!!.state == Issue.STATE_CLOSED) R.string.reopen else R.string.close)
}
@Subscribe
fun onEvent(event: IssueChangedEvent) {
if (issue!!.id == event.issue.id) {
issue = event.issue
bindIssue()
loadNotes()
}
}
}
package com.commit451.gitlab.activity;
import android.annotation.TargetApi;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.ViewGroup;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.model.Account;
import com.commit451.gitlab.navigation.Navigator;
import com.commit451.gitlab.ssl.CustomKeyManager;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* This activity acts as switching platform for the application directing the user to the appropriate
* activity based on their logged in state
*/
public class LaunchActivity extends BaseActivity {
//Figure out how this works, then reenable
private static final boolean PRIVATE_KEY_ENABLED = false;
private static final int REQUEST_DEVICE_AUTH = 123;
@BindView(R.id.root)
ViewGroup root;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch);
ButterKnife.bind(this);
figureOutWhatToDo();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_DEVICE_AUTH:
if (resultCode == RESULT_OK) {
moveAlong();
} else {
finish();
}
break;
}
}
private void figureOutWhatToDo() {
List<Account> accounts = Account.getAccounts();
if (accounts.isEmpty()) {
Navigator.navigateToLogin(this);
finish();
} else if (App.get().getPrefs().isRequireDeviceAuth()) {
showKeyguard();
} else {
if (PRIVATE_KEY_ENABLED) {
loadPrivateKey(accounts, 0);
} else {
moveAlong();
}
}
}
@TargetApi(21)
private void showKeyguard() {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.device_auth_title), getString(R.string.device_auth_message));
if (intent == null) {
moveAlong();
} else {
startActivityForResult(intent, REQUEST_DEVICE_AUTH);
}
}
private void moveAlong() {
Navigator.navigateToStartingActivity(this);
finish();
}
private void loadPrivateKey(final List<Account> accounts, final int i) {
if (i >= accounts.size()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
return;
}
String alias = accounts.get(i).getPrivateKeyAlias();
if (alias != null && !CustomKeyManager.isCached(alias)) {
CustomKeyManager.cache(this, alias, new CustomKeyManager.KeyCallback() {
@Override
public void onSuccess(CustomKeyManager.KeyEntry entry) {
loadPrivateKey(accounts, i + 1);
}
@Override
public void onError(Exception e) {
loadPrivateKey(accounts, i + 1);
}
});
} else {
loadPrivateKey(accounts, i + 1);
}
}
}
package com.commit451.gitlab.activity
import android.annotation.TargetApi
import android.app.Activity
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.ViewGroup
import butterknife.BindView
import butterknife.ButterKnife
import com.commit451.gitlab.R
import com.commit451.gitlab.data.Prefs
import com.commit451.gitlab.model.Account
import com.commit451.gitlab.navigation.Navigator
import com.commit451.gitlab.ssl.CustomKeyManager
/**
* This activity acts as switching platform for the application directing the user to the appropriate
* activity based on their logged in state
*/
class LaunchActivity : BaseActivity() {
companion object {
//Figure out how this works, then reenable
private val PRIVATE_KEY_ENABLED = false
private val REQUEST_DEVICE_AUTH = 123
}
@BindView(R.id.root) lateinit var root: ViewGroup
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_launch)
ButterKnife.bind(this)
figureOutWhatToDo()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_DEVICE_AUTH -> if (resultCode == Activity.RESULT_OK) {
moveAlong()
} else {
finish()
}
}
}
private fun figureOutWhatToDo() {
val accounts = Account.getAccounts()
if (accounts.isEmpty()) {
Navigator.navigateToLogin(this)
finish()
} else if (Prefs.isRequiredDeviceAuth) {
showKeyguard()
} else {
if (PRIVATE_KEY_ENABLED) {
loadPrivateKey(accounts, 0)
} else {
moveAlong()
}
}
}
@TargetApi(21)
fun showKeyguard() {
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
val intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.device_auth_title), getString(R.string.device_auth_message))
if (intent == null) {
moveAlong()
} else {
startActivityForResult(intent, REQUEST_DEVICE_AUTH)
}
}
private fun moveAlong() {
Navigator.navigateToStartingActivity(this)
finish()
}
private fun loadPrivateKey(accounts: List<Account>, i: Int) {
if (i >= accounts.size) {
runOnUiThread { }
return
}
val alias = accounts[i].privateKeyAlias
if (alias != null && !CustomKeyManager.isCached(alias)) {
CustomKeyManager.cache(this, alias, object : CustomKeyManager.KeyCallback {
override fun onSuccess(entry: CustomKeyManager.KeyEntry) {
loadPrivateKey(accounts, i + 1)
}
override fun onError(e: Exception) {
loadPrivateKey(accounts, i + 1)
}
})
} else {
loadPrivateKey(accounts, i + 1)
}
}
}
package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.Toast;
import com.commit451.gitlab.App;
import com.commit451.gitlab.R;
import com.commit451.gitlab.model.api.Build;
import com.commit451.gitlab.model.api.MergeRequest;
import com.commit451.gitlab.model.api.Milestone;
import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.model.api.RepositoryCommit;
import com.commit451.gitlab.navigation.Navigator;
import com.commit451.gitlab.rx.CustomSingleObserver;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* Intermediate activity when deep linking to another activity and things need to load
*/
public class LoadSomeInfoActivity extends BaseActivity {
private static final String EXTRA_LOAD_TYPE = "load_type";
private static final String EXTRA_PROJECT_NAMESPACE = "project_namespace";
private static final String EXTRA_PROJECT_NAME = "project_name";
private static final String EXTRA_COMMIT_SHA = "extra_commit_sha";
private static final String EXTRA_MERGE_REQUEST = "merge_request";
private static final String EXTRA_BUILD_ID = "build_id";
private static final String EXTRA_MILESTONE_ID = "milestone_id";
private static final int LOAD_TYPE_DIFF = 0;
private static final int LOAD_TYPE_MERGE_REQUEST = 1;
private static final int LOAD_TYPE_BUILD = 2;
private static final int LOAD_TYPE_MILESTONE = 3;
public static Intent newIntent(Context context, String namespace, String projectName, String commitSha) {
Intent intent = new Intent(context, LoadSomeInfoActivity.class);
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_COMMIT_SHA, commitSha);
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_DIFF);
return intent;
}
public static Intent newMergeRequestIntent(Context context, String namespace, String projectName, String mergeRequestId) {
Intent intent = new Intent(context, LoadSomeInfoActivity.class);
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_MERGE_REQUEST, mergeRequestId);
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_MERGE_REQUEST);
return intent;
}
public static Intent newBuildIntent(Context context, String namespace, String projectName, long buildId) {
Intent intent = new Intent(context, LoadSomeInfoActivity.class);
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_BUILD_ID, buildId);
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_BUILD);
return intent;
}
public static Intent newMilestoneIntent(Context context, String namespace, String projectName, String milestoneIid) {
Intent intent = new Intent(context, LoadSomeInfoActivity.class);
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_MILESTONE_ID, milestoneIid);
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_MILESTONE);
return intent;
}
@BindView(R.id.progress)
View progress;
private int loadType;
private Project project;
@OnClick(R.id.root)
void onRootClicked() {
finish();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
ButterKnife.bind(this);
progress.setVisibility(View.VISIBLE);
loadType = getIntent().getIntExtra(EXTRA_LOAD_TYPE, -1);
Timber.d("Loading some info type: %d", loadType);
switch (loadType) {
case LOAD_TYPE_DIFF:
case LOAD_TYPE_MERGE_REQUEST:
case LOAD_TYPE_BUILD:
case LOAD_TYPE_MILESTONE:
String namespace = getIntent().getStringExtra(EXTRA_PROJECT_NAMESPACE);
String project = getIntent().getStringExtra(EXTRA_PROJECT_NAME);
App.get().getGitLab().getProject(namespace, project)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Project>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
LoadSomeInfoActivity.this.onError();
}
@Override
public void success(@NonNull Project project) {
loadNextPart(project);
}
});
break;
}
}
private void loadNextPart(Project response) {
project = response;
switch (loadType) {
case LOAD_TYPE_DIFF:
String sha = getIntent().getStringExtra(EXTRA_COMMIT_SHA);
App.get().getGitLab().getCommit(response.getId(), sha)
.compose(LoadSomeInfoActivity.this.<RepositoryCommit>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<RepositoryCommit>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
LoadSomeInfoActivity.this.onError();
}
@Override
public void success(@NonNull RepositoryCommit repositoryCommit) {
Navigator.navigateToDiffActivity(LoadSomeInfoActivity.this, project, repositoryCommit);
finish();
}
});
return;
case LOAD_TYPE_MERGE_REQUEST:
String mergeRequestId = getIntent().getStringExtra(EXTRA_MERGE_REQUEST);
App.get().getGitLab().getMergeRequestsByIid(response.getId(), mergeRequestId)
.compose(LoadSomeInfoActivity.this.<List<MergeRequest>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<List<MergeRequest>>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
LoadSomeInfoActivity.this.onError();
}
@Override
public void success(@NonNull List<MergeRequest> mergeRequests) {
if (!mergeRequests.isEmpty()) {
Navigator.navigateToMergeRequest(LoadSomeInfoActivity.this, project, mergeRequests.get(0));
finish();
} else {
LoadSomeInfoActivity.this.onError();
}
}
});
return;
case LOAD_TYPE_BUILD:
long buildId = getIntent().getLongExtra(EXTRA_BUILD_ID, -1);
App.get().getGitLab().getBuild(response.getId(), buildId)
.compose(LoadSomeInfoActivity.this.<Build>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<Build>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
LoadSomeInfoActivity.this.onError();
}
@Override
public void success(@NonNull Build build) {
Navigator.navigateToBuild(LoadSomeInfoActivity.this, project, build);
finish();
}
});
return;
case LOAD_TYPE_MILESTONE:
String milestoneId = getIntent().getStringExtra(EXTRA_MILESTONE_ID);
App.get().getGitLab().getMilestonesByIid(response.getId(), milestoneId)
.compose(LoadSomeInfoActivity.this.<List<Milestone>>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CustomSingleObserver<List<Milestone>>() {
@Override
public void error(@NonNull Throwable t) {
Timber.e(t);
LoadSomeInfoActivity.this.onError();
}
@Override
public void success(@NonNull List<Milestone> milestones) {
if (!milestones.isEmpty()) {
Navigator.navigateToMilestone(LoadSomeInfoActivity.this, project, milestones.get(0));
finish();
} else {
LoadSomeInfoActivity.this.onError();
}
}
});
return;
}
}
private void onError() {
Toast.makeText(LoadSomeInfoActivity.this, R.string.failed_to_load, Toast.LENGTH_SHORT)
.show();
finish();
}
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.do_nothing, R.anim.fade_out);
}
}
package com.commit451.gitlab.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import com.commit451.gitlab.App
import com.commit451.gitlab.R
import com.commit451.gitlab.extension.setup
import com.commit451.gitlab.model.api.*
import com.commit451.gitlab.navigation.Navigator
import com.commit451.gitlab.rx.CustomSingleObserver
import timber.log.Timber
/**
* Intermediate activity when deep linking to another activity and things need to load
*/
class LoadSomeInfoActivity : BaseActivity() {
companion object {
private val EXTRA_LOAD_TYPE = "load_type"
private val EXTRA_PROJECT_NAMESPACE = "project_namespace"
private val EXTRA_PROJECT_NAME = "project_name"
private val EXTRA_COMMIT_SHA = "extra_commit_sha"
private val EXTRA_MERGE_REQUEST = "merge_request"
private val EXTRA_BUILD_ID = "build_id"
private val EXTRA_MILESTONE_ID = "milestone_id"
private val LOAD_TYPE_DIFF = 0
private val LOAD_TYPE_MERGE_REQUEST = 1
private val LOAD_TYPE_BUILD = 2
private val LOAD_TYPE_MILESTONE = 3
fun newIntent(context: Context, namespace: String, projectName: String, commitSha: String): Intent {
val intent = Intent(context, LoadSomeInfoActivity::class.java)
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace)
intent.putExtra(EXTRA_PROJECT_NAME, projectName)
intent.putExtra(EXTRA_COMMIT_SHA, commitSha)
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_DIFF)
return intent
}
fun newMergeRequestIntent(context: Context, namespace: String, projectName: String, mergeRequestId: String): Intent {
val intent = Intent(context, LoadSomeInfoActivity::class.java)
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace)
intent.putExtra(EXTRA_PROJECT_NAME, projectName)
intent.putExtra(EXTRA_MERGE_REQUEST, mergeRequestId)
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_MERGE_REQUEST)
return intent
}
fun newBuildIntent(context: Context, namespace: String, projectName: String, buildId: Long): Intent {
val intent = Intent(context, LoadSomeInfoActivity::class.java)
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace)
intent.putExtra(EXTRA_PROJECT_NAME, projectName)
intent.putExtra(EXTRA_BUILD_ID, buildId)
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_BUILD)
return intent
}
fun newMilestoneIntent(context: Context, namespace: String, projectName: String, milestoneIid: String): Intent {
val intent = Intent(context, LoadSomeInfoActivity::class.java)
intent.putExtra(EXTRA_PROJECT_NAMESPACE, namespace)
intent.putExtra(EXTRA_PROJECT_NAME, projectName)
intent.putExtra(EXTRA_MILESTONE_ID, milestoneIid)
intent.putExtra(EXTRA_LOAD_TYPE, LOAD_TYPE_MILESTONE)
return intent
}
}
@BindView(R.id.progress) lateinit var progress: View
private var loadType: Int = 0
private var project: Project? = null
@OnClick(R.id.root)
fun onRootClicked() {
finish()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_loading)
ButterKnife.bind(this)
progress.visibility = View.VISIBLE
loadType = intent.getIntExtra(EXTRA_LOAD_TYPE, -1)
Timber.d("Loading some info type: %d", loadType)
when (loadType) {
LOAD_TYPE_DIFF, LOAD_TYPE_MERGE_REQUEST, LOAD_TYPE_BUILD, LOAD_TYPE_MILESTONE -> {
val namespace = intent.getStringExtra(EXTRA_PROJECT_NAMESPACE)
val project = intent.getStringExtra(EXTRA_PROJECT_NAME)
App.get().gitLab.getProject(namespace, project)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<Project>() {
override fun error(t: Throwable) {
Timber.e(t)
this@LoadSomeInfoActivity.onError()
}
override fun success(project: Project) {
loadNextPart(project)
}
})
}
}
}
override fun finish() {
super.finish()
overridePendingTransition(R.anim.do_nothing, R.anim.fade_out)
}
fun loadNextPart(response: Project) {
project = response
when (loadType) {
LOAD_TYPE_DIFF -> {
val sha = intent.getStringExtra(EXTRA_COMMIT_SHA)
App.get().gitLab.getCommit(response.id, sha)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<RepositoryCommit>() {
override fun error(t: Throwable) {
Timber.e(t)
this@LoadSomeInfoActivity.onError()
}
override fun success(repositoryCommit: RepositoryCommit) {
Navigator.navigateToDiffActivity(this@LoadSomeInfoActivity, project!!, repositoryCommit)
finish()
}
})
return
}
LOAD_TYPE_MERGE_REQUEST -> {
val mergeRequestId = intent.getStringExtra(EXTRA_MERGE_REQUEST)
App.get().gitLab.getMergeRequestsByIid(response.id, mergeRequestId)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<List<MergeRequest>>() {
override fun error(t: Throwable) {
Timber.e(t)
this@LoadSomeInfoActivity.onError()
}
override fun success(mergeRequests: List<MergeRequest>) {
if (!mergeRequests.isEmpty()) {
Navigator.navigateToMergeRequest(this@LoadSomeInfoActivity, project!!, mergeRequests[0])
finish()
} else {
this@LoadSomeInfoActivity.onError()
}
}
})
return
}
LOAD_TYPE_BUILD -> {
val buildId = intent.getLongExtra(EXTRA_BUILD_ID, -1)
App.get().gitLab.getBuild(response.id, buildId)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<Build>() {
override fun error(t: Throwable) {
Timber.e(t)
this@LoadSomeInfoActivity.onError()
}
override fun success(build: Build) {
Navigator.navigateToBuild(this@LoadSomeInfoActivity, project!!, build)
finish()
}
})
return
}
LOAD_TYPE_MILESTONE -> {
val milestoneId = intent.getStringExtra(EXTRA_MILESTONE_ID)
App.get().gitLab.getMilestonesByIid(response.id, milestoneId)
.setup(bindToLifecycle())
.subscribe(object : CustomSingleObserver<List<Milestone>>() {
override fun error(t: Throwable) {
Timber.e(t)
this@LoadSomeInfoActivity.onError()
}
override fun success(milestones: List<Milestone>) {
if (!milestones.isEmpty()) {
Navigator.navigateToMilestone(this@LoadSomeInfoActivity, project!!, milestones[0])
finish()
} else {
this@LoadSomeInfoActivity.onError()
}
}
})
return
}
}
}
fun onError() {
Toast.makeText(this@LoadSomeInfoActivity, R.string.failed_to_load, Toast.LENGTH_SHORT)
.show()
finish()
}
}
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