├── beetle ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values-night │ │ │ └── colors.xml │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── themes.xml │ │ │ ├── attrs.xml │ │ │ └── strings.xml │ │ ├── drawable │ │ │ ├── ic_check.xml │ │ │ ├── ic_back.xml │ │ │ ├── ic_close.xml │ │ │ ├── ic_send.xml │ │ │ ├── ic_undo.xml │ │ │ ├── bg_paint_yellow.xml │ │ │ ├── ic_article.xml │ │ │ ├── bg_paint_black.xml │ │ │ ├── bg_chip.xml │ │ │ └── ic_beetle.xml │ │ ├── menu │ │ │ └── menu_feedback.xml │ │ └── layout │ │ │ ├── item_label.xml │ │ │ ├── item_assignee.xml │ │ │ ├── activity_edit.xml │ │ │ └── activity_feedback.xml │ │ ├── kotlin │ │ └── com │ │ │ └── karacca │ │ │ └── beetle │ │ │ ├── data │ │ │ ├── model │ │ │ │ ├── IssueRequest.kt │ │ │ │ ├── RepositoryInstallation.kt │ │ │ │ ├── Issue.kt │ │ │ │ ├── Image.kt │ │ │ │ ├── Label.kt │ │ │ │ ├── Collaborator.kt │ │ │ │ └── AccessToken.kt │ │ │ ├── repository │ │ │ │ ├── ImageRepository.kt │ │ │ │ └── GitHubRepository.kt │ │ │ └── service │ │ │ │ ├── ImageService.kt │ │ │ │ └── GitHubService.kt │ │ │ ├── utils │ │ │ ├── ReflectionUtils.kt │ │ │ ├── LifecycleHandler.kt │ │ │ ├── MarkdownUtils.kt │ │ │ ├── CollectDataTask.kt │ │ │ ├── NotificationUtils.kt │ │ │ ├── DeviceUtils.kt │ │ │ ├── BitmapUtils.kt │ │ │ └── ShakeDetector.kt │ │ │ ├── BeetleConfig.kt │ │ │ ├── ui │ │ │ ├── widget │ │ │ │ ├── HorizontalItemDecorator.kt │ │ │ │ └── FingerPaintImageView.kt │ │ │ ├── adapter │ │ │ │ ├── AssigneeAdapter.kt │ │ │ │ └── LabelAdapter.kt │ │ │ ├── EditScreenshotActivity.kt │ │ │ └── FeedbackActivity.kt │ │ │ └── Beetle.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle.kts ├── local.defaults.properties ├── docs ├── images │ ├── badge.png │ ├── edit.png │ └── feedback.png ├── terms.md └── privacy_policy.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .editorconfig ├── settings.gradle.kts ├── CHANGELOG.md ├── spotless ├── copyright.kt ├── copyright.kts └── copyright.xml ├── .github └── workflows │ ├── Build.yaml │ └── Publish.yaml ├── README.md ├── gradlew.bat ├── .gitignore ├── gradlew └── LICENSE /beetle/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /local.defaults.properties: -------------------------------------------------------------------------------- 1 | GITHUB_APP_ID=ID 2 | FREE_IMAGE_API_KEY=KEY 3 | -------------------------------------------------------------------------------- /docs/images/badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karacca/beetle/HEAD/docs/images/badge.png -------------------------------------------------------------------------------- /docs/images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karacca/beetle/HEAD/docs/images/edit.png -------------------------------------------------------------------------------- /docs/images/feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karacca/beetle/HEAD/docs/images/feedback.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karacca/beetle/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 2 | android.useAndroidX=true 3 | kotlin.code.style=official 4 | android.nonTransitiveRClass=true 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | insert_final_newline = true 6 | 7 | [*.{yml, json}] 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.{kt, kts, java}] 12 | indent_size = 4 13 | max_line_length = 100 -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | rootProject.name = "beetle" 10 | include(":beetle") 11 | include(":sample", ":sample-compose") 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 07 14:37:07 TRT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.0.1] - August 18, 2022 4 | 5 | - Add App ID, App Version and BuildType fields 6 | 7 | ## [2.0.0] - August 15, 2022 8 | 9 | - Beetle is completely refactored 10 | - GitHub API integration added 11 | - Feedback & Edit Screenshot screens redesigned 12 | -------------------------------------------------------------------------------- /spotless/copyright.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright $YEAR Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /spotless/copyright.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright $YEAR Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /spotless/copyright.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /beetle/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | #424242 16 | 17 | -------------------------------------------------------------------------------- /beetle/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /beetle/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | #E0E0E0 16 | #1976D2 17 | #FFEE58 18 | #80FFEE58 19 | 20 | -------------------------------------------------------------------------------- /beetle/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/Build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | concurrency: 9 | group: build-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 60 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | 21 | - name: Validate Gradle Wrapper 22 | uses: gradle/wrapper-validation-action@v1 23 | 24 | - name: Set up JDK 11 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: 11 28 | 29 | - name: Setup Gradle 30 | uses: gradle/gradle-build-action@v2 31 | 32 | - name: Check spotless 33 | run: ./gradlew spotlessCheck --stacktrace 34 | 35 | - name: Check lint 36 | run: ./gradlew lintDebug --stacktrace 37 | 38 | - name: Build all build type and flavor permutations 39 | run: ./gradlew assemble --stacktrace 40 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/IssueRequest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | /** 20 | * @author karacca 21 | * @date 28.07.2022 22 | */ 23 | 24 | internal data class IssueRequest( 25 | val title: String, 26 | val body: String, 27 | val assignees: List, 28 | val labels: List 29 | ) 30 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/RepositoryInstallation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author karacca 23 | * @date 20.07.2022 24 | */ 25 | 26 | internal data class RepositoryInstallation( 27 | @SerializedName("id") 28 | val id: Int? = null 29 | ) 30 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/Issue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author karacca 23 | * @date 28.07.2022 24 | */ 25 | 26 | internal data class Issue( 27 | @SerializedName("number") 28 | val number: Int? = null, 29 | @SerializedName("html_url") 30 | val htmlUrl: String? 31 | ) 32 | -------------------------------------------------------------------------------- /beetle/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /beetle/src/main/res/menu/menu_feedback.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/Image.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author karacca 23 | * @date 28.07.2022 24 | */ 25 | 26 | internal data class Image( 27 | @SerializedName("image") 28 | val image: Image?, 29 | ) { 30 | 31 | data class Image( 32 | @SerializedName("url") 33 | val url: String? 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/Publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGKEY }} 9 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD }} 10 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 11 | BEETLE_VERSION: ${{ github.ref_name }} 12 | 13 | jobs: 14 | publish: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 45 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | 22 | - name: Validate Gradle Wrapper 23 | uses: gradle/wrapper-validation-action@v1 24 | 25 | - name: Set up JDK 11 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: 11 29 | 30 | - name: Setup Gradle 31 | uses: gradle/gradle-build-action@v2 32 | 33 | - name: Check spotless 34 | run: ./gradlew spotlessCheck --stacktrace 35 | 36 | - name: Check lint 37 | run: ./gradlew lintDebug --stacktrace 38 | 39 | - name: Publish 40 | run: ./gradlew publish 41 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/Label.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author karacca 23 | * @date 25.07.2022 24 | */ 25 | 26 | internal data class Label( 27 | @SerializedName("id") 28 | val id: Long? = null, 29 | @SerializedName("name") 30 | val name: String, 31 | @SerializedName("color") 32 | val color: String? = null 33 | ) { 34 | var selected = false 35 | } 36 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_send.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_undo.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/Collaborator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | 21 | /** 22 | * @author karacca 23 | * @date 22.07.2022 24 | */ 25 | 26 | internal data class Collaborator( 27 | @SerializedName("login") 28 | val login: String, 29 | @SerializedName("id") 30 | val id: Int? = null, 31 | @SerializedName("avatar_url") 32 | val avatarUrl: String? = null 33 | ) { 34 | 35 | var selected = false 36 | } 37 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/bg_paint_yellow.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/ic_article.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /beetle/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/bg_paint_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /beetle/src/main/res/drawable/bg_chip.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/utils/ReflectionUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.utils 18 | 19 | import android.content.Context 20 | 21 | /** 22 | * @author karacca 23 | * @date 15.08.2022 24 | */ 25 | 26 | internal object ReflectionUtils { 27 | 28 | fun getBuildConfigValues(context: Context): HashMap { 29 | val result = hashMapOf() 30 | try { 31 | val clazz = Class.forName(context.packageName + ".BuildConfig") 32 | for (field in clazz.fields) { 33 | val fieldName = field.name 34 | val fieldValue = clazz.getField(fieldName).get(null) 35 | if (fieldValue != null) { 36 | result[field.name] = fieldValue 37 | } 38 | } 39 | } catch (exception: Exception) { 40 | } 41 | 42 | return result 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/utils/LifecycleHandler.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.utils 18 | 19 | import android.app.Activity 20 | import android.app.Application 21 | import android.os.Bundle 22 | import com.karacca.beetle.Beetle 23 | 24 | /** 25 | * @author karacca 26 | * @date 12.07.2022 27 | */ 28 | 29 | internal class LifecycleHandler(private val beetle: Beetle) : 30 | Application.ActivityLifecycleCallbacks { 31 | 32 | override fun onActivityResumed(p0: Activity) { 33 | beetle.setActivity(p0) 34 | } 35 | 36 | override fun onActivityPaused(p0: Activity) { 37 | beetle.setActivity(null) 38 | } 39 | 40 | override fun onActivityCreated(p0: Activity, p1: Bundle?) {} 41 | 42 | override fun onActivityStarted(p0: Activity) {} 43 | 44 | override fun onActivityStopped(p0: Activity) {} 45 | 46 | override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {} 47 | 48 | override fun onActivityDestroyed(p0: Activity) {} 49 | } 50 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/BeetleConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle 18 | 19 | /** 20 | * @author karacca 21 | * @date 9.08.2022 22 | */ 23 | 24 | class BeetleConfig { 25 | val userAttributes = arrayListOf>() 26 | 27 | var organization: String? = null 28 | var repository: String? = null 29 | val initialized: Boolean 30 | get() = organization != null && repository != null 31 | 32 | var enableAssignees = true 33 | var enableLabels = true 34 | 35 | fun key(key: String, value: String) { 36 | userAttributes.add(key to value) 37 | } 38 | 39 | fun key(key: String, value: Boolean) { 40 | userAttributes.add(key to value) 41 | } 42 | 43 | fun key(key: String, value: Double) { 44 | userAttributes.add(key to value) 45 | } 46 | 47 | fun key(key: String, value: Float) { 48 | userAttributes.add(key to value) 49 | } 50 | 51 | fun key(key: String, value: Int) { 52 | userAttributes.add(key to value) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/model/AccessToken.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.model 18 | 19 | import com.google.gson.annotations.SerializedName 20 | import java.text.SimpleDateFormat 21 | import java.util.Date 22 | import java.util.Locale 23 | import java.util.TimeZone 24 | 25 | /** 26 | * @author karacca 27 | * @date 22.07.2022 28 | */ 29 | 30 | internal data class AccessToken( 31 | @SerializedName("token") 32 | val token: String? = null, 33 | @SerializedName("expires_at") 34 | val expiresAt: String? = null 35 | ) { 36 | 37 | fun isValid(): Boolean { 38 | if (token == null || expiresAt == null) { 39 | return false 40 | } 41 | 42 | val dateFormat = SimpleDateFormat(DATE_PATTERN, Locale.getDefault()) 43 | dateFormat.timeZone = TimeZone.getTimeZone("UTC") 44 | return dateFormat.parse(expiresAt)!! > Date() 45 | } 46 | 47 | companion object { 48 | @Suppress("SpellCheckingInspection") 49 | private const val DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/ui/widget/HorizontalItemDecorator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.ui.widget 18 | 19 | import android.graphics.Rect 20 | import android.view.View 21 | import androidx.recyclerview.widget.RecyclerView 22 | 23 | /** 24 | * @author karacca 25 | * @date 28.07.2022 26 | */ 27 | 28 | internal class HorizontalItemDecorator( 29 | private val spacing: Int 30 | ) : RecyclerView.ItemDecoration() { 31 | 32 | override fun getItemOffsets( 33 | outRect: Rect, 34 | view: View, 35 | parent: RecyclerView, 36 | state: RecyclerView.State 37 | ) { 38 | super.getItemOffsets(outRect, view, parent, state) 39 | 40 | val position = parent.getChildLayoutPosition(view) 41 | val isFirstItem = position == 0 42 | 43 | @Suppress("UNUSED_VARIABLE") 44 | val isLastItem = position == (parent.adapter?.itemCount ?: 1) - 1 45 | 46 | outRect.right = spacing 47 | outRect.left = if (!isFirstItem) { 48 | spacing 49 | } else { 50 | 0 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/repository/ImageRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.repository 18 | 19 | import android.net.Uri 20 | import androidx.core.net.toFile 21 | import com.karacca.beetle.BuildConfig 22 | import com.karacca.beetle.data.model.Image 23 | import com.karacca.beetle.data.service.ImageService 24 | import okhttp3.MediaType.Companion.toMediaType 25 | import okhttp3.MultipartBody 26 | import okhttp3.RequestBody.Companion.asRequestBody 27 | import okhttp3.RequestBody.Companion.toRequestBody 28 | 29 | /** 30 | * @author karacca 31 | * @date 8.08.2022 32 | */ 33 | 34 | internal class ImageRepository { 35 | private val imageService = ImageService.newInstance() 36 | 37 | suspend fun uploadImage(screenshot: Uri): Image { 38 | val file = screenshot.toFile() 39 | return imageService.uploadImage( 40 | BuildConfig.FREE_IMAGE_API_KEY.toRequestBody(), 41 | MultipartBody.Part.createFormData( 42 | "source", 43 | file.name, 44 | file.asRequestBody("image/*".toMediaType()) 45 | ) 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /beetle/src/main/res/layout/item_label.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 23 | 24 | 29 | 30 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /beetle/src/main/res/layout/item_assignee.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 23 | 24 | 29 | 30 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/data/service/ImageService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.data.service 18 | 19 | import com.google.gson.Gson 20 | import com.karacca.beetle.BuildConfig 21 | import com.karacca.beetle.data.model.Image 22 | import okhttp3.MultipartBody 23 | import okhttp3.OkHttpClient 24 | import okhttp3.RequestBody 25 | import okhttp3.logging.HttpLoggingInterceptor 26 | import retrofit2.Retrofit 27 | import retrofit2.converter.gson.GsonConverterFactory 28 | import retrofit2.http.Multipart 29 | import retrofit2.http.POST 30 | import retrofit2.http.Part 31 | 32 | /** 33 | * @author karacca 34 | * @date 28.07.2022 35 | */ 36 | 37 | internal interface ImageService { 38 | 39 | @Multipart 40 | @POST("upload") 41 | suspend fun uploadImage( 42 | @Part("key") key: RequestBody, 43 | @Part image: MultipartBody.Part 44 | ): Image 45 | 46 | companion object { 47 | private const val BASE_URL = "https://freeimage.host/api/1/" 48 | 49 | fun newInstance(): ImageService { 50 | val loggingInterceptor = HttpLoggingInterceptor().apply { 51 | setLevel( 52 | if (BuildConfig.DEBUG) { 53 | HttpLoggingInterceptor.Level.BODY 54 | } else { 55 | HttpLoggingInterceptor.Level.NONE 56 | } 57 | ) 58 | } 59 | 60 | val okHttpClient = OkHttpClient.Builder() 61 | .addInterceptor(loggingInterceptor) 62 | .build() 63 | 64 | return Retrofit.Builder() 65 | .client(okHttpClient) 66 | .baseUrl(BASE_URL) 67 | .addConverterFactory(GsonConverterFactory.create(Gson())) 68 | .build() 69 | .create(ImageService::class.java) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/utils/MarkdownUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.utils 18 | 19 | import android.content.Context 20 | import com.karacca.beetle.R 21 | 22 | /** 23 | * @author karacca 24 | * @date 8.08.2022 25 | */ 26 | 27 | internal object MarkdownUtils { 28 | 29 | fun createDescription( 30 | context: Context, 31 | description: String, 32 | imageUrl: String?, 33 | appAndDeviceData: List>, 34 | userAttributes: List> 35 | ): String { 36 | val sb = StringBuilder() 37 | sb.appendLine("## ${context.getString(R.string.description)}") 38 | sb.appendLine(description) 39 | 40 | if (appAndDeviceData.isNotEmpty()) { 41 | sb.appendLine("## ${context.getString(R.string.app_and_device_data)}") 42 | val property = context.getString(R.string.key) 43 | val value = context.getString(R.string.value) 44 | sb.appendLine("|$property|$value|") 45 | sb.appendLine("|:-|:-|") 46 | 47 | for ((k, v) in appAndDeviceData) { 48 | sb.appendLine("|**$k**|$v|") 49 | } 50 | } 51 | 52 | if (userAttributes.isNotEmpty()) { 53 | sb.appendLine("## ${context.getString(R.string.user_attributes)}") 54 | val key = context.getString(R.string.key) 55 | val value = context.getString(R.string.value) 56 | sb.appendLine("|$key|$value|") 57 | sb.appendLine("|:-|:-|") 58 | 59 | for ((k, v) in userAttributes) { 60 | sb.appendLine("|**$k**|$v|") 61 | } 62 | } 63 | 64 | if (imageUrl != null) { 65 | sb.appendLine("## ${context.getString(R.string.feedback_screenshot_title)}") 66 | sb.appendLine("\"screenshot\"") 67 | } 68 | 69 | return sb.toString() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /beetle/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | Send Feedback 16 | Tap here to send feedback 17 | Thank you for your feedback #%1$d 18 | Tap here to see the details 19 | 20 | Title: 21 | Assignees: 22 | Labels: 23 | Give a short description 24 | Describe the issue you are experiencing. 25 | Please detail as much information as possible regarding your issue. 26 | Screenshot 27 | Tap here to highlight or hide info 28 | System Logs 29 | Tap here to view 30 | 31 | Description 32 | Key 33 | Value 34 | App & Device Data 35 | User Attributes 36 | App ID 37 | App Version 38 | Build Type 39 | Device 40 | OS Version 41 | Language 42 | Display 43 | Landscape 44 | Portrait 45 | 46 | Something went wrong 47 | Feedback Results 48 | Providing extra information 49 | to users about their feedbacks 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Beetle Beetle

