Skip to content
Snippets Groups Projects
Commit ade2c4cf authored by Jawnnypoo's avatar Jawnnypoo
Browse files

Merge branch 'develop'

parents eb09a2b1 1e4317f3
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 107 additions and 948 deletions
Loading
Loading
@@ -15,7 +15,6 @@ import java.util.Collection;
 
/**
* Shows the files
* Created by Jawnnypoo on 11/22/2015.
*/
public class FilesAdapter extends RecyclerView.Adapter<FileViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -13,7 +13,6 @@ import java.util.Collection;
 
/**
* All the groups
* Created by John on 10/8/15.
*/
public class GroupAdapter extends RecyclerView.Adapter<GroupViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,6 @@ import java.util.Collection;
 
/**
* Adapter for a list of users
* Created by John on 9/28/15.
*/
public class GroupMembersAdapter extends RecyclerView.Adapter<ProjectMemberViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -16,7 +16,6 @@ import java.util.Set;
 
/**
* Group pager adapter
* Created by Jawn on 9/21/2015.
*/
public class GroupPagerAdapter extends FragmentPagerAdapter {
 
Loading
Loading
Loading
Loading
@@ -18,7 +18,6 @@ import in.uncod.android.bypass.Bypass;
 
/**
* Nice notes
* Created by Jawn on 8/6/2015.
*/
public class IssueDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -14,7 +14,6 @@ import java.util.Collection;
 
/**
* Issues adapter
* Created by Jawn on 7/28/2015.
*/
public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -18,7 +18,6 @@ import java.util.Collection;
 
/**
* Shows a projects members and a groups members
* Created by Jawn on 7/28/2015.
*/
public class MemberAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -15,7 +15,6 @@ import java.util.List;
 
/**
* Merge request adapter!
* Created by Jawn on 9/20/2015.
*/
public class MergeRequestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
 
Loading
Loading
Loading
Loading
@@ -17,7 +17,6 @@ import in.uncod.android.bypass.Bypass;
 
/**
* Shows the comments and details of a merge request
* Created by John on 11/16/15.
*/
public class MergeRequestDetailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -11,7 +11,6 @@ import com.commit451.gitlab.fragment.UsersFragment;
 
/**
* The pager that controls the fragments when on the search activity
* Created by Jawn on 9/21/2015.
*/
public class SearchPagerAdapter extends FragmentPagerAdapter {
 
Loading
Loading
Loading
Loading
@@ -6,6 +6,7 @@ import android.support.v4.app.FragmentPagerAdapter;
 
import com.commit451.gitlab.R;
import com.commit451.gitlab.activity.ProjectActivity;
import com.commit451.gitlab.fragment.BuildsFragment;
import com.commit451.gitlab.fragment.CommitsFragment;
import com.commit451.gitlab.fragment.FeedFragment;
import com.commit451.gitlab.fragment.FilesFragment;
Loading
Loading
@@ -13,27 +14,29 @@ import com.commit451.gitlab.fragment.IssuesFragment;
import com.commit451.gitlab.fragment.MilestonesFragment;
import com.commit451.gitlab.fragment.ProjectMembersFragment;
import com.commit451.gitlab.fragment.MergeRequestsFragment;
import com.commit451.gitlab.fragment.OverviewFragment;
import com.commit451.gitlab.fragment.ProjectFragment;
import com.commit451.gitlab.model.api.Project;
 
import java.util.HashSet;
import java.util.Set;
 
import timber.log.Timber;
/**
* Controls the sections that should be shown in a {@link com.commit451.gitlab.activity.ProjectActivity}
* Created by Jawn on 9/20/2015.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
 
private static final int SECTION_COUNT = 8;
private static final int OVERVIEW_POS = 0;
private static final int SECTION_COUNT = 9;
private static final int PROJECT_POS = 0;
private static final int ACTIVITY_POS = 1;
private static final int FILES_POS = 2;
private static final int COMMITS_POS = 3;
private static final int MILESTONES_POS = 4;
private static final int ISSUES_POS = 5;
private static final int MERGE_REQUESTS_POS = 6;
private static final int PROJECT_MEMBERS_POS = 7;
private static final int BUILDS_POS = 4;
private static final int MILESTONES_POS = 5;
private static final int ISSUES_POS = 6;
private static final int MERGE_REQUESTS_POS = 7;
private static final int PROJECT_MEMBERS_POS = 8;
 
private final Project mProject;
private final String[] mTitles;
Loading
Loading
@@ -46,13 +49,20 @@ public class SectionsPagerAdapter extends FragmentPagerAdapter {
mTitles = context.getResources().getStringArray(R.array.main_tabs);
 
Project project = context.getProject();
if (!project.isBuildEnabled()) {
Timber.d("Builds are disabled");
mDisabledSections.add(BUILDS_POS);
}
if (!project.isIssuesEnabled()) {
Timber.d("Issues are disabled");
mDisabledSections.add(ISSUES_POS);
}
if (!project.isMergeRequestsEnabled()) {
Timber.d("Merge requests are disabled");
mDisabledSections.add(MERGE_REQUESTS_POS);
}
if (!project.isIssuesEnabled() && !project.isMergeRequestsEnabled()) {
Timber.d("Milestones are disabled");
mDisabledSections.add(MILESTONES_POS);
}
}
Loading
Loading
@@ -74,14 +84,16 @@ public class SectionsPagerAdapter extends FragmentPagerAdapter {
position = getCorrectPosition(position);
 
switch (position) {
case OVERVIEW_POS:
return OverviewFragment.newInstance();
case PROJECT_POS:
return ProjectFragment.newInstance();
case ACTIVITY_POS:
return FeedFragment.newInstance(mProject.getFeedUrl());
case FILES_POS:
return FilesFragment.newInstance();
case COMMITS_POS:
return CommitsFragment.newInstance();
case BUILDS_POS:
return BuildsFragment.newInstance();
case MILESTONES_POS:
return MilestonesFragment.newInstance();
case ISSUES_POS:
Loading
Loading
/*
* Copyright (C) 2006 The Android Open Source Project
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.commit451.gitlab.adapter;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.support.annotation.ArrayRes;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.v7.view.ContextThemeWrapper;
import android.support.v7.widget.ThemedSpinnerAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import com.afollestad.appthemeengine.Config;
import com.afollestad.appthemeengine.util.ATEUtil;
import com.commit451.gitlab.util.AppThemeUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* An Adapter derived from ArrayAdapter that also allows theming with AppThemeEngine
*/
public class ThemedArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
/**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is also
* used by the filter (see {@link #getFilter()} to make a synchronized copy of
* the original array of data.
*/
private final Object mLock = new Object();
private final LayoutInflater mInflater;
/**
* Contains the list of objects that represent the data of this ThemedArrayAdapter.
* The content of this list is referred to as "the array" in the documentation.
*/
private List<T> mObjects;
/**
* The resource indicating what views to inflate to display the content of this
* array adapter.
*/
private int mResource;
/**
* The resource indicating what views to inflate to display the content of this
* array adapter in a drop down widget.
*/
private int mDropDownResource;
/**
* If the inflated resource is not a TextView, {@link #mFieldId} is used to find
* a TextView inside the inflated views hierarchy. This field must contain the
* identifier that matches the one defined in the resource file.
*/
private int mFieldId = 0;
/**
* Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
* {@link #mObjects} is modified.
*/
private boolean mNotifyOnChange = true;
private Context mContext;
// A copy of the original mObjects array, initialized from and then used instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;
/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource) {
this(context, resource, 0, new ArrayList<T>());
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a layout to use when
* instantiating views.
* @param textViewResourceId The id of the TextView within the layout resource to be populated
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
this(context, resource, textViewResourceId, new ArrayList<T>());
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
* @param objects The objects to represent in the ListView.
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource, @NonNull T[] objects) {
this(context, resource, 0, Arrays.asList(objects));
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a layout to use when
* instantiating views.
* @param textViewResourceId The id of the TextView within the layout resource to be populated
* @param objects The objects to represent in the ListView.
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
@NonNull T[] objects) {
this(context, resource, textViewResourceId, Arrays.asList(objects));
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
* @param objects The objects to represent in the ListView.
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource, @NonNull List<T> objects) {
this(context, resource, 0, objects);
}
/**
* Constructor
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a layout to use when
* instantiating views.
* @param textViewResourceId The id of the TextView within the layout resource to be populated
* @param objects The objects to represent in the ListView.
*/
public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
@NonNull List<T> objects) {
mContext = context;
mInflater = LayoutInflater.from(context);
mResource = mDropDownResource = resource;
mObjects = objects;
mFieldId = textViewResourceId;
}
/**
* Adds the specified object at the end of the array.
*
* @param object The object to add at the end of the array.
*/
public void add(T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(object);
} else {
mObjects.add(object);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Adds the specified Collection at the end of the array.
*
* @param collection The Collection to add at the end of the array.
*/
public void addAll(Collection<? extends T> collection) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.addAll(collection);
} else {
mObjects.addAll(collection);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Adds the specified items at the end of the array.
*
* @param items The items to add at the end of the array.
*/
public void addAll(T ... items) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.addAll(mOriginalValues, items);
} else {
Collections.addAll(mObjects, items);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Inserts the specified object at the specified index in the array.
*
* @param object The object to insert into the array.
* @param index The index at which the object must be inserted.
*/
public void insert(T object, int index) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(index, object);
} else {
mObjects.add(index, object);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Removes the specified object from the array.
*
* @param object The object to remove.
*/
public void remove(T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.remove(object);
} else {
mObjects.remove(object);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Remove all elements from the list.
*/
public void clear() {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.clear();
} else {
mObjects.clear();
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* Sorts the content of this adapter using the specified comparator.
*
* @param comparator The comparator used to sort the objects contained
* in this adapter.
*/
public void sort(Comparator<? super T> comparator) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.sort(mOriginalValues, comparator);
} else {
Collections.sort(mObjects, comparator);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
/**
* {@inheritDoc}
*/
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mNotifyOnChange = true;
}
/**
* Control whether methods that change the list ({@link #add},
* {@link #insert}, {@link #remove}, {@link #clear}) automatically call
* {@link #notifyDataSetChanged}. If set to false, caller must
* manually call notifyDataSetChanged() to have the changes
* reflected in the attached view.
*
* The default is true, and calling notifyDataSetChanged()
* resets the flag to true.
*
* @param notifyOnChange if true, modifications to the list will
* automatically call {@link
* #notifyDataSetChanged}
*/
public void setNotifyOnChange(boolean notifyOnChange) {
mNotifyOnChange = notifyOnChange;
}
/**
* Returns the context associated with this array adapter. The context is used
* to create views from the resource passed to the constructor.
*
* @return The Context associated with this adapter.
*/
public Context getContext() {
return mContext;
}
/**
* {@inheritDoc}
*/
public int getCount() {
return mObjects.size();
}
/**
* {@inheritDoc}
*/
public T getItem(int position) {
return mObjects.get(position);
}
/**
* Returns the position of the specified item in the array.
*
* @param item The item to retrieve the position of.
*
* @return The position of the specified item.
*/
public int getPosition(T item) {
return mObjects.indexOf(item);
}
/**
* {@inheritDoc}
*/
public long getItemId(int position) {
return position;
}
/**
* {@inheritDoc}
*/
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(mInflater, position, convertView, parent, mResource, false);
}
private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
ViewGroup parent, int resource, boolean dropdown) {
View view;
TextView text;
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("ThemedArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ThemedArrayAdapter requires the resource ID to be a TextView", e);
}
// Theme the TextView with AppThemeEngine values
if (dropdown) {
text.setTextColor(Config.textColorPrimary(view.getContext(),
AppThemeUtil.resolveThemeKey(view.getContext())));
} else {
text.setTextColor(ATEUtil.isColorLight(Config.primaryColor(view.getContext(),
AppThemeUtil.resolveThemeKey(view.getContext()))) ? Color.BLACK : Color.WHITE);
}
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
}
return view;
}
/**
* <p>Sets the layout resource to create the drop down views.</p>
*
* @param resource the layout resource defining the drop down views
* @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
*/
public void setDropDownViewResource(@LayoutRes int resource) {
this.mDropDownResource = resource;
}
/**
* Sets the {@link Resources.Theme} against which drop-down views are
* inflated.
* <p>
* By default, drop-down views are inflated against the theme of the
* {@link Context} passed to the adapter's constructor.
*
* @param theme the theme against which to inflate drop-down views or
* {@code null} to use the theme from the adapter's context
* @see #getDropDownView(int, View, ViewGroup)
*/
@Override
public void setDropDownViewTheme(Resources.Theme theme) {
if (theme == null) {
mDropDownInflater = null;
} else if (theme == mInflater.getContext().getTheme()) {
mDropDownInflater = mInflater;
} else {
final Context context = new ContextThemeWrapper(mContext, theme);
mDropDownInflater = LayoutInflater.from(context);
}
}
@Override
public Resources.Theme getDropDownViewTheme() {
return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
return createViewFromResource(inflater, position, convertView, parent, mDropDownResource, true);
}
/**
* Creates a new ThemedArrayAdapter from external resources. The content of the array is
* obtained through {@link android.content.res.Resources#getTextArray(int)}.
*
* @param context The application's environment.
* @param textArrayResId The identifier of the array to use as the data source.
* @param textViewResId The identifier of the layout used to create views.
*
* @return An ThemedArrayAdapter<CharSequence>.
*/
public static ThemedArrayAdapter<CharSequence> createFromResource(Context context,
@ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
return new ThemedArrayAdapter<CharSequence>(context, textViewResId, strings);
}
/**
* {@inheritDoc}
*/
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
\ No newline at end of file
Loading
Loading
@@ -15,7 +15,6 @@ import java.util.Collection;
 
