Commit 5687f521 authored by M M Arif's avatar M M Arif

Merge branch '9-new-milestone' into 'develop'

Resolve "[New] Create new milestone"

Closes #9

See merge request mmarif4u/gitnex!18
parents a724179e a5b34cce
......@@ -6,8 +6,8 @@ android {
applicationId "org.mian.gitnex"
minSdkVersion 21
targetSdkVersion 28
versionCode 8
versionName "pre-alpha6"
versionCode 9
versionName "pre-alpha7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
......
......@@ -12,7 +12,8 @@
android:roundIcon="@mipmap/app_logo_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activities.ReplyToIssueActivity"></activity>
<activity android:name=".activities.NewMilestoneActivity"></activity>
<activity android:name=".activities.ReplyToIssueActivity" />
<activity android:name=".activities.IssueDetailActivity" />
<activity
android:name=".activities.RepoDetailActivity"
......
package org.mian.gitnex.activities;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import android.app.DatePickerDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.models.Milestones;
import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB;
import java.util.Calendar;
/**
* Author M M Arif
*/
public class NewMilestoneActivity extends AppCompatActivity implements View.OnClickListener {
private EditText milestoneDueDate;
private View.OnClickListener onClickListener;
private EditText milestoneTitle;
private EditText milestoneDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_milestone);
milestoneDueDate = findViewById(R.id.milestoneDueDate);
ImageView closeActivity = findViewById(R.id.close);
Button createNewMilestoneButton = findViewById(R.id.createNewMilestoneButton);
milestoneTitle = findViewById(R.id.milestoneTitle);
milestoneDescription = findViewById(R.id.milestoneDescription);
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
milestoneDueDate.setOnClickListener(this);
createNewMilestoneButton.setOnClickListener(createMilestoneListener);
}
private View.OnClickListener createMilestoneListener = new View.OnClickListener() {
public void onClick(View v) {
processNewMilestone();
}
};
private void processNewMilestone() {
AppUtil appUtil = new AppUtil();
TinyDB tinyDb = new TinyDB(getApplicationContext());
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
//String appLocale = tinyDb.getString("locale");
String newMilestoneTitle = milestoneTitle.getText().toString();
String newMilestoneDescription = milestoneDescription.getText().toString();
String newMilestoneDueDate = milestoneDueDate.getText().toString();
if(newMilestoneTitle.equals("")) {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.milestoneNameErrorEmpty);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
return;
}
if(!newMilestoneDescription.equals("")) {
if (appUtil.charactersLength(newMilestoneDescription) > 255) {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.milestoneDescError);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
return;
}
}
if(newMilestoneDueDate.equals("")) {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.milestoneDateEmpty);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
return;
}
String finalMilestoneDueDate = (AppUtil.customDateCombine(AppUtil.customDateFormat(newMilestoneDueDate)));
createNewMilestone(instanceUrl, instanceToken, repoOwner, repoName, newMilestoneTitle, newMilestoneDescription, finalMilestoneDueDate);
}
private void createNewMilestone(final String instanceUrl, final String token, String repoOwner, String repoName, String newMilestoneTitle, String newMilestoneDescription, String newMilestoneDueDate) {
Milestones createMilestone = new Milestones(newMilestoneDescription, newMilestoneTitle, newMilestoneDueDate);
Call<Milestones> call;
call = RetrofitClient
.getInstance(instanceUrl)
.getApiInterface()
.createMilestone(token, repoOwner, repoName, createMilestone);
call.enqueue(new Callback<Milestones>() {
@Override
public void onResponse(@NonNull Call<Milestones> call, @NonNull retrofit2.Response<Milestones> response) {
if(response.isSuccessful()) {
if(response.code() == 201) {
TinyDB tinyDb = new TinyDB(getApplicationContext());
tinyDb.putBoolean("milestoneCreated", true);
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.milestoneCreated);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
finish();
}
}
else {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.milestoneCreatedError);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
Log.i("isSuccessful", String.valueOf(response.body()));
}
}
@Override
public void onFailure(@NonNull Call<Milestones> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
@Override
public void onClick(View v) {
if (v == milestoneDueDate) {
final Calendar c = Calendar.getInstance();
int mYear = c.get(Calendar.YEAR);
final int mMonth = c.get(Calendar.MONTH);
final int mDay = c.get(Calendar.DAY_OF_MONTH);
DatePickerDialog datePickerDialog = new DatePickerDialog(this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
milestoneDueDate.setText(getString(R.string.setDueDate, year, (monthOfYear + 1), dayOfMonth));
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
}
}
private void initCloseListener() {
onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
};
}
}
......@@ -12,8 +12,10 @@ import org.mian.gitnex.R;
import org.mian.gitnex.models.Milestones;
import org.mian.gitnex.util.TinyDB;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
......@@ -98,6 +100,13 @@ public class MilestonesAdapter extends RecyclerView.Adapter<MilestonesAdapter.Mi
holder.msTitle.setText(currentItem.getTitle());
holder.msStatus.setText(currentItem.getState());
if(currentItem.getState().equals("open")) {
holder.msStatus.setTextColor((mCtx.getResources().getColor(R.color.colorLightGreen)));
}
else if(currentItem.getState().equals("closed")) {
holder.msStatus.setTextColor((mCtx.getResources().getColor(R.color.colorRed)));
}
if (!currentItem.getDescription().equals("")) {
final CharSequence bodyWithMD = Markwon.markdown(mdConfiguration, EmojiParser.parseToUnicode(currentItem.getDescription()));
holder.msDescription.setText(bodyWithMD);
......@@ -113,11 +122,24 @@ public class MilestonesAdapter extends RecyclerView.Adapter<MilestonesAdapter.Mi
if (timeFormat.equals("normal") || timeFormat.equals("pretty")) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", new Locale(locale));
String dueDate = formatter.format(currentItem.getDue_on());
Date date = null;
try {
date = formatter.parse(currentItem.getDue_on());
} catch (ParseException e) {
e.printStackTrace();
}
String dueDate = formatter.format(date);
holder.msDueDate.setText(dueDate);
} else if (timeFormat.equals("normal1")) {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", new Locale(locale));
String dueDate = formatter.format(currentItem.getDue_on());
Date date = null;
try {
date = formatter.parse(currentItem.getDue_on());
} catch (ParseException e) {
e.printStackTrace();
}
String dueDate = formatter.format(date);
holder.msDueDate.setText(dueDate);
}
......
......@@ -64,7 +64,7 @@ public class BranchesFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_milestones, container, false);
View v = inflater.inflate(R.layout.fragment_branches, container, false);
TinyDB tinyDb = new TinyDB(getContext());
final String instanceUrl = tinyDb.getString("instanceUrl");
......
package org.mian.gitnex.fragments;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
......@@ -19,9 +21,12 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.widget.ImageView;
import android.widget.ProgressBar;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.NewMilestoneActivity;
import org.mian.gitnex.adaptors.MilestonesAdapter;
import org.mian.gitnex.models.Milestones;
import org.mian.gitnex.util.TinyDB;
......@@ -37,6 +42,7 @@ public class MilestonesFragment extends Fragment {
private ProgressBar mProgressBar;
private MilestonesAdapter adapter;
private RecyclerView mRecyclerView;
private ImageView createNewMilestone;
private static String repoNameF = "param2";
private static String repoOwnerF = "param1";
......@@ -69,7 +75,7 @@ public class MilestonesFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_milestones, container, false);
final View v = inflater.inflate(R.layout.fragment_milestones, container, false);
setHasOptionsMenu(true);
TinyDB tinyDb = new TinyDB(getContext());
......@@ -89,6 +95,48 @@ public class MilestonesFragment extends Fragment {
DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(dividerItemDecoration);
createNewMilestone = v.findViewById(R.id.createNewMilestone);
createNewMilestone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(view.getContext(), NewMilestoneActivity.class);
startActivity(intent);
}
});
v.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
v.getWindowVisibleDisplayFrame(r);
int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 500) {
createNewMilestone.setVisibility(View.GONE);
}
}
});
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0 && createNewMilestone.isShown()) {
createNewMilestone.setVisibility(View.GONE);
} else if (dy < 0 ) {
createNewMilestone.setVisibility(View.VISIBLE);
}
}
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
mProgressBar = v.findViewById(R.id.progress_bar);
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
......@@ -109,6 +157,24 @@ public class MilestonesFragment extends Fragment {
return v;
}
@Override
public void onResume() {
super.onResume();
final TinyDB tinyDb = new TinyDB(getContext());
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
if(tinyDb.getBoolean("milestoneCreated")) {
MilestonesViewModel.loadMilestonesList(instanceUrl, instanceToken, repoOwner, repoName);
tinyDb.putBoolean("milestoneCreated", false);
}
}
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
......
......@@ -83,4 +83,7 @@ public interface ApiInterface {
@GET("repos/{owner}/{repo}/collaborators") // get collaborators list
Call<List<Collaborators>> getCollaborators(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName);
@POST("repos/{owner}/{repo}/milestones") // create new milestone
Call<Milestones> createMilestone(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Body Milestones jsonStr);
}
package org.mian.gitnex.models;
import java.util.Date;
/**
* Author M M Arif
*/
......@@ -14,7 +12,13 @@ public class Milestones {
private String state;
private int open_issues;
private int closed_issues;
private Date due_on;
private String due_on;
public Milestones(String description, String title, String due_on) {
this.description = description;
this.title = title;
this.due_on = due_on;
}
public int getId() {
return id;
......@@ -40,7 +44,7 @@ public class Milestones {
return closed_issues;
}
public Date getDue_on() {
public String getDue_on() {
return due_on;
}
}
......@@ -11,6 +11,7 @@ import android.util.DisplayMetrics;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Locale;
/**
......@@ -117,4 +118,40 @@ public class AppUtil {
}
public static String customDateFormat(String customDate) {
String[] parts = customDate.split("-");
final String year = parts[0];
final String month = parts[1];
final String day = parts[2];
String sMonth;
if (Integer.parseInt(month) < 10) {
sMonth = "0"+String.valueOf(month);
} else {
sMonth = String.valueOf(month);
}
String sDay;
if (Integer.parseInt(day) < 10) {
sDay = "0"+String.valueOf(day);
} else {
sDay = String.valueOf(day);
}
return year + "-" + sMonth + "-" + sDay;
}
public static String customDateCombine(String customDate) {
final Calendar c = Calendar.getInstance();
int mHour = c.get(Calendar.HOUR_OF_DAY);
int mMinute = c.get(Calendar.MINUTE);
int mSeconds = c.get(Calendar.SECOND);
return (customDate + "T" + mHour + ":" + mMinute + ":" + mSeconds + "Z");
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/colorPrimary">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
tools:ignore="UnusedAttribute">
<ImageView
android:id="@+id/close"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="15dp"
android:layout_marginLeft="15dp"
android:gravity="center_vertical"
android:contentDescription="@string/close"
android:src="@drawable/ic_close" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/pageTitleCreateMilestone"
android:textStyle="bold"
android:textColor="@color/white"
android:maxLines="1"
android:textSize="18sp" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/backgroundColor">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:paddingBottom="30dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newMilestoneTitle"
android:textColor="@color/colorWhite"
android:textSize="16sp" />
<EditText
android:id="@+id/milestoneTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="16sp"
tools:ignore="Autofill"
android:labelFor="@+id/milestoneTitle"
android:background="@drawable/shape_inputs"
android:textColor="@color/colorWhite"
android:textColorHint="@color/colorWhite"
android:inputType="text"
android:textColorHighlight="@color/colorWhite"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newMilestoneDescription"
android:textColor="@color/colorWhite"
android:textSize="16sp"
android:layout_marginTop="10dp" />
<EditText
android:id="@+id/milestoneDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="10dp"
android:background="@drawable/shape_inputs"
android:maxLines="8"
android:minLines="6"
tools:ignore="Autofill"
android:labelFor="@+id/milestoneDescription"
android:scrollbars="vertical"
android:gravity="top|start"
android:textSize="16sp"
android:textColor="@color/colorWhite"
android:textColorHint="@color/colorWhite"
android:inputType="textMultiLine"
android:textColorHighlight="@color/colorWhite"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newOrgDescInfo"
android:textColor="@color/colorWhite"
android:textSize="14sp"
android:gravity="end" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newMilestoneDueDate"
android:textColor="@color/colorWhite"
android:textSize="16sp"
android:layout_marginTop="10dp" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/milestoneDueDate"
android:editable="false"
android:focusable="false"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="16sp"
tools:ignore="Autofill"
android:inputType="date"
android:labelFor="@+id/milestoneDueDate"
android:background="@drawable/shape_inputs"
android:textColor="@color/colorWhite"
android:textColorHint="@color/colorWhite"
android:textColorHighlight="@color/colorWhite"/>
<Button
android:id="@+id/createNewMilestoneButton"
android:gravity="center"
android:layout_gravity="end"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="@string/newCreateButtonCopy"
android:background="@drawable/shape_buttons"
android:textColor="@color/btnTextColor" />
</LinearLayout>
</ScrollView>
</LinearLayout>
......@@ -52,7 +52,7 @@
android:layout_alignParentEnd="true"
android:background="@drawable/circle"
android:padding="12dp"
android:visibility="gone"
android:visibility="visible"
android:contentDescription="@string/addNewContent"
/>
......
......@@ -46,10 +46,9 @@
android:id="@+id/milestoneDescription"
android:layout_width="match_parent"