2 | 3 | [![Build](https://img.shields.io/github/workflow/status/karacca/beetle/Build)](https://github.com/karacca/beetle/actions/workflows/Build.yaml) 4 | [![Maven Central](https://img.shields.io/maven-central/v/com.karacca/beetle)](https://search.maven.org/artifact/com.karacca/beetle) 5 | [![License](https://img.shields.io/github/license/karacca/beetle)](https://www.apache.org/licenses/LICENSE-2.0) 6 | 7 | Collect feedback & bug reports on your Android apps into your GitHub Issues. 8 | 9 | ## Introduction & Features 10 | 11 | Beetle is a lightweight Android library that enables your users to submit feedback from their applications. It uses GitHub API and collect users feedbacks as GitHub Issues like [this one](https://github.com/karacca/beetle/issues/30). 12 | 13 | * Shake gesture detection for starting a feedback process 14 | * Collect information about the device. You can also add extra information you may add using `Beetle.configure()` 15 | * Users can add Title & Description about the Issue they are having 16 | * Users can assign developers & labels to the Issue 17 | * Current screenshot will be added to the Issue and also can be edited by the user 18 | * JIRA Integration (Coming soon 🚀) 19 | 20 | ## Screenshots 21 | 22 | Feedback Edit 23 | 24 | ## Download & Quick Start 25 | 26 | 1. Install [Beetle](https://github.com/marketplace/beetle-app) to your GitHub repository so it can take action on your behalf and create Issues 27 | 2. Add Beetle dependency to your Android app 28 | ```kotlin 29 | dependencies { 30 | implementation("com.karacca:beetle:2.0.1") 31 | } 32 | ``` 33 | 3. Initialize Beetle inside your Application 34 | ```kotlin 35 | class MyApplication : Application() { 36 | 37 | override fun onCreate() { 38 | super.onCreate() 39 | Beetle.init(this, "username", "repository") 40 | } 41 | } 42 | ``` 43 | 4. [Optional] Make configurations and add additional data 44 | ```kotlin 45 | Beetle.configure { 46 | enableAssignees = true 47 | enableLabels = true 48 | key("user_id", user.id) 49 | } 50 | ``` 51 | 52 | ## License 53 | 54 | Copyright 2022 Omer Karaca 55 | 56 | Licensed under the Apache License, Version 2.0 (the "License"); 57 | you may not use this file except in compliance with the License. 58 | You may obtain a copy of the License at 59 | 60 | https://www.apache.org/licenses/LICENSE-2.0 61 | 62 | Unless required by applicable law or agreed to in writing, software 63 | distributed under the License is distributed on an "AS IS" BASIS, 64 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 65 | See the License for the specific language governing permissions and 66 | limitations under the License. 67 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/utils/CollectDataTask.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @file:Suppress("DEPRECATION") 18 | 19 | package com.karacca.beetle.utils 20 | 21 | /** 22 | * @author karacca 23 | * @date 17.07.2022 24 | */ 25 | 26 | import android.annotation.SuppressLint 27 | import android.app.Activity 28 | import android.content.Context 29 | import android.graphics.Bitmap 30 | import android.net.Uri 31 | import android.os.AsyncTask 32 | import android.util.Log 33 | import androidx.annotation.WorkerThread 34 | import java.io.File 35 | 36 | @Suppress("DEPRECATION") 37 | @SuppressLint("StaticFieldLeak") 38 | internal class CollectDataTask( 39 | private var activity: Activity?, 40 | private var listener: OnCollectDataTaskListener 41 | ) : AsyncTask() { 42 | 43 | @Deprecated("Deprecated in Java") 44 | override fun doInBackground(vararg params: Bitmap?): Uri? { 45 | if (activity == null) 46 | return null 47 | 48 | val root = getScreenshotDirectoryRoot(activity!!) ?: return null 49 | val directory = File(root) 50 | if (directory.exists()) { 51 | val oldFiles = directory.listFiles() 52 | if (oldFiles != null && oldFiles.isNotEmpty()) { 53 | for (oldScreenShot in oldFiles) { 54 | if (!oldScreenShot.delete()) { 55 | Log.e(TAG, "Could not delete old screenshot: $oldScreenShot") 56 | } 57 | } 58 | } 59 | } 60 | 61 | var uri: Uri? = null 62 | val bitmap = if (params.isEmpty()) null else params[0] 63 | 64 | bitmap?.let { 65 | val screenShotFile = BitmapUtils.writeBitmapToFile(bitmap, directory) 66 | uri = Uri.fromFile(screenShotFile) 67 | } 68 | 69 | return uri 70 | } 71 | 72 | @Deprecated("Deprecated in Java") 73 | override fun onPostExecute(result: Uri?) { 74 | super.onPostExecute(result) 75 | listener.onDataReady(result) 76 | } 77 | 78 | interface OnCollectDataTaskListener { 79 | fun onDataReady(data: Uri?) 80 | } 81 | 82 | companion object { 83 | const val TAG = "CollectDataTask" 84 | private const val SCREENSHOT_DIRECTORY = "/screenshots" 85 | 86 | @WorkerThread 87 | fun getScreenshotDirectoryRoot(context: Context) = 88 | if (context.filesDir != null) "${context.filesDir}$SCREENSHOT_DIRECTORY" else null 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/ui/adapter/AssigneeAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.ui.adapter 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import androidx.appcompat.widget.AppCompatImageView 23 | import androidx.appcompat.widget.LinearLayoutCompat 24 | import androidx.recyclerview.widget.DiffUtil 25 | import androidx.recyclerview.widget.ListAdapter 26 | import androidx.recyclerview.widget.RecyclerView 27 | import coil.load 28 | import coil.transform.RoundedCornersTransformation 29 | import com.google.android.material.textview.MaterialTextView 30 | import com.karacca.beetle.R 31 | import com.karacca.beetle.data.model.Collaborator 32 | 33 | /** 34 | * @author karacca 35 | * @date 27.07.2022 36 | */ 37 | 38 | internal class AssigneeAdapter( 39 | private val listener: (Int) -> Unit 40 | ) : ListAdapter(CollaboratorDiff) { 41 | 42 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( 43 | LayoutInflater.from(parent.context).inflate( 44 | R.layout.item_assignee, 45 | parent, 46 | false 47 | ) 48 | ) 49 | 50 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 51 | holder.bind(getItem(position)) 52 | holder.itemView.setOnClickListener { listener.invoke(position) } 53 | } 54 | 55 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 56 | 57 | private val rootLayout = itemView.findViewById(R.id.layout_root) 58 | private val textView = itemView.findViewById(R.id.text_view) 59 | private val imageView = itemView.findViewById(R.id.image_view) 60 | 61 | fun bind(collaborator: Collaborator) { 62 | rootLayout.isSelected = collaborator.selected 63 | textView.text = collaborator.login 64 | imageView.load(collaborator.avatarUrl) { 65 | transformations(RoundedCornersTransformation(24f)) 66 | } 67 | } 68 | } 69 | 70 | object CollaboratorDiff : DiffUtil.ItemCallback() { 71 | override fun areItemsTheSame(oldItem: Collaborator, newItem: Collaborator): Boolean { 72 | return oldItem.id == newItem.id 73 | } 74 | 75 | override fun areContentsTheSame(oldItem: Collaborator, newItem: Collaborator): Boolean { 76 | return false 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /beetle/src/main/kotlin/com/karacca/beetle/ui/adapter/LabelAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Omer Karaca 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.karacca.beetle.ui.adapter 18 | 19 | import android.graphics.Color 20 | import android.view.LayoutInflater 21 | import android.view.View 22 | import android.view.ViewGroup 23 | import androidx.appcompat.widget.AppCompatImageView 24 | import androidx.appcompat.widget.LinearLayoutCompat 25 | import androidx.core.graphics.drawable.toDrawable 26 | import androidx.recyclerview.widget.DiffUtil 27 | import androidx.recyclerview.widget.ListAdapter 28 | import androidx.recyclerview.widget.RecyclerView 29 | import coil.load 30 | import coil.transform.RoundedCornersTransformation 31 | import com.google.android.material.textview.MaterialTextView 32 | import com.karacca.beetle.R 33 | import com.karacca.beetle.data.model.Label 34 | 35 | /** 36 | * @author karacca 37 | * @date 28.07.2022 38 | */ 39 | 40 | internal class LabelAdapter( 41 | private val listener: (Int) -> Unit 42 | ) : ListAdapter(LabelDiff) { 43 | 44 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( 45 | LayoutInflater.from(parent.context).inflate( 46 | R.layout.item_label, 47 | parent, 48 | false 49 | ) 50 | ) 51 | 52 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 53 | holder.bind(getItem(position)) 54 | holder.itemView.setOnClickListener { listener.invoke(position) } 55 | } 56 | 57 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 58 | 59 | private val rootLayout = itemView.findViewById(R.id.layout_root) 60 | private val textView = itemView.findViewById(R.id.text_view) 61 | private val imageView = itemView.findViewById(R.id.image_view) 62 | 63 | fun bind(label: Label) { 64 | rootLayout.isSelected = label.selected 65 | textView.text = label.name 66 | imageView.load(Color.parseColor("#${label.color}").toDrawable()) { 67 | transformations(RoundedCornersTransformation(24f)) 68 | } 69 | } 70 | } 71 | 72 | object LabelDiff : DiffUtil.ItemCallback