/**
* Adapter for a list of users
* Created by John on 9/28/15.
*/
public class UsersAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
Loading
Loading
Loading
Loading
@@ -12,7 +12,6 @@ import timber.log.Timber;
 
/**
* Adds the private token to all requests
* Created by Jawn on 9/15/2015.
*/
public class AuthenticationRequestInterceptor implements Interceptor {
 
Loading
Loading
Loading
Loading
@@ -2,9 +2,12 @@ package com.commit451.gitlab.api;
 
import android.support.annotation.Nullable;
 
import com.commit451.gitlab.model.api.Artifact;
import com.commit451.gitlab.model.api.Branch;
import com.commit451.gitlab.model.api.Build;
import com.commit451.gitlab.model.api.Contributor;
import com.commit451.gitlab.model.api.Diff;
import com.commit451.gitlab.model.api.FileUploadResponse;
import com.commit451.gitlab.model.api.Group;
import com.commit451.gitlab.model.api.GroupDetail;
import com.commit451.gitlab.model.api.Issue;
Loading
Loading
@@ -24,7 +27,9 @@ import com.commit451.gitlab.model.api.UserLogin;
 
import java.util.List;
 
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
Loading
Loading
@@ -123,7 +128,12 @@ public interface GitLab {
Call<List<Project>> getStarredProjects();
 
@GET(API_VERSION + "/projects/{id}")
Call<Project> getProject(@Path("id") long projectId);
Call<Project> getProject(@Path("id") String projectId);
// see https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md#get-single-project
@GET(API_VERSION + "/projects/{namespace}%2F{project_name}")
Call<Project> getProject(@Path("namespace") String namespace,
@Path("project_name") String projectName);
 
@GET
Call<List<Project>> getProjects(@Url String url);
Loading
Loading
@@ -153,6 +163,20 @@ public interface GitLab {
Call<Void> removeProjectMember(@Path("id") long projectId,
@Path("user_id") long userId);
 
@POST(API_VERSION + "/projects/fork/{id}")
Call<Void> forkProject(@Path("id") long projectId);
@POST(API_VERSION + "/projects/{id}/star")
Call<Project> starProject(@Path("id") long projectId);
@DELETE(API_VERSION + "/projects/{id}/star")
Call<Project> unstarProject(@Path("id") long projectId);
//@Multipart
@POST(API_VERSION + "/projects/{id}/uploads")
Call<FileUploadResponse> uploadFile(@Path("id") long projectId,
@Body RequestBody file);
/* --- MILESTONES --- */
 
@GET(API_VERSION + "/projects/{id}/milestones")
Loading
Loading
@@ -163,29 +187,30 @@ public interface GitLab {
 
@GET(API_VERSION + "/projects/{id}/milestones/{milestone_id}/issues")
Call<List<Issue>> getMilestoneIssues(@Path("id") long projectId,
@Path("milestone_id") long milestoneId);
@Path("milestone_id") long milestoneId);
@GET
Call<List<Issue>> getMilestoneIssues(@Url String url);
 
@FormUrlEncoded
@POST(API_VERSION + "/projects/{id}/milestones")
Call<Milestone> createMilestone(@Path("id") long projectId,
@Field("title") String title,
@Field("description") String description,
@Field("due_date") String dueDate);
@Field("title") String title,
@Field("description") String description,
@Field("due_date") String dueDate);
 
@FormUrlEncoded
@PUT(API_VERSION + "/projects/{id}/milestones/{milestone_id}")
Call<Milestone> editMilestone(@Path("id") long projectId,
@Path("milestone_id") long milestoneId,
@Field("title") String title,
@Field("description") String description,
@Field("due_date") String dueDate);
@Path("milestone_id") long milestoneId,
@Field("title") String title,
@Field("description") String description,
@Field("due_date") String dueDate);
 
@PUT(API_VERSION + "/projects/{id}/milestones/{milestone_id}")
Call<Milestone> updateMilestoneStatus(@Path("id") long projectId,
@Path("milestone_id") long milestoneId,
@Query("state_event") @Milestone.StateEvent String status);
@Path("milestone_id") long milestoneId,
@Query("state_event") @Milestone.StateEvent String status);
 
