Skip to content
Snippets Groups Projects
Commit cc5fea52 authored by Michi302's avatar Michi302
Browse files

Merge branch 'master' into fdroid

# Conflicts:
#	app/src/main/java/com/commit451/gitlab/LabCoatApp.java
#	app/src/test/java/com/commit451/gitlab/TestLabCoatApp.java
parents e3c730d9 2995603f
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 364 additions and 46 deletions
Loading
Loading
@@ -10,8 +10,8 @@ android {
minSdkVersion 16
targetSdkVersion 23
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionCode 222
versionName "2.2.2"
versionCode 224
versionName "2.2.4"
}
buildTypes {
release {
Loading
Loading
File deleted
Loading
Loading
@@ -3,6 +3,7 @@ package com.commit451.gitlab;
import android.app.Application;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.annotation.VisibleForTesting;
 
import com.squareup.leakcanary.LeakCanary;
import com.squareup.otto.Bus;
Loading
Loading
@@ -37,12 +38,17 @@ public class LabCoatApp extends Application {
sInstance = this;
 
forceLocale(Locale.ENGLISH);
setupLeakCanary();
 
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
 
JodaTimeAndroid.init(this);
}
@VisibleForTesting
protected void setupLeakCanary() {
LeakCanary.install(this);
}
 
Loading
Loading
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.util.Base64;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;
 
Loading
Loading
@@ -22,6 +29,8 @@ import com.commit451.gitlab.model.api.RepositoryFile;
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.Bind;
Loading
Loading
@@ -32,11 +41,20 @@ import retrofit.Retrofit;
import timber.log.Timber;
 
public class FileActivity extends BaseActivity {
private static final int PERMISSION_REQUEST_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);
Loading
Loading
@@ -45,6 +63,7 @@ public class FileActivity extends BaseActivity {
return intent;
}
 
@Bind(R.id.root) ViewGroup mRoot;
@Bind(R.id.toolbar) Toolbar mToolbar;
@Bind(R.id.file_blob) WebView mFileBlobView;
@Bind(R.id.progress) View mProgressView;
Loading
Loading
@@ -52,9 +71,9 @@ public class FileActivity extends BaseActivity {
private long mProjectId;
private String mPath;
private String mRef;
private String mFileName;
private byte[] mBlob;
private @Option int mOption;
 
private final Callback<RepositoryFile> mFileResponseCallback = new Callback<RepositoryFile>() {
 
Loading
Loading
@@ -135,10 +154,12 @@ public class FileActivity extends BaseActivity {
public boolean onMenuItemClick(MenuItem item) {
switch(item.getItemId()) {
case R.id.action_open:
openFile();
mOption = OPTION_OPEN;
checkAccountPermission();
return true;
case R.id.action_save:
saveBlob();
mOption = OPTION_SAVE;
checkAccountPermission();
return true;
}
return false;
Loading
Loading
@@ -153,6 +174,34 @@ public class FileActivity extends BaseActivity {
GitLabClient.instance().getFile(mProjectId, mPath, mRef).enqueue(mFileResponseCallback);
}
 
@TargetApi(23)
private void checkAccountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if (mOption == OPTION_SAVE) {
saveBlob();
} else {
openFile();
}
} else {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_WRITE_STORAGE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_WRITE_STORAGE: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (mOption == OPTION_SAVE) {
saveBlob();
} else {
openFile();
}
}
}
}
}
private File saveBlob() {
String state = Environment.getExternalStorageState();
 
Loading
Loading
@@ -177,6 +226,7 @@ public class FileActivity extends BaseActivity {
try {
outputStream.close();
} catch (IOException e) {
Timber.e(e, null);
}
}
}
Loading
Loading
Loading
Loading
@@ -31,7 +31,14 @@ public class AuthenticationRequestInterceptor implements Interceptor {
Request request = chain.request();
 
HttpUrl url = request.httpUrl();
if (url.toString().startsWith(mAccount.getServerUrl().toString())) {
String cleanUrl = url.toString();
cleanUrl = cleanUrl.substring(cleanUrl.indexOf(':'));
String cleanServerUrl = mAccount.getServerUrl().toString();
cleanServerUrl = cleanServerUrl.substring(cleanServerUrl.indexOf(':'));
if (cleanUrl.startsWith(cleanServerUrl)) {
String authorizationHeader = mAccount.getAuthorizationHeader();
if (authorizationHeader != null) {
request = request.newBuilder()
Loading
Loading
Loading
Loading
@@ -16,6 +16,7 @@ import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.model.api.RepositoryCommit;
import com.commit451.gitlab.model.api.RepositoryFile;
import com.commit451.gitlab.model.api.RepositoryTreeObject;
import com.commit451.gitlab.model.api.User;
import com.commit451.gitlab.model.api.UserBasic;
import com.commit451.gitlab.model.api.UserFull;
import com.commit451.gitlab.model.api.UserLogin;
Loading
Loading
@@ -73,7 +74,7 @@ public interface GitLab {
Call<List<UserBasic>> searchUsers(@Url String url, @Query("search") String query);
 
@GET(API_VERSION + "/users/{id}")
Call<UserBasic> getUser(@Path("id") long userId);
Call<User> getUser(@Path("id") long userId);
 
/* --- GROUPS --- */
 
Loading
Loading
Loading
Loading
@@ -16,7 +16,7 @@ import com.commit451.gitlab.adapter.FeedAdapter;
import com.commit451.gitlab.api.GitLabClient;
import com.commit451.gitlab.model.rss.Entry;
import com.commit451.gitlab.model.rss.Feed;
import com.commit451.gitlab.util.IntentUtil;
import com.commit451.gitlab.util.NavigationManager;
 
import butterknife.Bind;
import butterknife.ButterKnife;
Loading
Loading
@@ -93,7 +93,7 @@ public class FeedFragment extends BaseFragment {
private final FeedAdapter.Listener mFeedAdapterListener = new FeedAdapter.Listener() {
@Override
public void onFeedEntryClicked(Entry entry) {
IntentUtil.openPage(getActivity(), entry.getLink().getHref().toString());
NavigationManager.navigateToUrl(getActivity(), entry.getLink().getHref(), GitLabClient.getAccount());
}
};
 
Loading
Loading
Loading
Loading
@@ -3,43 +3,67 @@ package com.commit451.gitlab.transformation;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
 
import com.squareup.picasso.Transformation;
 
/**
* https://gist.github.com/julianshen/5829333
* Copyright (C) 2015 Wasabeef
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* https://github.com/wasabeef/picasso-transformations/blob/master/transformations/src/main/java/jp/wasabeef/picasso/transformations/CropCircleTransformation.java
*/
public class CircleTransformation implements Transformation {
@Override
public Bitmap transform(Bitmap source) {
if (source == null || (source.getWidth() == 0 && source.getHeight() == 0)) {
return source;
}
int size = Math.min(source.getWidth(), source.getHeight());
 
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
int width = (source.getWidth() - size) / 2;
int height = (source.getHeight() - size) / 2;
 
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
 
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
BitmapShader shader =
new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
if (width != 0 || height != 0) {
// source isn't square, move viewport to center
Matrix matrix = new Matrix();
matrix.setTranslate(-width, -height);
shader.setLocalMatrix(matrix);
}
paint.setShader(shader);
paint.setAntiAlias(true);
 
float r = size/2f;
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
 
squaredBitmap.recycle();
source.recycle();
return bitmap;
}
 
@Override
public String key() {
return "circle";
return "CropCircleTransformation()";
}
}
\ No newline at end of file
package com.commit451.gitlab.util;
import android.net.Uri;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class Gravatar {
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
private Gravatar() {}
public static Builder init() {
return init(null);
}
public static Builder init(String email) {
if (email != null && email.isEmpty()) {
email = null;
}
return new Builder(email);
}
private static String hexify(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
int v = bytes[i] & 0xFF;
hexChars[i * 2] = HEX_ARRAY[v >>> 4];
hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, 0, hexChars.length);
}
private static String md5(String raw) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(raw.getBytes(Charset.forName("UTF-8")));
return hexify(digest.digest());
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
public static class Builder {
private final String mEmail;
private boolean mSSL = false;
private boolean mExtension = false;
private int mSize = -1;
private String mDefaultImage = null;
private boolean mForceDefault = false;
private String mRating = null;
private Builder(String email) {
this.mEmail = email;
}
public Builder ssl() {
mSSL = true;
return this;
}
public Builder extension() {
mExtension = true;
return this;
}
public Builder size(int size) {
if (size < 1 || size > 2048) {
throw new IllegalArgumentException("Image size must be from 1px up to 2048px");
}
mSize = size;
return this;
}
public Builder defaultImage(DefaultImage defaultImage) {
switch (defaultImage) {
case _404:
mDefaultImage = "404";
break;
case MYSTERY_MAN:
mDefaultImage = "mm";
break;
case IDENTICON:
mDefaultImage = "identicon";
break;
case MONSTERID:
mDefaultImage = "monsterid";
break;
case WAVATAR:
mDefaultImage = "wavatar";
break;
case RETRO:
mDefaultImage = "retro";
break;
case BLANK:
mDefaultImage = "blank";
break;
}
return this;
}
public Builder defaultImage(Uri defaultImage) {
try {
mDefaultImage = URLEncoder.encode(defaultImage.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
return this;
}
public Builder forceDefault() {
mForceDefault = true;
return this;
}
public Builder rating(Rating rating) {
switch (rating) {
case G:
mRating = "g";
break;
case PG:
mRating = "pg";
break;
case R:
mRating = "r";
break;
case X:
mRating = "x";
break;
}
return this;
}
public Uri build() {
StringBuilder uriBuilder = new StringBuilder();
if (mSSL) {
uriBuilder.append("https://secure.gravatar.com/avatar/");
} else {
uriBuilder.append("http://www.gravatar.com/avatar/");
}
if (mEmail != null) {
uriBuilder.append(md5(mEmail));
} else {
uriBuilder.append("00000000000000000000000000000000");
}
if (mExtension) {
uriBuilder.append(".jpg");
}
StringBuilder queryBuilder = new StringBuilder();
if (mSize != -1) {
queryBuilder.append("&s=").append(mSize);
}
if (mDefaultImage != null) {
queryBuilder.append("&d=").append(mDefaultImage);
}
if (mForceDefault) {
queryBuilder.append("&f=y");
}
if (mRating != null) {
queryBuilder.append("&r=").append(mRating);
}
String query = queryBuilder.toString();
if (query.length() > 0) {
uriBuilder.append("?").append(query.substring(1));
}
return Uri.parse(uriBuilder.toString());
}
}
public enum Rating {
G, PG, R, X
}
public enum DefaultImage {
_404, MYSTERY_MAN, IDENTICON, MONSTERID, WAVATAR, RETRO, BLANK
}
}
package com.commit451.gitlab.util;
 
import android.net.Uri;
import android.text.TextUtils;
 
import com.commit451.gitlab.model.api.UserBasic;
import com.commit451.gitlab.model.api.UserFull;
 
import fr.tkeunebr.gravatar.Gravatar;
/**
* Utility for doing various image related things
* Created by Jawn on 9/20/2015.
*/
public class ImageUtil {
private static Gravatar sGravatar;
public static Uri getAvatarUrl(UserBasic user, int size) {
if (user != null) {
Uri avatarUrl = user.getAvatarUrl();
Loading
Loading
@@ -33,14 +28,11 @@ public class ImageUtil {
}
 
public static Uri getAvatarUrl(String email, int size) {
if (sGravatar == null) {
sGravatar = new Gravatar.Builder().ssl().build();
}
if (!TextUtils.isEmpty(email)) {
return Uri.parse(sGravatar.with(email).size(size).defaultImage(Gravatar.DefaultImage.IDENTICON).build());
}
return Uri.parse("https://secure.gravatar.com/avatar/00000000000000000000000000000000?d=identicon&f=y&s=" + size);
return Gravatar
.init(email)
.ssl()
.size(size)
.defaultImage(Gravatar.DefaultImage.IDENTICON)
.build();
}
}
Loading
Loading
@@ -3,6 +3,7 @@ package com.commit451.gitlab.util;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.ActivityOptionsCompat;
import android.view.View;
Loading
Loading
@@ -10,6 +11,7 @@ import android.widget.ImageView;
 
import com.commit451.gitlab.R;
import com.commit451.gitlab.activity.AboutActivity;
import com.commit451.gitlab.activity.AddIssueActivity;
import com.commit451.gitlab.activity.AddMilestoneActivity;
import com.commit451.gitlab.activity.AddUserActivity;
import com.commit451.gitlab.activity.FileActivity;
Loading
Loading
@@ -23,7 +25,7 @@ import com.commit451.gitlab.activity.ProjectActivity;
import com.commit451.gitlab.activity.ProjectsActivity;
import com.commit451.gitlab.activity.SearchActivity;
import com.commit451.gitlab.activity.UserActivity;
import com.commit451.gitlab.activity.AddIssueActivity;
import com.commit451.gitlab.model.Account;
import com.commit451.gitlab.model.api.Group;
import com.commit451.gitlab.model.api.Issue;
import com.commit451.gitlab.model.api.MergeRequest;
Loading
Loading
@@ -31,6 +33,10 @@ import com.commit451.gitlab.model.api.Milestone;
import com.commit451.gitlab.model.api.Project;
import com.commit451.gitlab.model.api.UserBasic;
 
import java.util.List;
import timber.log.Timber;
/**
* Manages navigation so that we can override things as needed
* Created by Jawn on 9/21/2015.
Loading
Loading
@@ -155,4 +161,42 @@ public class NavigationManager {
activity.overridePendingTransition(R.anim.fade_in, R.anim.do_nothing);
}
}
public static void navigateToUrl(Activity activity, Uri uri, Account account) {
Timber.d("navigateToUrl: %s", uri);
if (account.getServerUrl().getHost().equals(uri.getHost())) {
boolean handled = navigateToUrl(activity, uri);
if (!handled) {
IntentUtil.openPage(activity, uri.toString());
}
} else {
IntentUtil.openPage(activity, uri.toString());
}
}
/**
* Attempts to map a url to an activity within the app
* @param activity the current activity
* @param uri the url we want to map
* @return true if we navigated somewhere, false otherwise
*/
private static boolean navigateToUrl(Activity activity, Uri uri) {
//TODO figure out the url to activity mapping
if (uri.getPath().contains("issues")) {
List<String> pathSegments = uri.getPathSegments();
for (int i=0; i<pathSegments.size(); i++) {
//segment == issues, and there is one more segment in the path
if (pathSegments.get(i).equals("issues") && i != pathSegments.size()-1) {
//TODO this would probably break if we had query params or anything else in the url
String issueId = pathSegments.get(i+1);
//TODO actually navigate to issue activity which will load the needed project and issue
//navigateToIssue(activity, null, issueId);
return true;
}
}
navigateToProject(activity, -1);
return true;
}
return false;
}
}
Loading
Loading
@@ -2,6 +2,7 @@
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FileActivity" >
Loading
Loading
Loading
Loading
@@ -19,10 +19,10 @@
android:id="@+id/creator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:textSize="14sp"
android:background="?attr/selectableItemBackground"
tools:text="Created by Commit451"/>
Loading
Loading
@@ -31,7 +31,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="16dp">
android:layout_margin="16dp"
android:baselineAligned="false">
 
<LinearLayout
android:layout_width="0dp"
Loading
Loading
Loading
Loading
@@ -153,7 +153,7 @@
<string name="close">Close</string>
<string name="created_issue">created issue</string>
<string name="error_changing_issue">There was an error changing the issue</string>
<string name="no_issues">No issues. Good job?</string>
<string name="no_issues">No issues</string>
<string name="issue_opened">Opened</string>
<string name="issue_closed">Closed</string>
<string name="issue_all">All</string>
Loading
Loading
@@ -227,7 +227,7 @@
<string name="about">About</string>
<string name="contributors">Contributors</string>
<string name="sauce">See the Sauce</string>
<string name="source_url">https://gitlab.com/Commit451/GitLabAndroid</string>
<string name="source_url">https://gitlab.com/Commit451/LabCoat</string>
<string name="failed_to_load_contributors">Failed to load contributors :(</string>
 
<!-- Account -->
Loading
Loading
Loading
Loading
@@ -23,4 +23,9 @@ public class TestLabCoatApp extends LabCoatApp implements TestLifecycleApplicati
public void afterTest(Method method) {
 
}
@Override
protected void setupLeakCanary() {
//Intentionally left blank
}
}
Loading
Loading
@@ -4,7 +4,6 @@ buildscript {
repositories {
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://dl.bintray.com/commit451/maven"}
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
Loading
Loading
@@ -18,6 +17,5 @@ allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://dl.bintray.com/commit451/maven"}
}
}
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