diff --git a/app/build.gradle b/app/build.gradle index 0197e4cd..b09fc16e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,4 +145,5 @@ dependencies { compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' compile 'com.github.kabouzeid:RecyclerView-FastScroll:1.8-kmod' compile 'com.heinrichreimersoftware:material-intro:b8ec16d3d6' + compile 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c20d1349..642ddad9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -107,11 +107,9 @@ - - @@ -119,11 +117,9 @@ - - - - - - - + - + \ No newline at end of file diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java index 89bd684f..95c8bc18 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java @@ -19,6 +19,7 @@ import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.ChangelogDialog; import com.kabouzeid.gramophone.dialogs.DonationsDialog; import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity; +import com.kabouzeid.gramophone.ui.activities.bugreport.BugReportActivity; import com.kabouzeid.gramophone.ui.activities.intro.AppIntroActivity; import butterknife.Bind; @@ -36,7 +37,6 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen private static String GITHUB = "https://github.com/kabouzeid"; private static String WEBSITE = "http://kabouzeid.com/"; - private static String REPORT_BUGS = "https://github.com/kabouzeid/phonograph-issue-tracker"; private static String GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/u/0/communities/106227738496107108513"; private static String TRANSLATE = "https://phonograph.oneskyapp.com/collaboration/project?id=26521"; private static String RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=com.kabouzeid.gramophone"; @@ -179,7 +179,7 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen } else if (v == visitWebsite) { openUrl(WEBSITE); } else if (v == reportBugs) { - openUrl(REPORT_BUGS); + startActivity(new Intent(this, BugReportActivity.class)); } else if (v == joinGooglePlusCommunity) { openUrl(GOOGLE_PLUS_COMMUNITY); } else if (v == translate) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java new file mode 100644 index 00000000..7cc9d56f --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java @@ -0,0 +1,414 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport; + +import android.app.Activity; +import android.app.Dialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.StringDef; +import android.support.annotation.StringRes; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.RadioButton; +import android.widget.TextView; +import android.widget.Toast; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.kabouzeid.appthemehelper.ThemeStore; +import com.kabouzeid.appthemehelper.util.TintHelper; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.misc.DialogAsyncTask; +import com.kabouzeid.gramophone.ui.activities.base.AbsThemeActivity; +import com.kabouzeid.gramophone.ui.activities.bugreport.model.DeviceInfo; +import com.kabouzeid.gramophone.ui.activities.bugreport.model.Report; +import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.ExtraInfo; +import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.GithubLogin; +import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.GithubTarget; + +import org.eclipse.egit.github.core.Issue; +import org.eclipse.egit.github.core.client.GitHubClient; +import org.eclipse.egit.github.core.client.RequestException; +import org.eclipse.egit.github.core.service.IssueService; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class BugReportActivity extends AbsThemeActivity { + + private static final int STATUS_BAD_CREDENTIALS = 401; + private static final int STATUS_ISSUES_NOT_ENABLED = 410; + + @StringDef({RESULT_OK, RESULT_BAD_CREDENTIALS, RESULT_INVALID_TOKEN, RESULT_ISSUES_NOT_ENABLED, + RESULT_UNKNOWN}) + @Retention(RetentionPolicy.SOURCE) + private @interface Result { + } + + private static final String RESULT_OK = "RESULT_OK"; + private static final String RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"; + private static final String RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN"; + private static final String RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"; + private static final String RESULT_UNKNOWN = "RESULT_UNKNOWN"; + + private DeviceInfo deviceInfo; + + @Bind(R.id.toolbar) + Toolbar toolbar; + + @Bind(R.id.input_title) + TextInputEditText inputTitle; + @Bind(R.id.input_description) + TextInputEditText inputDescription; + @Bind(R.id.air_textDeviceInfo) + TextView textDeviceInfo; + + @Bind(R.id.input_username) + TextInputEditText inputUsername; + @Bind(R.id.input_password) + TextInputEditText inputPassword; + @Bind(R.id.option_use_account) + RadioButton optionUseAccount; + @Bind(R.id.option_anonymous) + RadioButton optionManual; + + @Bind(R.id.button_send) + FloatingActionButton sendFab; + + private static final String ISSUE_TRACKER_LINK = "https://github.com/kabouzeid/phonograph-issue-tracker"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bug_report); + ButterKnife.bind(this); + + setStatusbarColorAuto(); + setNavigationbarColorAuto(); + setTaskDescriptionColorAuto(); + + initViews(); + + if (TextUtils.isEmpty(getTitle())) + setTitle(R.string.report_an_issue); + + + deviceInfo = new DeviceInfo(this); + textDeviceInfo.setText(deviceInfo.toString()); + } + + private void initViews() { + final int accentColor = ThemeStore.accentColor(this); + final int primaryColor = ThemeStore.primaryColor(this); + toolbar.setBackgroundColor(primaryColor); + setSupportActionBar(toolbar); + //noinspection ConstantConditions + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + TintHelper.setTintAuto(optionUseAccount, accentColor, false); + optionUseAccount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + inputTitle.setEnabled(true); + inputDescription.setEnabled(true); + inputUsername.setEnabled(true); + inputPassword.setEnabled(true); + + optionManual.setChecked(false); + sendFab.hide(new FloatingActionButton.OnVisibilityChangedListener() { + @Override + public void onHidden(FloatingActionButton fab) { + super.onHidden(fab); + sendFab.setImageResource(R.drawable.ic_send_white_24dp); + sendFab.show(); + } + }); + } + }); + TintHelper.setTintAuto(optionManual, accentColor, false); + optionManual.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + inputTitle.setEnabled(false); + inputDescription.setEnabled(false); + inputUsername.setEnabled(false); + inputPassword.setEnabled(false); + + optionUseAccount.setChecked(false); + sendFab.hide(new FloatingActionButton.OnVisibilityChangedListener() { + @Override + public void onHidden(FloatingActionButton fab) { + super.onHidden(fab); + sendFab.setImageResource(R.drawable.ic_open_in_browser_white_24dp); + sendFab.show(); + } + }); + } + }); + + inputPassword.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEND) { + reportIssue(); + return true; + } + return false; + } + }); + + textDeviceInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + copyDeviceInfoToClipBoard(); + } + }); + + TintHelper.setTintAuto(sendFab, accentColor, true); + sendFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + reportIssue(); + } + }); + + TintHelper.setTintAuto(inputTitle, accentColor, false); + TintHelper.setTintAuto(inputDescription, accentColor, false); + TintHelper.setTintAuto(inputUsername, accentColor, false); + TintHelper.setTintAuto(inputPassword, accentColor, false); + } + + private void reportIssue() { + if (optionUseAccount.isChecked()) { + if (!validateInput()) return; + String username = inputUsername.getText().toString(); + String password = inputPassword.getText().toString(); + sendBugReport(new GithubLogin(username, password)); + } else { + copyDeviceInfoToClipBoard(); + + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(ISSUE_TRACKER_LINK)); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(i); + } + } + + private void copyDeviceInfoToClipBoard() { + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo.toMarkdown()); + clipboard.setPrimaryClip(clip); + + Toast.makeText(BugReportActivity.this, R.string.copied_device_info_to_clipboard, Toast.LENGTH_LONG).show(); + } + + private boolean validateInput() { + boolean hasErrors = false; + + if (optionUseAccount.isChecked()) { + if (TextUtils.isEmpty(inputUsername.getText())) { + setError(inputUsername, R.string.bug_report_no_username); + hasErrors = true; + } else { + removeError(inputUsername); + } + + if (TextUtils.isEmpty(inputPassword.getText())) { + setError(inputPassword, R.string.bug_report_no_password); + hasErrors = true; + } else { + removeError(inputPassword); + } + } + + if (TextUtils.isEmpty(inputTitle.getText())) { + setError(inputTitle, R.string.bug_report_no_title); + hasErrors = true; + } else { + removeError(inputTitle); + } + + if (TextUtils.isEmpty(inputDescription.getText())) { + setError(inputDescription, R.string.bug_report_no_description); + hasErrors = true; + } else { + removeError(inputDescription); + } + + return !hasErrors; + } + + private void setError(TextInputEditText editText, @StringRes int errorRes) { + TextInputLayout layout = (TextInputLayout) editText.getParent(); + layout.setError(getString(errorRes)); + } + + private void removeError(TextInputEditText editText) { + TextInputLayout layout = (TextInputLayout) editText.getParent(); + layout.setError(null); + } + + private void sendBugReport(GithubLogin login) { + if (!validateInput()) return; + + String bugTitle = inputTitle.getText().toString(); + String bugDescription = inputDescription.getText().toString(); + + ExtraInfo extraInfo = new ExtraInfo(); + onSaveExtraInfo(extraInfo); + + Report report = new Report(bugTitle, bugDescription, deviceInfo, extraInfo); + GithubTarget target = new GithubTarget("kabouzeid", "phonograph-issue-tracker"); + + ReportIssueAsyncTask.report(this, report, target, login); + } + + protected void onSaveExtraInfo(ExtraInfo extraInfo) { + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + } + return super.onOptionsItemSelected(item); + } + + private static class ReportIssueAsyncTask extends DialogAsyncTask { + private final Report report; + private final GithubTarget target; + private final GithubLogin login; + + public static void report(Activity activity, Report report, GithubTarget target, + GithubLogin login) { + new ReportIssueAsyncTask(activity, report, target, login).execute(); + } + + private ReportIssueAsyncTask(Activity activity, Report report, GithubTarget target, + GithubLogin login) { + super(activity); + this.report = report; + this.target = target; + this.login = login; + } + + @Override + protected Dialog createDialog(@NonNull Context context) { + return new MaterialDialog.Builder(context) + .progress(true, 0) + .progressIndeterminateStyle(true) + .title(R.string.bug_report_uploading) + .show(); + } + + @Override + @Result + protected String doInBackground(Void... params) { + GitHubClient client; + if (login.shouldUseApiToken()) { + client = new GitHubClient().setOAuth2Token(login.getApiToken()); + } else { + client = new GitHubClient().setCredentials(login.getUsername(), login.getPassword()); + } + + Issue issue = new Issue().setTitle(report.getTitle()).setBody(report.getDescription()); + try { + new IssueService(client).createIssue(target.getUsername(), target.getRepository(), issue); + return RESULT_OK; + } catch (RequestException e) { + switch (e.getStatus()) { + case STATUS_BAD_CREDENTIALS: + if (login.shouldUseApiToken()) + return RESULT_INVALID_TOKEN; + return RESULT_BAD_CREDENTIALS; + case STATUS_ISSUES_NOT_ENABLED: + return RESULT_ISSUES_NOT_ENABLED; + default: + e.printStackTrace(); + return RESULT_UNKNOWN; + } + } catch (IOException e) { + e.printStackTrace(); + return RESULT_UNKNOWN; + } + } + + @Override + protected void onPostExecute(@Result String result) { + super.onPostExecute(result); + + Context context = getContext(); + if (context == null) return; + + switch (result) { + case RESULT_OK: + tryToFinishActivity(); + break; + case RESULT_BAD_CREDENTIALS: + new MaterialDialog.Builder(context) + .title(R.string.bug_report_failed) + .content(R.string.bug_report_failed_wrong_credentials) + .positiveText(android.R.string.ok) + .show(); + break; + case RESULT_INVALID_TOKEN: + new MaterialDialog.Builder(context) + .title(R.string.bug_report_failed) + .content(R.string.bug_report_failed_invalid_token) + .positiveText(android.R.string.ok) + .show(); + break; + case RESULT_ISSUES_NOT_ENABLED: + new MaterialDialog.Builder(context) + .title(R.string.bug_report_failed) + .content(R.string.bug_report_failed_issues_not_available) + .positiveText(android.R.string.ok) + .show(); + break; + default: + new MaterialDialog.Builder(context) + .title(R.string.bug_report_failed) + .content(R.string.bug_report_failed_unknown) + .positiveText(android.R.string.ok) + .onPositive(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, + @NonNull DialogAction which) { + tryToFinishActivity(); + } + }) + .cancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + tryToFinishActivity(); + } + }) + .show(); + break; + } + } + + private void tryToFinishActivity() { + Context context = getContext(); + if (context instanceof Activity && !((Activity) context).isFinishing()) { + ((Activity) context).finish(); + } + } + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java new file mode 100644 index 00000000..2f610835 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java @@ -0,0 +1,94 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport.model; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.annotation.IntRange; + +import java.util.Arrays; + +public class DeviceInfo { + private final int versionCode; + private final String versionName; + private final String buildVersion = Build.VERSION.INCREMENTAL; + private final String releaseVersion = Build.VERSION.RELEASE; + @IntRange(from = 0) + private final int sdkVersion = Build.VERSION.SDK_INT; + private final String buildID = Build.DISPLAY; + private final String brand = Build.BRAND; + private final String manufacturer = Build.MANUFACTURER; + private final String device = Build.DEVICE; + private final String model = Build.MODEL; + private final String product = Build.PRODUCT; + private final String hardware = Build.HARDWARE; + @SuppressLint("NewApi") + @SuppressWarnings("deprecation") + private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? + Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2}; + @SuppressLint("NewApi") + private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? + Build.SUPPORTED_32_BIT_ABIS : null; + @SuppressLint("NewApi") + private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? + Build.SUPPORTED_64_BIT_ABIS : null; + + public DeviceInfo(Context context) { + PackageInfo packageInfo; + try { + packageInfo = context.getPackageManager() + .getPackageInfo(context.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + packageInfo = null; + } + if (packageInfo != null) { + versionCode = packageInfo.versionCode; + versionName = packageInfo.versionName; + } else { + versionCode = -1; + versionName = null; + } + } + + public String toMarkdown() { + return "Device info:\n" + + "---\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
App version" + versionName + "
App version code" + versionCode + "
Android build version" + buildVersion + "
Android release version" + releaseVersion + "
Android SDK version" + sdkVersion + "
Android build ID" + buildID + "
Device brand" + brand + "
Device manufacturer" + manufacturer + "
Device name" + device + "
Device model" + model + "
Device product name" + product + "
Device hardware name" + hardware + "
ABIs" + Arrays.toString(abis) + "
ABIs (32bit)" + Arrays.toString(abis32Bits) + "
ABIs (64bit)" + Arrays.toString(abis64Bits) + "
\n"; + } + + @Override + public String toString() { + return "App version: " + versionName + "\n" + + "App version code: " + versionCode + "\n" + + "Android build version: " + buildVersion + "\n" + + "Android release version: " + releaseVersion + "\n" + + "Android SDK version: " + sdkVersion + "\n" + + "Android build ID: " + buildID + "\n" + + "Device brand: " + brand + "\n" + + "Device manufacturer: " + manufacturer + "\n" + + "Device name: " + device + "\n" + + "Device model: " + model + "\n" + + "Device product name: " + product + "\n" + + "Device hardware name: " + hardware + "\n" + + "ABIs: " + Arrays.toString(abis) + "\n" + + "ABIs (32bit): " + Arrays.toString(abis32Bits) + "\n" + + "ABIs (64bit): " + Arrays.toString(abis64Bits); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/Report.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/Report.java new file mode 100644 index 00000000..3d8345fe --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/Report.java @@ -0,0 +1,31 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport.model; + +import com.kabouzeid.gramophone.ui.activities.bugreport.model.github.ExtraInfo; + +public class Report { + private final String title; + + private final String description; + + private final DeviceInfo deviceInfo; + + private final ExtraInfo extraInfo; + + public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) { + this.title = title; + this.description = description; + this.deviceInfo = deviceInfo; + this.extraInfo = extraInfo; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description + "\n\n" + + "-\n\n" + + deviceInfo.toMarkdown() + "\n\n" + + extraInfo.toMarkdown(); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/ExtraInfo.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/ExtraInfo.java new file mode 100644 index 00000000..2524bfa9 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/ExtraInfo.java @@ -0,0 +1,59 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport.model.github; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class ExtraInfo { + private final Map extraInfo = new LinkedHashMap<>(); + + public void put(String key, String value) { + extraInfo.put(key, value); + } + + public void put(String key, boolean value) { + extraInfo.put(key, Boolean.toString(value)); + } + + public void put(String key, double value) { + extraInfo.put(key, Double.toString(value)); + } + + public void put(String key, float value) { + extraInfo.put(key, Float.toString(value)); + } + + public void put(String key, long value) { + extraInfo.put(key, Long.toString(value)); + } + + public void put(String key, int value) { + extraInfo.put(key, Integer.toString(value)); + } + + public void put(String key, Object value) { + extraInfo.put(key, String.valueOf(value)); + } + + public void remove(String key) { + extraInfo.remove(key); + } + + public String toMarkdown() { + if (extraInfo.isEmpty()) return ""; + + StringBuilder output = new StringBuilder(); + output.append("Extra info:\n" + + "---\n" + + "\n"); + for (String key : extraInfo.keySet()) { + output.append("\n"); + } + output.append("
") + .append(key) + .append("") + .append(extraInfo.get(key)) + .append("
\n"); + + return output.toString(); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java new file mode 100644 index 00000000..97430f36 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java @@ -0,0 +1,40 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport.model.github; + +import android.text.TextUtils; + +public class GithubLogin { + private final String username; + + private final String password; + + private final String apiToken; + + public GithubLogin(String username, String password) { + this.username = username; + this.password = password; + this.apiToken = null; + } + + public GithubLogin(String apiToken) { + this.username = null; + this.password = null; + this.apiToken = apiToken; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean shouldUseApiToken() { + return TextUtils.isEmpty(username) || TextUtils.isEmpty(password); + } + + public String getApiToken() { + return apiToken; + } + +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubTarget.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubTarget.java new file mode 100644 index 00000000..5efbb6b6 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubTarget.java @@ -0,0 +1,20 @@ +package com.kabouzeid.gramophone.ui.activities.bugreport.model.github; + +public class GithubTarget { + private final String username; + + private final String repository; + + public GithubTarget(String username, String repository) { + this.username = username; + this.repository = repository; + } + + public String getUsername() { + return username; + } + + public String getRepository() { + return repository; + } +} diff --git a/app/src/main/res/drawable/ic_send_white_24dp.xml b/app/src/main/res/drawable/ic_send_white_24dp.xml new file mode 100644 index 00000000..9cfa9cce --- /dev/null +++ b/app/src/main/res/drawable/ic_send_white_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/activity_bug_report.xml b/app/src/main/res/layout/activity_bug_report.xml new file mode 100644 index 00000000..9bd6d882 --- /dev/null +++ b/app/src/main/res/layout/activity_bug_report.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bug_report_card_device_info.xml b/app/src/main/res/layout/bug_report_card_device_info.xml new file mode 100644 index 00000000..d934adbf --- /dev/null +++ b/app/src/main/res/layout/bug_report_card_device_info.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/bug_report_card_report.xml b/app/src/main/res/layout/bug_report_card_report.xml new file mode 100644 index 00000000..5f4f37f4 --- /dev/null +++ b/app/src/main/res/layout/bug_report_card_report.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/raw/notices.xml b/app/src/main/res/raw/notices.xml index 5ce390f5..aca186cb 100644 --- a/app/src/main/res/raw/notices.xml +++ b/app/src/main/res/raw/notices.xml @@ -22,6 +22,12 @@ Copyright (C) 2015 Haruki Hasegawa Apache Software License 2.0 + + android-issue-reporter + https://github.com/HeinrichReimer/android-issue-reporter + Copyright 2016 Heinrich Reimer + Apache Software License 2.0 + Android-ObservableScrollView https://github.com/ksoichiro/Android-ObservableScrollView diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2fdc304b..11cf9640 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -248,4 +248,27 @@ Phonograph - Big Phonograph - Classic Phonograph - Small + Report an issue + Issue + Login + Title + Description + Device info + Send using GitHub account + Send manually + Username + Password + Please enter an issue title + Please enter an issue description + Please enter your valid GitHub username + Please enter your valid GitHub password + Uploading report to GitHub… + Unable to send report + Wrong username or password + Invalid access token. Please contact the app developer. + Issues are not enabled for the selected repository. Please contact the app developer. + An unexpected error occurred. Please contact the app developer. + Copied device info to clipboard. + Your account data is only used for authentication. + You will be forwarded to the issue tracker website.