/* --- MERGE REQUESTS --- */
 
Loading
Loading
@@ -207,11 +232,11 @@ public interface GitLab {
 
@GET(API_VERSION + "/projects/{id}/merge_requests/{merge_request_id}/commits")
Call<List<RepositoryCommit>> getMergeRequestCommits(@Path("id") long projectId,
@Path("merge_request_id") long mergeRequestId);
@Path("merge_request_id") long mergeRequestId);
 
@GET(API_VERSION + "/projects/{id}/merge_requests/{merge_request_id}/changes")
Call<MergeRequest> getMergeRequestChanges(@Path("id") long projectId,
@Path("merge_request_id") long mergeRequestId);
@Path("merge_request_id") long mergeRequestId);
 
@GET
Call<List<Note>> getMergeRequestNotes(@Url String url);
Loading
Loading
@@ -222,6 +247,10 @@ public interface GitLab {
@Path("merge_request_id") long mergeRequestId,
@Field("body") String body);
 
@PUT(API_VERSION + "/projects/{id}/merge_requests/{merge_request_id}")
Call<MergeRequest> acceptMergeRequest(@Path("id") long projectId,
@Path("merge_request_id") long mergeRequestId);
/* --- ISSUES --- */
 
@GET(API_VERSION + "/projects/{id}/issues")
Loading
Loading
@@ -234,14 +263,18 @@ public interface GitLab {
 
@GET(API_VERSION + "/projects/{id}/issues/{issue_id}")
Call<Issue> getIssue(@Path("id") long projectId,
@Path("issue_id") long issueId);
@Path("issue_id") String issueId);
@GET(API_VERSION + "/projects/{id}/issues")
Call<List<Issue>> getIssuesByIid(@Path("id") long projectId,
@Query("iid") String internalIssueId);
 
