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";