diff --git a/app/build.gradle b/app/build.gradle
index 6024e591..bf7b5f31 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -79,12 +79,15 @@ dependencies {
implementation 'com.github.angads25:filepicker:1.1.1'
implementation 'com.github.takusemba:spotlight:1.8.0'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha'
+ implementation 'com.applandeo:material-calendar-view:1.7.0'
// test libraries
+ implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(path: ':unitrackerlibrary')
+ implementation 'androidx.work:work-runtime:2.2.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e99130c4..44af864a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,22 +1,4 @@
-
-
-
-
+
+
+
trackerAdapter;
+ private ArrayAdapter authAdapter;
private EditText txtAccountServer, txtAccountUserName, txtAccountPassword,
txtAccountAPI, txtAccountImageURL, txtAccountDescription, txtAccountExtended;
private AutoCompleteTextView txtAccountTitle;
@@ -112,6 +113,7 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
Authentication.Tracker item = trackerAdapter.getItem(position);
if (item != null) {
+ fillAuthByTracker(item);
switch (item) {
case Local:
txtAccountServer.setText(Authentication.Tracker.Local.name());
@@ -270,6 +272,12 @@ protected void initControls() {
new Thread(() -> {
try {
+ if(!chkAccountGuest.isChecked()) {
+ if(currentAccount.getAuthentication() == Authentication.Auth.OAUTH) {
+ GithubTokenProvider githubTokenProvider = new GithubTokenProvider(currentAccount);
+ currentAccount.setAPIKey(githubTokenProvider.refreshToken());
+ }
+ }
IBugService bugService = Helper.getCurrentBugService(this.currentAccount, this.getApplicationContext());
if (chkAccountGuest.isChecked() || bugService.testConnection()) {
AccountActivity.this.runOnUiThread(() -> {
@@ -306,19 +314,17 @@ protected void initControls() {
});
this.lvAccounts = this.findViewById(R.id.lvAccounts);
- this.cmbAccountTracker = this.findViewById(R.id.cmbAccountTracker);
- // disable github
- List trackers = new LinkedList<>();
- for(Authentication.Tracker tracker : Authentication.Tracker.values()) {
- if(tracker!= Authentication.Tracker.Github) {
- trackers.add(tracker);
- }
- }
- this.trackerAdapter = new ArrayAdapter<>(this.getApplicationContext(), R.layout.spinner_item, trackers);
+ this.cmbAccountTracker = this.findViewById(R.id.cmbAccountTracker);
+ this.trackerAdapter = new ArrayAdapter<>(this.getApplicationContext(), R.layout.spinner_item, Authentication.Tracker.values());
this.cmbAccountTracker.setAdapter(this.trackerAdapter);
this.trackerAdapter.notifyDataSetChanged();
+ this.cmbAccountAuthentication = this.findViewById(R.id.cmbAccountAuthentication);
+ this.authAdapter = new ArrayAdapter<>(this.getApplicationContext(), R.layout.spinner_item);
+ this.cmbAccountAuthentication.setAdapter(this.authAdapter);
+ this.authAdapter.notifyDataSetChanged();
+
this.chkAccountGuest = this.findViewById(R.id.chkAccountGuest);
this.txtAccountTitle = this.findViewById(R.id.txtAccountTitle);
List ls = Arrays.asList(Authentication.Tracker.values());
@@ -337,6 +343,25 @@ protected void initControls() {
OnBoardingHelper.tutorialStep2(AccountActivity.this, () -> this.manageControls(true, true, false), this.findViewById(R.id.tblControls));
}
+ private void fillAuthByTracker(Authentication.Tracker tracker) {
+ this.authAdapter.clear();
+ this.authAdapter.addAll(Authentication.Auth.values());
+ switch (tracker) {
+ case MantisBT:
+ this.authAdapter.remove(Authentication.Auth.OAUTH);
+ this.authAdapter.remove(Authentication.Auth.API_KEY);
+ break;
+ case YouTrack:
+ this.authAdapter.remove(Authentication.Auth.OAUTH);
+ this.authAdapter.remove(Authentication.Auth.Basic);
+ break;
+ case Github:
+ this.authAdapter.remove(Authentication.Auth.Basic);
+ this.authAdapter.remove(Authentication.Auth.API_KEY);
+ break;
+ }
+ }
+
@Override
protected void initValidators() {
this.accountValidator = new Validator(this.getApplicationContext());
@@ -377,6 +402,7 @@ protected void manageControls(boolean editMode, boolean reset, boolean selected)
this.txtAccountDescription.setEnabled(editMode);
this.cmdAccountImageGallery.setEnabled(editMode);
this.cmbAccountTracker.setEnabled(editMode);
+ this.cmbAccountAuthentication.setEnabled(editMode);
this.chkAccountGuest.setEnabled(editMode);
this.txtAccountExtended.setEnabled(editMode);
@@ -403,6 +429,11 @@ private void objectToControls() {
this.cmbAccountTracker.setSelection(this.trackerAdapter.getPosition(Authentication.Tracker.Local));
this.txtAccountServer.setText(Authentication.Tracker.Local.name());
}
+ if (this.currentAccount.getAuthentication() !=null) {
+ this.cmbAccountAuthentication.setSelection(this.authAdapter.getPosition(this.currentAccount.getAuthentication()));
+ } else {
+ this.cmbAccountAuthentication.setSelection(this.authAdapter.getPosition(Authentication.Auth.Basic));
+ }
if (this.currentAccount.getCover() != null) {
Bitmap bitmap = BitmapFactory.decodeByteArray(this.currentAccount.getCover(), 0, this.currentAccount.getCover().length);
this.cmdAccountImageGallery.setImageBitmap(bitmap);
@@ -426,6 +457,7 @@ private void controlsToObject() {
this.currentAccount.setDescription(this.txtAccountDescription.getText().toString());
this.currentAccount.setTracker(this.trackerAdapter.getItem(this.cmbAccountTracker.getSelectedItemPosition()));
this.currentAccount.setGuest(this.chkAccountGuest.isChecked());
+ this.currentAccount.setAuthentication(this.authAdapter.getItem(this.cmbAccountAuthentication.getSelectedItemPosition()));
if (this.cmdAccountImageGallery.getDrawable() instanceof BitmapDrawable) {
this.currentAccount.setCover(Converter.convertDrawableToByteArray(this.cmdAccountImageGallery.getDrawable()));
} else {
diff --git a/app/src/main/java/de/domjos/unitrackermobile/activities/CalendarActivity.java b/app/src/main/java/de/domjos/unitrackermobile/activities/CalendarActivity.java
new file mode 100644
index 00000000..bd0292cc
--- /dev/null
+++ b/app/src/main/java/de/domjos/unitrackermobile/activities/CalendarActivity.java
@@ -0,0 +1,197 @@
+package de.domjos.unitrackermobile.activities;
+
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.applandeo.materialcalendarview.CalendarUtils;
+import com.applandeo.materialcalendarview.CalendarView;
+import com.applandeo.materialcalendarview.EventDay;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.domjos.unitrackerlibrary.interfaces.IBugService;
+import de.domjos.unitrackerlibrary.model.issues.Issue;
+import de.domjos.unitrackerlibrary.model.objects.DescriptionObject;
+import de.domjos.unitrackerlibrary.model.projects.Project;
+import de.domjos.unitrackerlibrary.model.projects.Version;
+import de.domjos.unitrackerlibrary.services.engine.Authentication;
+import de.domjos.unitrackerlibrary.tasks.AbstractTask;
+import de.domjos.unitrackerlibrary.tasks.IssueTask;
+import de.domjos.unitrackerlibrary.tasks.ProjectTask;
+import de.domjos.unitrackerlibrary.tasks.VersionTask;
+import de.domjos.unitrackerlibrary.utils.MessageHelper;
+import de.domjos.unitrackermobile.R;
+import de.domjos.unitrackermobile.custom.AbstractActivity;
+import de.domjos.unitrackermobile.custom.CustomEventDay;
+import de.domjos.unitrackermobile.helper.Helper;
+
+public class CalendarActivity extends AbstractActivity {
+ private CalendarView calendarView;
+ private ProgressBar progressBar;
+ private LinearLayout llToObject;
+ private TextView lblCalendarTitle, lblCalendarSubTitle;
+ private List eventDays;
+ private Intent intent;
+
+
+ public CalendarActivity() {
+ super(R.layout.calendar_activity);
+ }
+
+ @Override
+ protected void initActions() {
+ this.calendarView.setOnDayClickListener(eventDay -> {
+ if(eventDay instanceof CustomEventDay) {
+ CustomEventDay customEventDay = (CustomEventDay) eventDay;
+
+ this.lblCalendarSubTitle.setText(customEventDay.getDescriptionObject().getTitle());
+ if(customEventDay.getDescriptionObject() instanceof Project) {
+ this.lblCalendarTitle.setText(this.getString(R.string.projects));
+ this.intent = new Intent(this.getApplicationContext(), ProjectActivity.class);
+ } else if(customEventDay.getDescriptionObject() instanceof Version) {
+ this.lblCalendarTitle.setText(this.getString(R.string.versions));
+ this.intent = new Intent(this.getApplicationContext(), VersionActivity.class);
+ } else if(customEventDay.getDescriptionObject() instanceof Issue) {
+ this.lblCalendarTitle.setText(this.getString(R.string.issues));
+ this.intent = new Intent(this.getApplicationContext(), MainActivity.class);
+ } else {
+ this.lblCalendarTitle.setText(this.getString(R.string.calendar));
+ this.intent = null;
+ }
+
+ }
+ });
+
+ this.llToObject.setOnClickListener(view -> {
+ if(intent!=null) {
+ startActivity(intent);
+ }
+ });
+ }
+
+ @Override
+ protected void initControls() {
+ this.calendarView = this.findViewById(R.id.cvEventCalendar);
+ this.progressBar = this.findViewById(R.id.pbCalendar);
+
+ this.llToObject = this.findViewById(R.id.llToObject);
+ this.lblCalendarTitle = this.findViewById(R.id.lblCalendarTitle);
+ this.lblCalendarSubTitle = this.findViewById(R.id.lblCalendarSubTitle);
+ }
+
+ @Override
+ protected void reload() {
+ this.insertEvents();
+ }
+
+ private void insertEvents() {
+ try {
+ this.progressBar.setVisibility(View.VISIBLE);
+ List accounts = MainActivity.GLOBALS.getSqLiteGeneral().getAccounts("");
+ this.eventDays = new LinkedList<>();
+ for(Authentication account : accounts) {
+ IBugService bugService = Helper.getCurrentBugService(account, this.getApplicationContext());
+
+ ProjectTask projectTask = new ProjectTask(CalendarActivity.this, bugService, false, false, R.drawable.ic_apps_black_24dp);
+ projectTask.after(new AbstractTask.PostExecuteListener() {
+ @Override
+ public void onPostExecute(Object o) {
+ if(o instanceof List) {
+ List objects = (List) o;
+ for (Object obj : objects) {
+ if(obj instanceof Project) {
+ Project project = (Project) obj;
+ addEvent(project.getReleasedAt(), project.getTitle(), project);
+
+ VersionTask versionTask = new VersionTask(CalendarActivity.this, bugService, project.getId(), false, false, "versions", R.drawable.ic_update_black_24dp);
+ versionTask.after(new AbstractTask.PostExecuteListener() {
+ @Override
+ public void onPostExecute(Object o) {
+ List objList = (List) o;
+ for(Object obj : objList) {
+ Version version = (Version) obj;
+ addEvent(version.getReleasedVersionAt(), String.format("%s: %s", project.getTitle(), version.getTitle()), version);
+ }
+ }
+ });
+ versionTask.execute(0);
+
+ IssueTask issueTask = new IssueTask(CalendarActivity.this, bugService, project.getId(), false, false, false, R.drawable.ic_bug_report_black_24dp);
+ issueTask.after(new AbstractTask.PostExecuteListener() {
+ @Override
+ public void onPostExecute(Object o) {
+ List objList = (List) o;
+ loadingFinish(0, objList.size());
+ int i = 0;
+ for(Object obj : objList) {
+ Issue issue = (Issue) obj;
+
+ IssueTask detailTasks = new IssueTask(CalendarActivity.this, bugService, project.getId(), false, true, false, R.drawable.ic_bug_report_black_24dp);
+ detailTasks.after(new AbstractTask.PostExecuteListener() {
+ @Override
+ public void onPostExecute(Object o) {
+ List obj = (List) o;
+ Issue detailIssue = (Issue) obj.get(0);
+ if(detailIssue.getDueDate() != null) {
+ addEvent(detailIssue.getDueDate().getTime(), String.format("%s: %s", project.getTitle(), detailIssue.getTitle()), detailIssue);
+ }
+ }
+ });
+ detailTasks.execute(issue.getId());
+ i++;
+ loadingFinish(i, objList.size());
+ }
+ }
+ });
+ issueTask.execute(0);
+ }
+ }
+ }
+ }
+ });
+ projectTask.execute(0);
+ }
+ } catch (Exception ex) {
+ MessageHelper.printException(ex, CalendarActivity.this);
+ }
+ }
+
+ private void loadingFinish(int current, int max) {
+ this.progressBar.setMax(max);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ this.progressBar.setProgress(current, true);
+ } else {
+ this.progressBar.setProgress(current);
+ }
+ if(current==max) {
+ this.progressBar.setVisibility(View.GONE);
+ } else {
+ this.progressBar.setVisibility(View.VISIBLE);
+ }
+ this.progressBar.invalidate();
+ }
+
+
+ private void addEvent(long time, String title, DescriptionObject descriptionObject) {
+ Calendar calendar = Calendar.getInstance();
+ Date dt = new Date();
+ dt.setTime(time);
+ calendar.setTime(dt);
+
+ Drawable drawable = CalendarUtils.getDrawableText(this.getApplicationContext(), title, Typeface.DEFAULT_BOLD, R.color.colorPrimary, 14);
+ CustomEventDay customEventDay = new CustomEventDay(calendar, drawable, R.color.colorPrimary);
+ customEventDay.setDescriptionObject(descriptionObject);
+ this.eventDays.add(customEventDay);
+ this.calendarView.setEvents(this.eventDays);
+ this.calendarView.invalidate();
+ }
+}
diff --git a/app/src/main/java/de/domjos/unitrackermobile/activities/MainActivity.java b/app/src/main/java/de/domjos/unitrackermobile/activities/MainActivity.java
index fa869332..9774c152 100644
--- a/app/src/main/java/de/domjos/unitrackermobile/activities/MainActivity.java
+++ b/app/src/main/java/de/domjos/unitrackermobile/activities/MainActivity.java
@@ -743,6 +743,9 @@ public boolean onNavigationItemSelected(MenuItem item) {
case R.id.navExtendedSearch:
intent = new Intent(this.getApplicationContext(), SearchActivity.class);
break;
+ case R.id.navCalendar:
+ intent = new Intent(this.getApplicationContext(), CalendarActivity.class);
+ break;
case R.id.navExport:
intent = new Intent(this.getApplicationContext(), ExportActivity.class);
break;
diff --git a/app/src/main/java/de/domjos/unitrackermobile/custom/CustomEventDay.java b/app/src/main/java/de/domjos/unitrackermobile/custom/CustomEventDay.java
new file mode 100644
index 00000000..ef6a91f8
--- /dev/null
+++ b/app/src/main/java/de/domjos/unitrackermobile/custom/CustomEventDay.java
@@ -0,0 +1,42 @@
+package de.domjos.unitrackermobile.custom;
+
+import android.graphics.drawable.Drawable;
+
+import com.applandeo.materialcalendarview.EventDay;
+
+import java.util.Calendar;
+
+import de.domjos.unitrackerlibrary.model.objects.DescriptionObject;
+
+public class CustomEventDay extends EventDay {
+ private DescriptionObject descriptionObject;
+
+ public CustomEventDay(Calendar day) {
+ super(day);
+ }
+
+ public CustomEventDay(Calendar day, int drawable) {
+ super(day, drawable);
+ }
+
+ public CustomEventDay(Calendar day, Drawable drawable) {
+ super(day, drawable);
+ }
+
+ public CustomEventDay(Calendar day, int drawable, int labelColor) {
+ super(day, drawable, labelColor);
+ }
+
+ public CustomEventDay(Calendar day, Drawable drawable, int labelColor) {
+ super(day, drawable, labelColor);
+ }
+
+
+ public DescriptionObject getDescriptionObject() {
+ return this.descriptionObject;
+ }
+
+ public void setDescriptionObject(DescriptionObject descriptionObject) {
+ this.descriptionObject = descriptionObject;
+ }
+}
diff --git a/app/src/main/java/de/domjos/unitrackermobile/helper/SQLiteGeneral.java b/app/src/main/java/de/domjos/unitrackermobile/helper/SQLiteGeneral.java
index c5d78206..7793132f 100644
--- a/app/src/main/java/de/domjos/unitrackermobile/helper/SQLiteGeneral.java
+++ b/app/src/main/java/de/domjos/unitrackermobile/helper/SQLiteGeneral.java
@@ -25,6 +25,8 @@
import net.sqlcipher.database.SQLiteOpenHelper;
import net.sqlcipher.database.SQLiteStatement;
+import java.lang.reflect.Field;
+import java.sql.Types;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -72,6 +74,8 @@ public void onCreate(SQLiteDatabase db) {
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
this.initDatabase(db);
this.updateDatabase(db);
+
+ this.addColumnIfNotExists(db, "accounts", "authentication", Types.VARCHAR, 50, Authentication.Auth.Basic.name(), true);
}
private SQLiteDatabase getReadableDatabase() {
@@ -123,6 +127,7 @@ List getAccounts(String where, boolean onlyCheck) {
authentication.setTracker(Authentication.Tracker.valueOf(this.getString(cursor, "tracker")));
authentication.setId(cursor.getLong(cursor.getColumnIndex("ID")));
authentication.setGuest(cursor.getLong(cursor.getColumnIndex("guest")) == 1);
+ authentication.setAuthentication(Authentication.Auth.valueOf(this.getString(cursor, "authentication")));
authentications.add(authentication);
}
cursor.close();
@@ -149,10 +154,10 @@ public void insertOrUpdateAccount(Authentication authentication) {
SQLiteDatabase db = this.getWritableDatabase();
SQLiteStatement stmt;
if (authentication.getId() != null) {
- stmt = db.compileStatement("UPDATE accounts SET title=?, serverName=?, apiKey=?, userName=?, password=?, description=?, cover=?, tracker=?, guest=? WHERE ID=?");
- stmt.bindLong(10, authentication.getId());
+ stmt = db.compileStatement("UPDATE accounts SET title=?, serverName=?, apiKey=?, userName=?, password=?, description=?, cover=?, tracker=?, guest=?, authentication=? WHERE ID=?");
+ stmt.bindLong(11, authentication.getId());
} else {
- stmt = db.compileStatement("INSERT INTO accounts(title, serverName, apiKey, userName, password, description, cover, tracker, guest) VALUES(?,?,?,?,?,?,?,?,?)");
+ stmt = db.compileStatement("INSERT INTO accounts(title, serverName, apiKey, userName, password, description, cover, tracker, guest, authentication) VALUES(?,?,?,?,?,?,?,?,?,?)");
}
stmt.bindString(1, authentication.getTitle());
stmt.bindString(3, authentication.getAPIKey());
@@ -171,6 +176,11 @@ public void insertOrUpdateAccount(Authentication authentication) {
stmt.bindString(8, Authentication.Tracker.Local.name());
}
stmt.bindLong(9, authentication.isGuest() ? 1 : 0);
+ if(authentication.getAuthentication() != null) {
+ stmt.bindString(10, authentication.getAuthentication().name());
+ } else {
+ stmt.bindString(10, Authentication.Auth.Basic.name());
+ }
if(authentication.getId() == null) {
authentication.setId(stmt.executeInsert());
@@ -267,4 +277,58 @@ private void updateDatabase(SQLiteDatabase db) {
MessageHelper.printException(ex, this.context);
}
}
+
+ private void addColumnIfNotExists(SQLiteDatabase db, String table, String column, int type, int length, String defaultValue, boolean notNull) {
+ try {
+ if(this.columnNotExists(db, table, column)) {
+ Map types = this.getAllJdbcTypeNames();
+ String typeString = types.get(type);
+ if(typeString!=null) {
+ if(typeString.toLowerCase().equals("varchar")) {
+ typeString += "(" + length + ")";
+ }
+ } else {
+ return;
+ }
+ if(defaultValue != null) {
+ if(!defaultValue.equals("")) {
+ typeString += " DEFAULT " + defaultValue;
+ } else {
+ typeString += " DEFAULT ''";
+ }
+ }
+ if(notNull) {
+ typeString += " NOT NULL";
+ }
+
+ db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s %s", table, column, typeString));
+ }
+ } catch (Exception ex) {
+ MessageHelper.printException(ex, this.context);
+ }
+ }
+
+ private Map getAllJdbcTypeNames() throws Exception {
+
+ Map result = new LinkedHashMap<>();
+
+ for (Field field : Types.class.getFields()) {
+ result.put(field.getInt(null), field.getName());
+ }
+
+ return result;
+ }
+
+ private boolean columnNotExists(SQLiteDatabase db, String table, String column) {
+ boolean exists = false;
+ Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
+ while (cursor.moveToNext()) {
+ if(cursor.getString(1).equals(column)) {
+ exists = true;
+ break;
+ }
+ }
+ cursor.close();
+ return !exists;
+ }
}
diff --git a/app/src/main/res/drawable/ic_perm_contact_calendar_black_24dp.xml b/app/src/main/res/drawable/ic_perm_contact_calendar_black_24dp.xml
new file mode 100644
index 00000000..0d6b3bbd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_perm_contact_calendar_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/account_activity.xml b/app/src/main/res/layout/account_activity.xml
index eefd0394..3573d1f1 100644
--- a/app/src/main/res/layout/account_activity.xml
+++ b/app/src/main/res/layout/account_activity.xml
@@ -85,6 +85,20 @@
android:text="@string/accounts_guest" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/nav_main.xml b/app/src/main/res/menu/nav_main.xml
index c0604937..dfb4a8b2 100644
--- a/app/src/main/res/menu/nav_main.xml
+++ b/app/src/main/res/menu/nav_main.xml
@@ -59,6 +59,10 @@
android:id="@+id/navExtendedSearch"
android:icon="@drawable/ic_search_black_24dp"
android:title="@string/search" />
+
- Account ändern
Tracker
Gast
+ Authentifizierung
Servername
Benutzername
Passwort
@@ -232,6 +233,8 @@
Exportiere Diagramme
Export erfolgreich!
+ Kalender
+
Filter
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 79603fa1..d6514f8e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -32,6 +32,7 @@
Change Account
Tracker
Guest
+ Authentication
Servername
Username
Password
@@ -235,6 +236,8 @@
Export Diagrams
Export successfully!
+ Calendar
+
Filter
diff --git a/build.gradle b/build.gradle
index 97c442ff..df127e3a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@
* along with UniTrackerMobile. If not, see .
*/
-ext.propVersion = 11
+ext.propVersion = 12
ext.propVersionCode = "0.2.alpha.1"
ext.propJavaVersion = "8"
diff --git a/unitrackerlibrary/src/main/AndroidManifest.xml b/unitrackerlibrary/src/main/AndroidManifest.xml
index b45a0af6..7605121c 100644
--- a/unitrackerlibrary/src/main/AndroidManifest.xml
+++ b/unitrackerlibrary/src/main/AndroidManifest.xml
@@ -16,4 +16,4 @@
~ along with UniTrackerMobile. If not, see .
-->
-
+
diff --git a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/AccessTokenProvider.java b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/AccessTokenProvider.java
new file mode 100644
index 00000000..77b91610
--- /dev/null
+++ b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/AccessTokenProvider.java
@@ -0,0 +1,8 @@
+package de.domjos.unitrackerlibrary.services.authentication;
+
+public interface AccessTokenProvider {
+
+ String authType();
+ String token();
+ String refreshToken();
+}
diff --git a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/GithubTokenProvider.java b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/GithubTokenProvider.java
new file mode 100644
index 00000000..206c96f5
--- /dev/null
+++ b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/authentication/GithubTokenProvider.java
@@ -0,0 +1,47 @@
+package de.domjos.unitrackerlibrary.services.authentication;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import de.domjos.unitrackerlibrary.services.engine.Authentication;
+import de.domjos.unitrackerlibrary.services.engine.JSONEngine;
+
+public final class GithubTokenProvider implements AccessTokenProvider {
+ private Authentication authentication;
+ private String token;
+
+ public GithubTokenProvider(Authentication authentication) {
+ this.authentication = authentication;
+ this.token = this.authentication.getAPIKey();
+ }
+
+ @Override
+ public String authType() {
+ return "token";
+ }
+
+ @Override
+ public String token() {
+ if(this.token.equals("")) {
+ this.token = this.refreshToken();
+ }
+ return this.token;
+ }
+
+ @Override
+ public String refreshToken() {
+ try {
+ JSONEngine jsonEngine = new JSONEngine(this.authentication);
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("client_secret", "b5d664ec43c104fe21b8dbf7699fbb6d82118ad8");
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put("user");
+ jsonObject.put("scopes", jsonArray);
+ jsonEngine.executeRequest(this.authentication.getServer() + "/authorizations/clients/fb4ad3bae97a0a91e6a4", jsonObject.toString(), "PUT");
+ if(jsonEngine.getCurrentState() == 200 || jsonEngine.getCurrentState() == 201) {
+ return new JSONObject(jsonEngine.getCurrentMessage()).getString("hashed_token");
+ }
+ } catch (Exception ignored) {}
+ return null;
+ }
+}
diff --git a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/Authentication.java b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/Authentication.java
index 251428d4..03bc0c3c 100644
--- a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/Authentication.java
+++ b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/Authentication.java
@@ -28,6 +28,7 @@ public class Authentication extends DescriptionObject {
private byte[] cover;
private Tracker tracker;
private boolean guest;
+ private Auth authentication;
public Authentication() {
this("", "", "", "");
@@ -104,6 +105,14 @@ public void setGuest(boolean guest) {
this.guest = guest;
}
+ public Auth getAuthentication() {
+ return this.authentication;
+ }
+
+ public void setAuthentication(Auth authentication) {
+ this.authentication = authentication;
+ }
+
public enum Tracker {
Local,
YouTrack,
@@ -117,4 +126,10 @@ public enum Tracker {
Backlog,
//Tuleap
}
+
+ public enum Auth {
+ Basic,
+ API_KEY,
+ OAUTH
+ }
}
diff --git a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/JSONEngine.java b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/JSONEngine.java
index 4449c9a0..d5382c1c 100644
--- a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/JSONEngine.java
+++ b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/engine/JSONEngine.java
@@ -25,7 +25,7 @@
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
-
+import de.domjos.unitrackerlibrary.services.authentication.AccessTokenProvider;
import de.domjos.unitrackerlibrary.utils.Converter;
import okhttp3.Call;
import okhttp3.Credentials;
@@ -42,26 +42,31 @@ public class JSONEngine {
private final List headers;
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private static final MediaType OctetStream = MediaType.get("application/octet-stream");
- protected OkHttpClient client;
+ private OkHttpClient client;
private String currentMessage;
private int state;
- protected boolean noBasicLogin;
+ private AccessTokenProvider accessTokenProvider;
public JSONEngine(Authentication authentication) {
- this.noBasicLogin = false;
this.authentication = authentication;
this.headers = new LinkedList<>();
this.client = this.getClient();
}
public JSONEngine(Authentication authentication, String... headers) {
- this.noBasicLogin = false;
this.authentication = authentication;
this.headers = new LinkedList<>();
this.headers.addAll(Arrays.asList(headers));
this.client = this.getClient();
}
+ public JSONEngine(Authentication authentication, AccessTokenProvider accessTokenProvider) {
+ this.authentication = authentication;
+ this.headers = new LinkedList<>();
+ this.accessTokenProvider = accessTokenProvider;
+ this.client = this.getClient();
+ }
+
public String getCurrentMessage() {
return this.currentMessage;
}
@@ -70,6 +75,7 @@ public int getCurrentState() {
return this.state;
}
+ @SuppressWarnings("SameParameterValue")
protected void addHeader(String header) {
this.headers.add(header);
}
@@ -96,7 +102,7 @@ protected int executeRequest(String path) throws Exception {
}
}
- protected int executeRequest(String path, String body, String type) throws Exception {
+ public int executeRequest(String path, String body, String type) throws Exception {
Call call = this.initAuthentication(path, body, type);
Response response = call.execute();
this.state = response.code();
@@ -257,22 +263,42 @@ private Call delete(String path) {
return this.initAuthentication(path, "", "DELETE");
}
- protected OkHttpClient getClient() {
+ private OkHttpClient getClient() {
+ return this.getClient(false);
+ }
+
+ private OkHttpClient getClient(boolean basic) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.followRedirects(true)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS);
- if(!this.noBasicLogin) {
+ if(basic) {
builder.authenticator((route, response) -> {
- String credential;
- if (!authentication.getAPIKey().trim().isEmpty()) {
- credential = Credentials.basic(authentication.getAPIKey(), UUID.randomUUID().toString());
- } else {
- credential = Credentials.basic(authentication.getUserName(), authentication.getPassword().trim());
- }
+ String credential = Credentials.basic(authentication.getUserName(), authentication.getPassword().trim());
return response.request().newBuilder().header("Authorization", credential).build();
});
+ } else {
+ if(this.authentication.getAuthentication() != Authentication.Auth.OAUTH) {
+ builder.authenticator((route, response) -> {
+ String credential;
+ if (this.authentication.getAuthentication() == Authentication.Auth.Basic) {
+ credential = Credentials.basic(authentication.getUserName(), authentication.getPassword().trim());
+ } else {
+ credential = Credentials.basic(authentication.getAPIKey(), UUID.randomUUID().toString());
+ }
+ return response.request().newBuilder().header("Authorization", credential).build();
+ });
+ } else {
+ if(this.accessTokenProvider!=null) {
+ builder.authenticator((route, response) -> {
+ String token = this.accessTokenProvider.token();
+ return response.request().newBuilder().header("Authorization", this.accessTokenProvider.authType() + " " + token).build();
+ });
+ } else {
+ return this.getClient(true);
+ }
+ }
}
return builder.build();
diff --git a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/tracker/Github.java b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/tracker/Github.java
index 48da51eb..3ba0bd46 100644
--- a/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/tracker/Github.java
+++ b/unitrackerlibrary/src/main/java/de/domjos/unitrackerlibrary/services/tracker/Github.java
@@ -40,44 +40,22 @@
import de.domjos.unitrackerlibrary.model.projects.Project;
import de.domjos.unitrackerlibrary.model.projects.Version;
import de.domjos.unitrackerlibrary.permissions.GithubPermissions;
+import de.domjos.unitrackerlibrary.services.authentication.GithubTokenProvider;
import de.domjos.unitrackerlibrary.services.engine.Authentication;
import de.domjos.unitrackerlibrary.services.engine.JSONEngine;
import de.domjos.unitrackerlibrary.utils.Converter;
public final class Github extends JSONEngine implements IBugService {
private Authentication authentication;
- private String oAuthParams, client_id, client_secret;
public Github(Authentication authentication) {
- super(authentication);
+ super(authentication, new GithubTokenProvider(authentication));
this.authentication = authentication;
-
- this.oAuthParams = "";
- if(this.authentication.getAPIKey()!=null) {
- if(!this.authentication.getAPIKey().trim().isEmpty()) {
- if(this.authentication.getAPIKey().contains("|")) {
- String[] client = this.authentication.getAPIKey().split("\\|");
- this.client_id = client[0];
- this.client_secret = client[1];
- this.oAuthParams = "?client_id=" + client[0].trim() + "&client_secret=" + client[1].trim();
- }
- }
- }
}
@Override
public boolean testConnection() throws Exception {
- String token = this.authorize();
- if(!token.isEmpty()) {
- super.addHeader("Authorization: token " + token);
- super.noBasicLogin = true;
- super.client = super.getClient();
- } else {
- super.noBasicLogin = false;
- super.client = super.getClient();
- }
-
- int status = this.executeRequest("/user" + this.oAuthParams);
+ int status = this.executeRequest("/user");
if (status == 200 || status == 201) {
JSONObject jsonObject = new JSONObject(this.getCurrentMessage());
return jsonObject.has("plan");
@@ -85,17 +63,6 @@ public boolean testConnection() throws Exception {
return false;
}
- private String authorize() throws Exception {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("client_secret", this.client_secret);
- int status = this.executeRequest("/authorizations/clients/" + this.client_id, jsonObject.toString(), "PUT");
- if(status == 200 || status == 201) {
- JSONObject response = new JSONObject(this.getCurrentMessage());
- return response.getString("hashed_token");
- }
- return "";
- }
-
@Override
public String getTrackerVersion() {
return "v3";