@FormUrlEncoded
@POST(API_VERSION + "/projects/{id}/issues")
Call<Issue> createIssue(@Path("id") long projectId,
@Field("title") String title,
@Field("description") String description,
@Field("assignee_id") @Nullable Long assigneeId,
@Field("assignee_id") @Nullable Long assigneeId,
@Field("milestone_id") @Nullable Long milestoneId);
 
@PUT(API_VERSION + "/projects/{id}/issues/{issue_id}")
Loading
Loading
@@ -276,7 +309,7 @@ public interface GitLab {
Call<List<Branch>> getBranches(@Path("id") long projectId);
 
@GET(API_VERSION + "/projects/{id}/repository/contributors")
Call<List<Contributor>> getContributors(@Path("id") long projectId);
Call<List<Contributor>> getContributors(@Path("id") String projectId);
 
@GET(API_VERSION + "/projects/{id}/repository/tree")
Call<List<RepositoryTreeObject>> getTree(@Path("id") long projectId,
Loading
Loading
@@ -300,8 +333,10 @@ public interface GitLab {
@GET(API_VERSION + "/projects/{id}/repository/commits/{sha}/diff")
Call<List<Diff>> getCommitDiff(@Path("id") long projectId,
@Path("sha") String commitSHA);
/**
* Get the current labels for a project
*
* @param projectId id
* @return all the labels within a project
*/
Loading
Loading
@@ -310,9 +345,10 @@ public interface GitLab {
 
/**
* Create a new label
*
* @param projectId id
* @param name the name of the label
* @param color the color, ex. #ff0000
* @param name the name of the label
* @param color the color, ex. #ff0000
* @return call onSuccess the newly created label
*/
@POST(API_VERSION + "/projects/{id}/labels")
Loading
Loading
@@ -322,11 +358,41 @@ public interface GitLab {
 
/**
* Delete the label by its name
*
* @param projectId id
* @return all the labels within a project
*/
@DELETE(API_VERSION + "/projects/{id}/labels")
Call<Label> deleteLabel(@Path("id") long projectId,
@Query("name") String name);
@Query("name") String name);
/* --- BUILDS --- */
@GET(API_VERSION + "/projects/{id}/builds")
Call<List<Build>> getBuilds(@Path("id") long projectId,
@Query("scope") String scope);
@GET
Call<List<Build>> getBuilds(@Url String url,
@Query("scope") String state);
@GET(API_VERSION + "/projects/{id}/builds/{build_id}")
Call<Build> getBuild(@Path("id") long projectId,
@Path("build_id") long buildId);
@POST(API_VERSION + "/projects/{id}/builds/{build_id}/retry")
Call<Build> retryBuild(@Path("id") long projectId,
@Path("build_id") long buildId);
@POST(API_VERSION + "/projects/{id}/builds/{build_id}/erase")
Call<Build> eraseBuild(@Path("id") long projectId,
@Path("build_id") long buildId);
@POST(API_VERSION + "/projects/{id}/builds/{build_id}/cancel")
Call<Build> cancelBuild(@Path("id") long projectId,
@Path("build_id") long buildId);
 
@GET(API_VERSION + "/projects/{id}/builds/{build_id}/artifacts")
Call<List<Artifact>> getBuildArtifacts(@Path("id") long projectId,
@Path("build_id") long buildId);
}
\ No newline at end of file
package com.commit451.gitlab.api;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
/**
* Intercepts requests and logs them using Timber
* Created by John on 9/11/15.
*/
public class TimberRequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Timber.i("Sending request %s%n%s",
request.url(), request.headers());
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Timber.i("Received response for %s in %.1fms%n%s%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers(), response.body());
return response;
}
}
\ No newline at end of file
package com.commit451.gitlab.customtabs;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.support.design.widget.Snackbar;
import com.commit451.gitlab.R;
/**
* A fallback to open the url in the browser
*/
public class BrowserFallback implements CustomTabsActivityHelper.CustomTabFallback {
@Override
public void openUri(Activity activity, Uri uri) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(uri);
try {
activity.startActivity(i);
} catch (ActivityNotFoundException e) {
Snackbar.make(activity.getWindow().getDecorView(), R.string.error_no_browser, Snackbar.LENGTH_SHORT)
.show();
}
}
}
package com.commit451.gitlab.customtabs;
import android.app.Activity;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import java.util.List;
public class CustomTabsActivityHelper {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private ConnectionCallback mConnectionCallback;
/**
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView
*
* @param activity The host activity
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available
* @param uri the Uri to be opened
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available
*/
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(activity, uri);
}
}
/**
* Unbinds the Activity from the Custom Tabs Service
* @param activity the activity that is connected to the service
*/
public void unbindCustomTabsService(Activity activity) {
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
}
/**
* Creates or retrieves an exiting CustomTabsSession
*
* @return a CustomTabsSession
*/
public CustomTabsSession getSession() {
if (mClient == null) {
mCustomTabsSession = null;
} else if (mCustomTabsSession == null) {
mCustomTabsSession = mClient.newSession(null);
}
return mCustomTabsSession;
}
/**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service
* @param connectionCallback
*/
public void setConnectionCallback(ConnectionCallback connectionCallback) {
this.mConnectionCallback = connectionCallback;
}
/**
* Binds the Activity to the Custom Tabs Service
* @param activity the activity to be binded to the service
*/
public void bindCustomTabsService(Activity activity) {
if (mClient != null) return;
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
if (packageName == null) return;
mConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
//Initialize a session as soon as possible.
getSession();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mClient = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
};
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
if (mClient == null) return false;
CustomTabsSession session = getSession();
if (session == null) return false;
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
}
/**
* A Callback for when the service is connected or disconnected. Use those callbacks to
* handle UI changes when the service is connected or disconnected
*/
public interface ConnectionCallback {
/**
* Called when the service is connected
*/
void onCustomTabsConnected();
/**
* Called when the service is disconnected
*/
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available
*/
public interface CustomTabFallback {
/**
*
* @param activity The Activity that wants to open the Uri
* @param uri The uri to be opened by the fallback
*/
void openUri(Activity activity, Uri uri);
}
}
\ No newline at end of file
package com.commit451.gitlab.customtabs;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.support.customtabs.CustomTabsService;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class for Custom Tabs.
*/
public class CustomTabsHelper {
private static final String TAG = "CustomTabsHelper";
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
"android.support.customtabs.extra.KEEP_ALIVE";
private static String sPackageNameToUse;
private CustomTabsHelper() {}
public static void addKeepAliveExtra(Context context, Intent intent) {
Intent keepAliveIntent = new Intent().setClassName(
context.getPackageName(), KeepAliveService.class.getCanonicalName());
intent.putExtra(EXTRA_CUSTOM_TABS_KEEP_ALIVE, keepAliveIntent);
}
/**
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
* the one chosen by the user if there is one, otherwise makes a best effort to return a
* valid package name.
*
* This is <strong>not</strong> threadsafe.
*
* @param context {@link Context} to use for accessing {@link PackageManager}.
* @return The package name recommended to use for connecting to custom tabs related components.
*/
public static String getPackageNameToUse(Context context) {
if (sPackageNameToUse != null) return sPackageNameToUse;
PackageManager pm = context.getPackageManager();
// Get default VIEW intent handler.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
// Get all apps that can handle VIEW intents.
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
sPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
sPackageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
sPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
sPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
sPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
sPackageNameToUse = LOCAL_PACKAGE;
}
return sPackageNameToUse;
}
/**
* Used to check whether there is a specialized handler for a given intent.
* @param intent The intent to check with.
* @return Whether there is a specialized handler for the given intent.
*/
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
try {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> handlers = pm.queryIntentActivities(
intent,
PackageManager.GET_RESOLVED_FILTER);
if (handlers == null || handlers.size() == 0) {
return false;
}
for (ResolveInfo resolveInfo : handlers) {
IntentFilter filter = resolveInfo.filter;
if (filter == null) continue;
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
if (resolveInfo.activityInfo == null) continue;
return true;
}
} catch (RuntimeException e) {
Log.e(TAG, "Runtime exception while getting specialized handlers");
}
return false;
}
/**
* @return All possible chrome package names that provide custom tabs feature.
*/
public static String[] getPackages() {
return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE};
}
}
\ No newline at end of file
package com.commit451.gitlab.customtabs;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
/**
* Empty service used by the custom tab to bind to, raising the application's importance.
*/
public class KeepAliveService extends Service {
private static final Binder sBinder = new Binder();
@Override
public IBinder onBind(Intent intent) {
return sBinder;
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment