├── .circleci ├── config.yml └── generate_checksum ├── .github └── workflows │ ├── deploy-javadoc.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── CodeStyle.xml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── plugins ├── build.gradle.kts ├── library │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── deploygate │ │ └── plugins │ │ ├── BaseSdkPlugin.kt │ │ ├── MavenPublishPlugin.kt │ │ ├── SdkMockPlugin.kt │ │ ├── SdkPlugin.kt │ │ ├── dsl │ │ └── SdkExtension.kt │ │ ├── ext │ │ └── ProjectExt.kt │ │ ├── internal │ │ ├── SdkExtensionImpl.kt │ │ └── SdkMetaData.kt │ │ └── tasks │ │ └── GenerateMetaDataJsonTask.kt ├── sample │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── deploygate │ │ └── plugins │ │ └── SampleAppPlugin.kt └── settings.gradle ├── sample ├── README.md ├── build.gradle ├── proguard-project.txt └── src │ ├── devmock │ └── res │ │ ├── values-ja │ │ └── strings.xml │ │ └── values │ │ └── strings.xml │ ├── devreal │ ├── AndroidManifest.xml │ └── res │ │ ├── values-ja │ │ └── strings.xml │ │ └── values │ │ └── strings.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── deploygate │ │ │ └── sample │ │ │ ├── App.java │ │ │ ├── BootBroadcastReceiver.java │ │ │ ├── DirectBootBroadcastReceiver.java │ │ │ └── SampleActivity.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ ├── ic_launcher.png │ │ └── logo_w.png │ │ ├── layout │ │ └── activity_crash_me.xml │ │ ├── values-ja │ │ └── strings.xml │ │ ├── values-large │ │ └── dimens.xml │ │ ├── values-v11 │ │ └── styles.xml │ │ ├── values-v14 │ │ └── styles.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── stablemock │ └── res │ │ ├── values-ja │ │ └── strings.xml │ │ └── values │ │ └── strings.xml │ └── stablereal │ ├── AndroidManifest.xml │ └── res │ ├── values-ja │ └── strings.xml │ └── values │ └── strings.xml ├── scripts ├── release.sh ├── verify-bytecode-version └── verify-publications.bash ├── sdk ├── build.gradle ├── deploygate-sdk-proguard-rules.txt └── src │ ├── debug │ └── java │ │ └── com │ │ └── deploygate │ │ └── sdk │ │ └── internal │ │ └── Config.java │ ├── main │ ├── AndroidManifest.xml │ ├── aidl │ │ └── com │ │ │ └── deploygate │ │ │ └── service │ │ │ ├── IDeployGateSdkService.aidl │ │ │ └── IDeployGateSdkServiceCallback.aidl │ └── java │ │ └── com │ │ └── deploygate │ │ ├── sdk │ │ ├── ClientId.java │ │ ├── Compatibility.java │ │ ├── CustomAttributes.java │ │ ├── CustomLog.java │ │ ├── CustomLogConfiguration.java │ │ ├── CustomLogInstructionSerializer.java │ │ ├── DeployGate.java │ │ ├── DeployGateCallback.java │ │ ├── DeployGateCaptureCreateCallback.java │ │ ├── DeployGateClient.java │ │ ├── DeployGateInitializeCallback.java │ │ ├── DeployGateProvider.java │ │ ├── DeployGateSdkConfiguration.java │ │ ├── DeployGateStatusChangeCallback.java │ │ ├── DeployGateUncaughtExceptionHandler.java │ │ ├── DeployGateUpdateAvailableCallback.java │ │ ├── HostApp.java │ │ ├── ILogcatInstructionSerializer.java │ │ ├── Instruction.java │ │ ├── LogcatInstructionSerializer.java │ │ ├── LogcatProcess.java │ │ ├── SdkDeviceStatesCollector.java │ │ ├── SendLogcatRequest.java │ │ └── internal │ │ │ ├── Logger.java │ │ │ ├── VisibilityLifecycleCallbacks.java │ │ │ └── annotations │ │ │ └── Experimental.java │ │ └── service │ │ └── DeployGateEvent.java │ ├── release │ └── java │ │ └── com │ │ └── deploygate │ │ └── sdk │ │ └── internal │ │ └── Config.java │ └── test │ └── java │ └── com │ └── deploygate │ ├── sdk │ ├── ClientIdTest.java │ ├── CompatibilityTest.java │ ├── CustomAttributesInterfaceTest.java │ ├── CustomAttributesTest.java │ ├── CustomLogInstructionSerializerTest.java │ ├── CustomLogTest.java │ ├── DeployGateClientTest.java │ ├── DeployGateInterfaceTest.java │ ├── DeployGateSdkConfigurationInterfaceTest.java │ ├── DeployGateSdkConfigurationTest.java │ ├── DeployGateTest.java │ ├── HostAppTest.java │ ├── LogcatInstructionSerializerTest.java │ ├── LogcatProcessTest.java │ ├── SendLogcatRequestTest.java │ ├── helper │ │ ├── Bundles.java │ │ └── FakeLogcat.java │ ├── mockito │ │ └── BundleMatcher.java │ └── truth │ │ └── BundleSubject.java │ └── service │ └── FakeDeployGateClientService.java ├── sdkMock ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── deploygate │ │ └── sdk │ │ ├── CustomAttributes.java │ │ ├── CustomLogConfiguration.java │ │ ├── DeployGate.java │ │ ├── DeployGateCallback.java │ │ ├── DeployGateCaptureCreateCallback.java │ │ ├── DeployGateInitializeCallback.java │ │ ├── DeployGateSdkConfiguration.java │ │ ├── DeployGateStatusChangeCallback.java │ │ └── DeployGateUpdateAvailableCallback.java │ └── test │ └── java │ └── com │ └── deploygate │ └── sdk │ ├── CustomAttributesInterfaceTest.java │ ├── DeployGateInterfaceTest.java │ └── DeployGateSdkConfigurationInterfaceTest.java └── settings.gradle /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | android: 5 | docker: 6 | - image: cimg/android:2023.07 7 | working_directory: ~/deploygate-android-sdk 8 | environment: 9 | GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" -Dorg.gradle.daemon=false' 10 | 11 | commands: 12 | restore_gradle_cache: 13 | parameters: &gradle_cache_parameters 14 | cache_version: 15 | type: string 16 | default: v1 17 | steps: 18 | - run: .circleci/generate_checksum > gradle.checksum 19 | - restore_cache: 20 | keys: 21 | - gradle-<< parameters.cache_version >>-{{ checksum "gradle.checksum" }} 22 | save_gradle_cache: 23 | parameters: *gradle_cache_parameters 24 | steps: 25 | - save_cache: 26 | paths: 27 | - ~/.android 28 | - ~/.gradle 29 | - .gradle 30 | - ~/.m2 31 | key: gradle-<< parameters.cache_version >>-{{ checksum "gradle.checksum" }} 32 | 33 | jobs: 34 | build: 35 | executor: android 36 | steps: 37 | - checkout 38 | - restore_gradle_cache 39 | - run: ./gradlew verifyBytecodeVersionDebug verifyBytecodeVersionRelease --continue 40 | - save_gradle_cache 41 | test: 42 | executor: android 43 | steps: 44 | - checkout 45 | - restore_gradle_cache 46 | - run: ./gradlew sdk:test sdkMock:test --continue 47 | - save_gradle_cache 48 | - store_test_results: 49 | path: sdk/build/test-results 50 | when: always 51 | - store_test_results: 52 | path: sdkMock/build/test-results 53 | when: always 54 | 55 | workflows: 56 | version: 2 57 | on_commit: 58 | jobs: 59 | - build 60 | - test 61 | -------------------------------------------------------------------------------- /.circleci/generate_checksum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | while read path; do 6 | md5sum $path 7 | done < <(find . -name "build.gradle" | sort) 8 | 9 | while read path; do 10 | md5sum $path 11 | done < <(find . -name "*.build.gradle" | sort) 12 | -------------------------------------------------------------------------------- /.github/workflows/deploy-javadoc.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Javadoc 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - '**javadoc**' 10 | 11 | 12 | jobs: 13 | check-new-release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | ref: gh-pages 19 | - uses: actions/github-script@v7 20 | id: versions 21 | with: 22 | result-encoding: json 23 | script: | 24 | const fs = require('fs'); 25 | 26 | const opts = github.rest.repos.listReleases.endpoint.merge({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo 29 | }); 30 | const releases = await github.paginate(opts); 31 | 32 | const missings = []; 33 | 34 | releases.forEach((release) => { 35 | if (!release.draft && !release.prerelease && release.tag_name) { 36 | if (!fs.existsSync(release.tag_name)) { 37 | missings.push(release.tag_name); 38 | } 39 | } 40 | }); 41 | 42 | return missings; 43 | outputs: 44 | versions: ${{ steps.versions.outputs.result }} 45 | deploy-new-javadoc: 46 | needs: 47 | - check-new-release 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v4 51 | with: 52 | ref: gh-pages 53 | - id: download-javadoc 54 | if: > 55 | needs.check-new-release.outputs.versions != '[]' 56 | env: 57 | MISSING_VERSIONS: ${{ join(fromJSON(needs.check-new-release.outputs.versions), ' ') }} 58 | run: | 59 | for version in $MISSING_VERSIONS; do 60 | url="https://repo1.maven.org/maven2/com/deploygate/sdk/${version}/sdk-${version}-javadoc.jar" 61 | dist="$version" 62 | 63 | mkdir -p "$dist" 64 | 65 | if curl -sSfL -I --url "$url"; then 66 | curl -sSfL \ 67 | -X GET \ 68 | --url "$url" \ 69 | -o javadoc.jar 70 | unzip javadoc.jar -d "$dist/" 71 | 72 | rm javadoc.jar 73 | fi 74 | 75 | if [[ ! -f "$dist/index.html" ]]; then 76 | rm -fr "$dist" 77 | fi 78 | done 79 | - uses: actions/github-script@v6 80 | with: 81 | script: | 82 | const fs = require('fs'); 83 | const opts = github.rest.repos.listReleases.endpoint.merge({ 84 | owner: context.repo.owner, 85 | repo: context.repo.repo 86 | }); 87 | const releases = await github.paginate(opts); 88 | const metadata = []; 89 | 90 | for (const release of releases) { 91 | if (release.draft || release.prerelease) { 92 | continue; 93 | } 94 | 95 | try { 96 | fs.mkdirSync(release.tag_name); 97 | console.log(`javadocs of ${release.tag_name} are not found`); 98 | metadata.push({ 99 | version: release.tag_name, 100 | release_link: release.html_url, 101 | released_at: release.published_at 102 | }); 103 | } catch (e) { 104 | console.log(`javadocs of ${release.tag_name} are found`); 105 | metadata.push({ 106 | version: release.tag_name, 107 | javadoc_link: release.tag_name, 108 | release_link: release.html_url, 109 | released_at: release.published_at 110 | }); 111 | } 112 | } 113 | 114 | fs.writeFileSync('releases.json', JSON.stringify(metadata)); 115 | - name: "commit and push if needed" 116 | run: | 117 | git config --local user.email "no-reply@deploygate.com" 118 | git config --local user.name "github pages automation" 119 | git add . 120 | if git commit -m ":rocket: ${{ needs.check-new-release.outputs.latest-version }} javadoc"; then 121 | if [[ "${{ github.event_name }}" = "push" ]]; then 122 | echo 'git push origin gh-pages (dry-run)' 123 | else 124 | git push origin gh-pages 125 | fi 126 | fi -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release artifacts 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - '**' 7 | tags: 8 | - '[0-9]+.[0-9]+.[0-9]+' 9 | - '[0-9]+.[0-9]+.[0-9]+-{alpha,rc}[0-9]+' 10 | 11 | jobs: 12 | production-release: 13 | runs-on: ubuntu-latest 14 | env: 15 | GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4g -XX:MaxMetaspaceSize=2g -Dkotlin.daemon.jvm.options=-Xmx1536m" 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-java@v4 19 | with: 20 | java-version: '17.x' 21 | java-package: jdk 22 | distribution: 'temurin' 23 | cache: 'gradle' 24 | - uses: gradle/actions/setup-gradle@v3 25 | - run: RELEASE_VERSION="${GITHUB_REF#refs/tags/}" ./gradlew verifyReleaseVersion 26 | - name: Set up gradle.properties for sigining and nexus 27 | # Add timeout setting due to https://github.com/DeployGate/gradle-deploygate-plugin/runs/2523846388 28 | run: | 29 | echo >> gradle.properties 30 | cat<> gradle.properties 31 | systemProp.org.gradle.internal.http.socketTimeout=120000 32 | NEXUS_USERNAME=${{ secrets.SHARED_NEXUS_TOKEN_USERNAME }} 33 | NEXUS_PASSWORD=${{ secrets.SHARED_NEXUS_TOKEN_PASSWORD }} 34 | EOF 35 | - name: Publish artifacts 36 | run: ./scripts/release.sh 37 | env: 38 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SECRET_KEY }} 39 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }} 40 | - name: Slack Notification 41 | uses: rtCamp/action-slack-notify@v2 42 | env: 43 | SLACK_TITLE: 'Android SDK Release' 44 | SLACK_MESSAGE: "${{ github.ref }} has been published to Sonatype. ref: https://oss.sonatype.org/" 45 | SLACK_WEBHOOK: ${{ secrets.SHARED_FOR_RELEASE_ARTIFACT_SLACK_INCOMING_WEBHOOK_URL }} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build and test projects 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | pull_request: 9 | 10 | # Run this job when new version has been released and available. 11 | workflow_dispatch: 12 | 13 | 14 | jobs: 15 | sdk-build: 16 | runs-on: ubuntu-latest 17 | env: 18 | GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx4g -XX:MaxMetaspaceSize=2g -Dkotlin.daemon.jvm.options=-Xmx1536m" 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-java@v4 22 | with: 23 | java-version: '17.x' 24 | java-package: jdk 25 | distribution: 'temurin' 26 | cache: 'gradle' 27 | - uses: gradle/actions/setup-gradle@v3 28 | - name: Check sdk publications 29 | run: ./scripts/verify-publications.bash sdk 30 | - name: Check sdk-mock publications 31 | run: ./scripts/verify-publications.bash sdkMock 32 | - run: ./gradlew sdk:generateMetaDataJson 33 | - run: | 34 | ls -R ~/.m2/repository 35 | 36 | mkdir -p tmp/artifacts 37 | cp -r ~/.m2/repository/com/deploygate/* tmp/artifacts/ 38 | cp sdk/build/generated-sdk-metadata/sdk-meta-data-* tmp/artifacts/ 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | name: artifacts 42 | path: tmp/artifacts 43 | retention-days: 3 44 | if-no-files-found: error 45 | 46 | build-and-upload: 47 | runs-on: ubuntu-latest 48 | if: > 49 | github.event_name != 'workflow_dispatch' 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: actions/setup-java@v4 53 | with: 54 | java-version: '17.x' 55 | java-package: jdk 56 | distribution: 'temurin' 57 | cache: 'gradle' 58 | - uses: gradle/actions/setup-gradle@v3 59 | - run: ./gradlew sample:bundleDevrealDebug sample:bundleDevrealRelease sample:bundleDevmockDebug sample:bundleDevmockRelease 60 | - name: upload aab files 61 | run: | 62 | if [[ -z '${{ secrets.SHARED_DEPLOYGATE_APP_OWNER_NAME_FOR_PUBLIC_REPO }}' ]]; then 63 | echo '::notice::contributors cannot upload files to deploygate' 64 | exit 0 65 | fi 66 | 67 | if [[ -z '${{ github.event.pull_request.number }}' ]]; then 68 | distribution_name="canary" 69 | else 70 | distribution_name="sdk/pulls/${{ github.event.pull_request.number }}" 71 | fi 72 | 73 | while read -r aab_path; do 74 | curl \ 75 | -F file=@$aab_path \ 76 | -F distribution_name="$distribution_name" \ 77 | -H 'Authorization: token ${{ secrets.SHARED_DEPLOYGATE_API_TOKEN_FOR_PUBLIC_REPO }}' \ 78 | --url 'https://deploygate.com/api/users/${{ secrets.SHARED_DEPLOYGATE_APP_OWNER_NAME_FOR_PUBLIC_REPO }}/apps' | \ 79 | jq -r '"\(.results.package_name) \(.results.distribution.url)"' 80 | done < <(find ./sample/build/outputs/bundle -name "*.aab") 81 | 82 | distribute-stable: 83 | runs-on: ubuntu-latest 84 | if: > 85 | github.event_name == 'workflow_dispatch' 86 | steps: 87 | - uses: actions/checkout@v4 88 | - uses: actions/setup-java@v4 89 | with: 90 | java-version: '17.x' 91 | java-package: jdk 92 | distribution: 'temurin' 93 | cache: 'gradle' 94 | - uses: gradle/actions/setup-gradle@v3 95 | - run: ./gradlew sample:bundleStablerealDistribute sample:bundleStablemockDistribute 96 | - name: upload aab files 97 | run: | 98 | distribution_name="deploygate-sdk-sample" 99 | 100 | while read -r aab_path; do 101 | curl \ 102 | -F file=@$aab_path \ 103 | -F distribution_name="$distribution_name" \ 104 | -H 'Authorization: token ${{ secrets.SHARED_DEPLOYGATE_API_TOKEN_FOR_PUBLIC_REPO }}' \ 105 | --url 'https://deploygate.com/api/users/${{ secrets.SHARED_DEPLOYGATE_APP_OWNER_NAME_FOR_PUBLIC_REPO }}/apps' | \ 106 | jq -r '"\(.results.package_name) \(.results.distribution.url)"' 107 | done < <(find ./sample/build/outputs/bundle -name "*.aab") -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | proguard/ 15 | local.properties 16 | 17 | # gradle 18 | build/ 19 | .gradle 20 | 21 | #idea 22 | *.iml 23 | .idea/ 24 | 25 | .DS_Store -------------------------------------------------------------------------------- /CodeStyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | 18 | 19 | 20 | 25 | 26 | 28 | 29 | 53 | 54 | 55 | 57 | 58 | 59 |
60 | 61 | 62 | 63 | xmlns:android 64 | 65 | ^$ 66 | 67 | 68 | 69 |
70 |
71 | 72 | 73 | 74 | xmlns:.* 75 | 76 | ^$ 77 | 78 | 79 | BY_NAME 80 | 81 |
82 |
83 | 84 | 85 | 86 | .*:id 87 | 88 | http://schemas.android.com/apk/res/android 89 | 90 | 91 | 92 |
93 |
94 | 95 | 96 | 97 | .*:name 98 | 99 | http://schemas.android.com/apk/res/android 100 | 101 | 102 | 103 |
104 |
105 | 106 | 107 | 108 | name 109 | 110 | ^$ 111 | 112 | 113 | 114 |
115 |
116 | 117 | 118 | 119 | style 120 | 121 | ^$ 122 | 123 | 124 | 125 |
126 |
127 | 128 | 129 | 130 | .* 131 | 132 | ^$ 133 | 134 | 135 | BY_NAME 136 | 137 |
138 |
139 | 140 | 141 | 142 | .* 143 | 144 | http://schemas.android.com/apk/res/android 145 | 146 | 147 | ANDROID_ATTRIBUTE_ORDER 148 | 149 |
150 |
151 | 152 | 153 | 154 | .* 155 | 156 | .* 157 | 158 | 159 | BY_NAME 160 | 161 |
162 |
163 |
164 |
165 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeployGate SDK for Android 2 | 3 | [![Build Status](https://travis-ci.org/DeployGate/deploygate-android-sdk.svg?branch=master)](https://travis-ci.org/DeployGate/deploygate-android-sdk) 4 | [![Download](https://img.shields.io/maven-central/v/com.deploygate/sdk)](https://ossindex.sonatype.org/component/pkg:maven/com.deploygate/sdk) 5 | 6 | You can integrate DeployGate's realtime remote logging & crash reporting without code modification on your apps in development. [Sample Code](./sample). 7 | 8 | > 4.3.0 and later require Android Studio whose versions are the latest patch version of 3.3 or above. 9 | > For the more details, please see https://developer.android.com/studio/releases/gradle-plugin#4.0.1 10 | 11 | ## Install 12 | 13 | In your build.gradle of `app` module: 14 | 15 | ```gradle 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | // Use SDK dependency for variants like debug. 22 | debugImplementation 'com.deploygate:sdk:' 23 | 24 | // Use no-op implementation for variants you would like to disable DeployGate SDK. 25 | // see also "Mock" section below for more details 26 | releaseImplementation 'com.deploygate:sdk-mock:' 27 | } 28 | ``` 29 | 30 | Then synchronize, build and upload your app to DeployGate. 31 | 32 | > Since 4.0.0, you don't need to add `DeployGate.install(this)` to your `Application#onCreate` except you have multiple processes. It is automatically called when your application process starts through the ContentProvider initialization. 33 | 34 | ### For Jetpack App Startup users or those who would like to initialize SDK manually 35 | 36 | DeployGate SDK uses `ContentProvider` to initialize itself so you need to remove the provider from your manifest file. 37 | 38 | ```AndroidManifest.xml 39 | 40 | 45 | 46 | ``` 47 | 48 | And also, you need to call `DeployGate#install` in your Application class, ContentProvider or AndroidX Startup Initializer. 49 | For example, add to your custom application class, content provider, or else. 50 | 51 | ```java 52 | import com.deploygate.sdk.DeployGate; 53 | import com.deploygate.sdk.DeployGateSdkConfiguration; 54 | 55 | DeployGateSdkConfiguration config = 56 | new DeployGateSdkConfiguration.Builder(). 57 | setEnabledOnNonDebuggableBuild(true). // Make sure you set true here. 58 | build(); 59 | DeployGate.install(context, config); 60 | ``` 61 | 62 | ## Usage 63 | 64 | You can retrieve detailed information on current running build and status change events through functions and callback listeners. 65 | 66 | For example, you can prevent your application running on unauthorized devices by putting the following code in your main Activity's `Activity#onCreate`. 67 | 68 | ```java 69 | DeployGate.registerCallback(new DeployGateCallback() { 70 | @Override 71 | public void onInitialized(boolean isServiceAvailable) { 72 | if (!isServiceAvailable) { 73 | Toast.makeText(this, "DeployGate is not available", Toast.LENGTH_SHORT).show(); 74 | finish(); 75 | } 76 | } 77 | 78 | @Override 79 | public void onStatusChanged(boolean isManaged, boolean isAuthorized, String loginUsername, boolean isStopped) { 80 | if (!isAuthorized) { 81 | Toast.makeText(this, "This device is not authorized to use this app", Toast.LENGTH_SHORT).show(); 82 | finish(); 83 | } 84 | } 85 | 86 | @Override 87 | public void onUpdateAvailable(int revision, String versionName, int versionCode) {} 88 | }, true); 89 | ``` 90 | 91 | For the more details, pelase refer to [DeployGate Docs - Android SDK](https://docs.deploygate.com/docs/developer-tools/android-sdk/) 92 | 93 | ### Capture feature 94 | 95 | Workspaces, which have activated the Capture feature, can storee screenshots, device states and Logcat at DeployGate. The Capture feature is enabled by default. 96 | 97 | ```java 98 | import com.deploygate.sdk.DeployGate; 99 | import com.deploygate.sdk.DeployGateSdkConfiguration; 100 | 101 | DeployGateSdkConfiguration config = 102 | new DeployGateSdkConfiguration.Builder() 103 | .setCaptureEnabled(true) // Defaults to true 104 | .build(); 105 | DeployGate.install(context, config); 106 | ``` 107 | 108 | Please refer to https://docs.deploygate.com/docs/developer-guide/capture/about-capture for the details. 109 | 110 | #### Custom Attributes 111 | 112 | You can add attributes to Captures. 113 | 114 | ```java 115 | import com.deploygate.sdk.DeployGate; 116 | 117 | CustomAttributes buildEnvironment = DeployGate.getBuildEnvironment(); 118 | buildEnvironment.putString("git_ref", BuildConfig.BUILD_GIT_REF); 119 | 120 | CustomAttributes runtimeExtra = DeployGate.getRuntimeExtra(); 121 | runtimeExtra.putString("is_authorized", authorizationRepository.isAuthorized()); 122 | ``` 123 | 124 | **Limitation** 125 | 126 | - `buildEnvironment` and `runtimeExtra` allow only at most **8** attributes. 127 | - Attribute keys must consist of `_a-z0-9` and must start with `a-z`. 128 | - Every value must be equal or shorter than 64 length. 129 | 130 | ## Mock 131 | 132 | Do you want to disable DeployGate SDK on production builds? If so, please use `sdk-mock` dependency for production builds instead of `sdk`. `sdk-mock` dependency has public interfaces that are same as of `sdk` but their implementations are empty, so you don't have to modify your app code for specific build variants. 133 | 134 | To use it, simply replace the dependency from `sdk` to `sdk-mock`. 135 | You can use it with a conjunction of `productFlavors` and `buildConfig` of Gradle 136 | like the following example: 137 | 138 | ```gradle 139 | dependencies { 140 | // use full implementation for debug builds 141 | debugImplementation 'com.deploygate:sdk:' 142 | 143 | // use mocked implementation for release builds 144 | releaseImplementation 'com.deploygate:sdk-mock:' 145 | } 146 | ``` 147 | 148 | > Proper variants depends on your use-case. If you are using R8-applied applications during the QA process, you may need to use non-mock SDK even in release build type. 149 | 150 | ## Upload your app to DeployGate 151 | 152 | [Gradle DeployGate Plugin](https://github.com/DeployGate/gradle-deploygate-plugin/) will be your help. Please note that the SDK works without the Gradle plugin and vice versa. 153 | 154 | ## Links 155 | 156 | * [SDK Document](https://deploygate.com/docs/sdk) 157 | * [Reference (Javadoc)](https://deploygate.github.io/deploygate-android-sdk/) 158 | * Javadoc hosting is currently in beta. 159 | * [Previous releases, download JAR/AARs](https://search.maven.org/artifact/com.deploygate/sdk) 160 | * [Issues](https://github.com/deploygate/deploygate-android-sdk/issues) 161 | 162 | ## License 163 | 164 | Copyright © 2017- DeployGate 165 | 166 | Licensed under [the Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 167 | http://www.apache.org/licenses/LICENSE-2.0 168 | 169 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 170 | 171 | ## Development 172 | 173 | - Clone this repo and open by Android Studio. 174 | - See the `/build.gradle` for the version of AGP. 175 | - Import `/CodeStyle.xml` 176 | - Modify java or required files 177 | - Add new tests for your changes 178 | - Make sure all tests are passed 179 | 180 | ```bash 181 | # help: build and install artifacts into your local maven repo 182 | ./gradlew clean \ 183 | sdk:verifyBytecodeVersionRelease sdkMock:verifyBytecodeVersionRelease \ 184 | sdk:publishReleasePublicationToMavenLocal sdkMock:publishReleasePublicationToMavenLocal \ 185 | --stacktrace 186 | ``` 187 | 188 | ### sdk 189 | 190 | - Consider if we should use external libraries carefully 191 | - Allow users to opt out non-required features 192 | - Minimize the impact of proguard rules 193 | 194 | ### sdkMock 195 | 196 | sdkMock must be No-Op implementation. 197 | 198 | - Do not have any permission 199 | - In the same version, the public API of sdkMock and sdk must have no difference 200 | 201 | ## Deployment 202 | 203 | Use GitHub Actions. 204 | 205 | - [release a new version](.github/workflows/release.yml) 206 | - [distribute a new sample with the latest version](.github/workflows/test.yml) 207 | - [deploy the Javadocs](.github/workflows/deploy-javadoc.yml) 208 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | plugins { 4 | id("com.deploygate.plugins.sdk") apply false 5 | id("com.deploygate.plugins.sample-app") apply false 6 | } 7 | 8 | task clean(type: Delete) { 9 | delete rootProject.buildDir 10 | } 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.disableAutomaticComponentCreation=true 3 | org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=2g 4 | kotlin.daemon.jvm.options=-Xmx1536m -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeployGate/deploygate-android-sdk/8ad73db5d118b5ed301b86b702d9c8de4063c09f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 3 | before_install: 4 | - sdk install java 17.0.8-tem 5 | - sdk use java 17.0.8-tem 6 | -------------------------------------------------------------------------------- /plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` apply false 3 | } 4 | 5 | tasks.register("clean", Delete::class) { 6 | delete(rootProject.buildDir) 7 | } 8 | -------------------------------------------------------------------------------- /plugins/library/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22") 7 | implementation("com.android.tools.build:gradle:7.4.2") 8 | implementation("com.squareup.moshi:moshi-kotlin:1.15.0") 9 | } 10 | 11 | gradlePlugin { 12 | plugins { 13 | create("sdk") { 14 | id = "com.deploygate.plugins.sdk" 15 | implementationClass = "com.deploygate.plugins.SdkPlugin" 16 | } 17 | create("sdk-mock") { 18 | id = "com.deploygate.plugins.sdk-mock" 19 | implementationClass = "com.deploygate.plugins.SdkMockPlugin" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/BaseSdkPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins 2 | 3 | import com.android.build.api.dsl.LibraryDefaultConfig 4 | import com.android.build.api.dsl.LibraryExtension 5 | import com.deploygate.plugins.dsl.SdkExtension 6 | import com.deploygate.plugins.ext.libraryExtension 7 | import org.gradle.api.GradleException 8 | import org.gradle.api.JavaVersion 9 | import org.gradle.api.Plugin 10 | import org.gradle.api.Project 11 | import org.gradle.kotlin.dsl.apply 12 | import org.gradle.kotlin.dsl.dependencies 13 | 14 | abstract class BaseSdkPlugin : Plugin { 15 | companion object { 16 | /** 17 | * sdk/java/com/deploygate/sdk/HostAppTest.java needs to be changed for a new release 18 | */ 19 | const val ARTIFACT_VERSION = "4.10.0" 20 | 21 | val JAVA_VERSION = JavaVersion.VERSION_1_7 22 | } 23 | 24 | protected val artifactVersion: String 25 | get() = ARTIFACT_VERSION 26 | 27 | override fun apply(target: Project) { 28 | target.version = ARTIFACT_VERSION 29 | 30 | target.extensions.add("sdk", createSdkExtension()) 31 | 32 | target.apply(plugin = "com.android.library") 33 | target.apply() 34 | 35 | target.libraryExtension.configureLibraryExtension() 36 | 37 | target.tasks.register("verifyReleaseVersion") { 38 | doLast { 39 | if (target.version != System.getenv("RELEASE_VERSION")) { 40 | throw GradleException("${target.version} does not equal to ${System.getenv("RELEASE_VERSION")}") 41 | } 42 | } 43 | } 44 | 45 | target.dependencies { 46 | add("testImplementation", "androidx.test:runner:1.5.2") 47 | add("testImplementation", "androidx.test.ext:junit:1.1.5") 48 | add("testImplementation", "org.robolectric:robolectric:4.10.3") 49 | add("testImplementation", "androidx.test:rules:1.5.0") 50 | add("testImplementation", "com.google.truth:truth:1.0") 51 | } 52 | } 53 | 54 | private fun LibraryExtension.configureLibraryExtension() { 55 | compileSdk = 33 56 | 57 | defaultConfig { 58 | minSdk = 14 59 | // TODO remove this property and set the default sdk version for robolectric test instead 60 | @Suppress("DEPRECATION") 61 | targetSdk = 33 62 | 63 | configureLibraryDefaultConfig() 64 | } 65 | 66 | buildTypes { 67 | release { 68 | isMinifyEnabled = false 69 | } 70 | } 71 | 72 | compileOptions { 73 | sourceCompatibility(JAVA_VERSION) 74 | targetCompatibility(JAVA_VERSION) 75 | } 76 | 77 | testOptions { 78 | unitTests { 79 | isIncludeAndroidResources = true 80 | 81 | all { 82 | it.jvmArgs( 83 | "-Xmx1g", 84 | ) 85 | } 86 | } 87 | } 88 | 89 | buildFeatures { 90 | buildConfig = true 91 | } 92 | 93 | lint { 94 | abortOnError = false 95 | } 96 | 97 | publishing { 98 | singleVariant("release") { 99 | withJavadocJar() 100 | withSourcesJar() 101 | } 102 | } 103 | } 104 | 105 | abstract fun createSdkExtension(): SdkExtension 106 | abstract fun LibraryDefaultConfig.configureLibraryDefaultConfig() 107 | } -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/MavenPublishPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins 2 | 3 | import com.deploygate.plugins.ext.internalApiLibraryExtension 4 | import com.deploygate.plugins.ext.publishingExtension 5 | import com.deploygate.plugins.ext.sdkExtension 6 | import com.deploygate.plugins.ext.signingExtension 7 | import org.gradle.api.Plugin 8 | import org.gradle.api.Project 9 | import org.gradle.api.artifacts.repositories.PasswordCredentials 10 | import org.gradle.api.publish.PublicationContainer 11 | import org.gradle.api.publish.PublishingExtension 12 | import org.gradle.api.publish.maven.MavenPublication 13 | import org.gradle.api.tasks.Exec 14 | import org.gradle.kotlin.dsl.apply 15 | import org.gradle.kotlin.dsl.create 16 | import org.gradle.kotlin.dsl.getByType 17 | import org.gradle.kotlin.dsl.named 18 | import org.gradle.kotlin.dsl.register 19 | import org.gradle.plugins.signing.SigningExtension 20 | import java.util.Locale 21 | 22 | class MavenPublishPlugin : Plugin { 23 | companion object { 24 | private val RELEASE_VERSION_REGEX = "^\\d+\\.\\d+\\.\\d+\$".toRegex() 25 | } 26 | 27 | override fun apply(target: Project) { 28 | target.apply(plugin = "maven-publish") 29 | target.apply(plugin = "signing") 30 | 31 | val artifactVersion = target.version as String 32 | 33 | val repositoryUsername = target.findProperty("NEXUS_USERNAME") as? String 34 | val repositoryPassword = target.findProperty("NEXUS_PASSWORD") as? String 35 | val signingKey = target.findProperty("signingKey") as? String 36 | val signingPassword = target.findProperty("signingPassword") as? String 37 | 38 | val isRelease = artifactVersion.matches(RELEASE_VERSION_REGEX) 39 | 40 | target.internalApiLibraryExtension.libraryVariants.configureEach { 41 | val variant = this 42 | 43 | target.tasks.register("verifyBytecodeVersion${variant.name.capitalize(Locale.ROOT)}") 44 | .configure { 45 | dependsOn(variant.assembleProvider) 46 | 47 | commandLine( 48 | target.rootProject.file("scripts/verify-bytecode-version"), 49 | "--aar", 50 | variant.packageLibraryProvider.flatMap { it.archiveFile }.get(), 51 | "--java", 52 | BaseSdkPlugin.JAVA_VERSION.toString() 53 | ) 54 | } 55 | } 56 | 57 | target.publishingExtension.configurePublishingExtension( 58 | target = target, 59 | artifactVersion = artifactVersion, 60 | isRelease = isRelease, 61 | repositoryUsername = repositoryUsername, 62 | repositoryPassword = repositoryPassword, 63 | ) 64 | target.signingExtension.configureSigningExtension( 65 | isSigningRequired = isRelease, 66 | signingKey = signingKey, 67 | signingPassword = signingPassword, 68 | publications = target.extensions.getByType().publications, 69 | ) 70 | 71 | target.gradle.taskGraph.whenReady { 72 | with(target.signingExtension) { 73 | isRequired = isRequired && hasTask("publishReleasePublicationToMavenRepository") 74 | } 75 | } 76 | } 77 | 78 | private fun PublishingExtension.configurePublishingExtension( 79 | target: Project, 80 | artifactVersion: String, 81 | isRelease: Boolean, 82 | repositoryUsername: String?, 83 | repositoryPassword: String?, 84 | ) { 85 | repositories { 86 | maven { 87 | setUrl( 88 | if (isRelease) { 89 | "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 90 | } else { 91 | "https://oss.sonatype.org/content/repositories/snapshots/" 92 | } 93 | ) 94 | 95 | credentials(PasswordCredentials::class.java) { 96 | username = repositoryUsername 97 | password = repositoryPassword 98 | } 99 | } 100 | } 101 | 102 | 103 | publications { 104 | create("release") { 105 | // This block would be evaluated before components.getByName("release") was created 106 | 107 | groupId = "com.deploygate" 108 | version = artifactVersion 109 | 110 | pom { 111 | name.set(target.sdkExtension.displayName) 112 | description.set(target.sdkExtension.description) 113 | url.set("https://github.com/DeployGate/deploygate-android-sdk") 114 | 115 | licenses { 116 | license { 117 | name.set("The Apache Software License, Version 2.0") 118 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 119 | distribution.set("repo") 120 | } 121 | } 122 | 123 | developers { 124 | developer { 125 | id.set("deploygate") 126 | name.set("DeployGate") 127 | } 128 | } 129 | 130 | scm { 131 | url.set("https://github.com/DeployGate/deploygate-android-sdk") 132 | } 133 | } 134 | } 135 | } 136 | 137 | target.afterEvaluate { 138 | publications { 139 | // Configure some values after AGP has been configured 140 | named("release") { 141 | from(components.getByName("release")) 142 | artifactId = sdkExtension.artifactId 143 | } 144 | } 145 | } 146 | } 147 | 148 | 149 | private fun SigningExtension.configureSigningExtension( 150 | isSigningRequired: Boolean, 151 | signingKey: String?, 152 | signingPassword: String?, 153 | publications: PublicationContainer, 154 | ) { 155 | isRequired = isSigningRequired 156 | useInMemoryPgpKeys(signingKey, signingPassword) 157 | 158 | publications.configureEach { 159 | sign(this) 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/SdkMockPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins 2 | 3 | import com.android.build.api.dsl.LibraryDefaultConfig 4 | import com.deploygate.plugins.dsl.SdkExtension 5 | import com.deploygate.plugins.internal.SdkExtensionImpl 6 | 7 | class SdkMockPlugin : BaseSdkPlugin() { 8 | override fun createSdkExtension(): SdkExtension { 9 | return SdkExtensionImpl( 10 | displayName = "DeployGate SDK Mock", 11 | artifactId = "sdk-mock", 12 | description = "Mocked dummy DeployGate SDK for Android to reduce footprint of your release version app without code modification", 13 | ) 14 | } 15 | 16 | override fun LibraryDefaultConfig.configureLibraryDefaultConfig() { 17 | // Do not add meta-data and so on for sdk-mock 18 | } 19 | } -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/SdkPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins 2 | 3 | import com.android.build.api.dsl.LibraryDefaultConfig 4 | import com.deploygate.plugins.dsl.SdkExtension 5 | import com.deploygate.plugins.internal.SdkExtensionImpl 6 | import com.deploygate.plugins.tasks.GenerateMetaDataJsonTask 7 | import org.gradle.api.Project 8 | import org.gradle.kotlin.dsl.register 9 | import java.io.File 10 | 11 | class SdkPlugin : BaseSdkPlugin() { 12 | companion object { 13 | // A map of isSupporting> 14 | // The order of elements are important! 15 | private val SUPPORTED_FEATURES = arrayOf( 16 | "UPDATE_MESSAGE_OF_BUILD" to true, 17 | "SERIALIZED_EXCEPTION" to true, 18 | "LOGCAT_BUNDLE" to true, 19 | "STREAMED_LOGCAT" to true, 20 | "DEVICE_CAPTURE" to true, 21 | ) 22 | 23 | private val FEATURE_FLAGS: LinkedHashMap = 24 | SUPPORTED_FEATURES.mapIndexed { idx, (feature, _) -> 25 | feature to 1.shl(idx) 26 | }.toMap(LinkedHashMap()) 27 | 28 | private val ACTIVE_FEATURE_FLAGS = 29 | SUPPORTED_FEATURES.fold(0) { acc, (feature, isSupporting) -> 30 | if (isSupporting) { 31 | acc or FEATURE_FLAGS.getOrElse(feature) { error("$feature is not found in FEATURE_FLAGS") } 32 | } else { 33 | acc 34 | } 35 | } 36 | 37 | private const val KEY_PREFIX = "com.deploygate.sdk." 38 | 39 | private val META_DATA_ENTRIES = 40 | mapOf( 41 | "deploygate_sdk_feature_flags_name" to "${KEY_PREFIX}feature_flags", 42 | "deploygate_sdk_feature_flags_value" to "$ACTIVE_FEATURE_FLAGS", 43 | 44 | "deploygate_sdk_version_name" to "${KEY_PREFIX}version", 45 | "deploygate_sdk_version_value" to "4", 46 | 47 | "deploygate_sdk_artifact_version_name" to "${KEY_PREFIX}artifact_version", 48 | "deploygate_sdk_artifact_version_value" to ARTIFACT_VERSION, 49 | ) 50 | } 51 | 52 | override fun apply(target: Project) { 53 | super.apply(target) 54 | 55 | target.tasks.register("generateMetaDataJson") { 56 | getArtifactVersion().set(ARTIFACT_VERSION) 57 | getFeatureFlags().set(FEATURE_FLAGS) 58 | getActiveFeatureFlags().set(ACTIVE_FEATURE_FLAGS) 59 | getMetaData().set(META_DATA_ENTRIES) 60 | outputFile = 61 | File(target.buildDir, "generated-sdk-metadata/sdk-meta-data-$ARTIFACT_VERSION.json") 62 | } 63 | } 64 | 65 | override fun createSdkExtension(): SdkExtension { 66 | return SdkExtensionImpl( 67 | displayName = "DeployGate SDK", 68 | artifactId = "sdk", 69 | description = "DeployGate SDK for Android", 70 | ) 71 | } 72 | 73 | override fun LibraryDefaultConfig.configureLibraryDefaultConfig() { 74 | FEATURE_FLAGS.forEach { (feature, flag) -> 75 | buildConfigField("int", feature, "$flag") 76 | } 77 | 78 | addManifestPlaceholders(META_DATA_ENTRIES) 79 | } 80 | } -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/dsl/SdkExtension.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins.dsl 2 | 3 | interface SdkExtension { 4 | val artifactId: String 5 | val displayName: String 6 | val description: String 7 | } -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/ext/ProjectExt.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins.ext 2 | 3 | import com.android.build.api.dsl.LibraryExtension 4 | import com.deploygate.plugins.dsl.SdkExtension 5 | import org.gradle.api.Project 6 | import org.gradle.api.publish.PublishingExtension 7 | import org.gradle.kotlin.dsl.getByType 8 | import org.gradle.plugins.signing.SigningExtension 9 | 10 | val Project.sdkExtension: SdkExtension 11 | get() = extensions.getByType() 12 | 13 | val Project.libraryExtension: LibraryExtension 14 | get() = extensions.getByType() 15 | 16 | val Project.signingExtension: SigningExtension 17 | get() = extensions.getByType() 18 | 19 | val Project.publishingExtension: PublishingExtension 20 | get() = extensions.getByType() 21 | 22 | val Project.internalApiLibraryExtension: com.android.build.gradle.LibraryExtension 23 | get() = extensions.getByType() -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/internal/SdkExtensionImpl.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins.internal 2 | 3 | import com.deploygate.plugins.dsl.SdkExtension 4 | 5 | data class SdkExtensionImpl( 6 | override val artifactId: String, 7 | override val displayName: String, 8 | override val description: String, 9 | ) : SdkExtension -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/internal/SdkMetaData.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins.internal 2 | 3 | import com.squareup.moshi.Json 4 | 5 | class SdkMetaData( 6 | @Json(name = "artifact_version") 7 | var artifactVersion: String, 8 | @Json(name = "feature_flags") 9 | var featureFlags: Map, 10 | @Json(name = "active_feature_flags") 11 | var activeFeatureFlags: Int, 12 | @Json(name = "meta_data") 13 | var metaData: Map, 14 | ) { 15 | fun validate() { 16 | require(artifactVersion.isNotBlank()) 17 | require(featureFlags.isNotEmpty()) 18 | require(activeFeatureFlags > 0) 19 | require(metaData.isNotEmpty()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /plugins/library/src/main/java/com/deploygate/plugins/tasks/GenerateMetaDataJsonTask.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins.tasks 2 | 3 | import com.deploygate.plugins.internal.SdkMetaData 4 | import com.squareup.moshi.JsonAdapter 5 | import com.squareup.moshi.Moshi 6 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 7 | import org.gradle.api.provider.MapProperty 8 | import org.gradle.api.provider.Property 9 | import org.gradle.api.tasks.Input 10 | import org.gradle.plugins.ide.api.GeneratorTask 11 | import org.gradle.plugins.ide.internal.generator.generator.Generator 12 | import java.io.File 13 | 14 | abstract class GenerateMetaDataJsonTask : GeneratorTask() { 15 | @Input 16 | abstract fun getArtifactVersion(): Property 17 | 18 | @Input 19 | abstract fun getFeatureFlags(): MapProperty 20 | 21 | @Input 22 | abstract fun getActiveFeatureFlags(): Property 23 | 24 | @Input 25 | abstract fun getMetaData(): MapProperty 26 | 27 | init { 28 | generator = object : Generator { 29 | override fun read(inputFile: File): SdkMetaData { 30 | return sdkMetaDataAdapter.fromJson(inputFile.readText())!! 31 | } 32 | 33 | override fun defaultInstance(): SdkMetaData { 34 | return SdkMetaData( 35 | artifactVersion = "", 36 | featureFlags = emptyMap(), 37 | metaData = emptyMap(), 38 | activeFeatureFlags = 0, 39 | ) 40 | } 41 | 42 | override fun write(`object`: SdkMetaData, outputFile: File) { 43 | outputFile.writeText( 44 | text = sdkMetaDataAdapter.toJson(`object`), 45 | charset = Charsets.UTF_8, 46 | ) 47 | } 48 | 49 | override fun configure(`object`: SdkMetaData) { 50 | `object`.apply { 51 | this.artifactVersion = getArtifactVersion().get() 52 | this.featureFlags = getFeatureFlags().get() 53 | this.activeFeatureFlags = getActiveFeatureFlags().get() 54 | this.metaData = getMetaData().get() 55 | } 56 | 57 | `object`.validate() 58 | } 59 | } 60 | } 61 | 62 | companion object { 63 | private val sdkMetaDataAdapter: JsonAdapter = Moshi.Builder() 64 | .addLast(KotlinJsonAdapterFactory()) 65 | .build() 66 | .adapter(SdkMetaData::class.java) 67 | .failOnUnknown() 68 | .indent(" ") 69 | } 70 | } -------------------------------------------------------------------------------- /plugins/sample/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22") 7 | implementation("com.android.tools.build:gradle:7.4.2") 8 | implementation("com.project.starter:easylauncher:5.0.1") 9 | } 10 | 11 | gradlePlugin { 12 | plugins { 13 | create("sample") { 14 | id = "com.deploygate.plugins.sample-app" 15 | implementationClass = "com.deploygate.plugins.SampleAppPlugin" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /plugins/sample/src/main/java/com/deploygate/plugins/SampleAppPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.deploygate.plugins 2 | 3 | import com.android.build.gradle.AppExtension 4 | import com.project.starter.easylauncher.filter.ColorRibbonFilter 5 | import com.project.starter.easylauncher.plugin.EasyLauncherExtension 6 | import org.gradle.api.JavaVersion 7 | import org.gradle.api.Plugin 8 | import org.gradle.api.Project 9 | import org.gradle.kotlin.dsl.apply 10 | import org.gradle.kotlin.dsl.invoke 11 | 12 | class SampleAppPlugin : Plugin { 13 | override fun apply(target: Project) { 14 | target.apply(plugin = "com.android.application") 15 | target.apply(plugin = "com.starter.easylauncher") 16 | 17 | target.extensions.findByType(AppExtension::class.java)?.configureAppExtension() 18 | target.extensions.findByType(EasyLauncherExtension::class.java) 19 | ?.configureEasyLauncherExtension() 20 | } 21 | 22 | private fun AppExtension.configureAppExtension() { 23 | compileSdkVersion(33) 24 | 25 | defaultConfig { 26 | applicationId = "com.deploygate.sample" 27 | minSdk = 14 28 | targetSdk = 33 29 | } 30 | 31 | buildTypes { 32 | named("debug") { 33 | applicationIdSuffix = ".debug" 34 | } 35 | named("release") { 36 | minifyEnabled(true) 37 | proguardFiles( 38 | getDefaultProguardFile("proguard-android-optimize.txt"), 39 | "proguard-project.txt", 40 | ) 41 | } 42 | create("distribute") { 43 | debuggable(true) 44 | matchingFallbacks += "release" 45 | } 46 | } 47 | 48 | flavorDimensions("mode") 49 | 50 | productFlavors { 51 | create("devreal") { 52 | dimension("mode") 53 | applicationIdSuffix(".realsdk.dev") 54 | } 55 | create("devmock") { 56 | dimension("mode") 57 | applicationIdSuffix(".mocksdk.dev") 58 | } 59 | create("stablereal") { 60 | dimension("mode") 61 | applicationIdSuffix(".realsdk.stable") 62 | } 63 | create("stablemock") { 64 | dimension("mode") 65 | applicationIdSuffix(".mocksdk.stable") 66 | } 67 | } 68 | 69 | compileOptions { 70 | sourceCompatibility(JavaVersion.VERSION_1_8) 71 | targetCompatibility(JavaVersion.VERSION_1_8) 72 | } 73 | 74 | lintOptions { 75 | isAbortOnError = false 76 | } 77 | } 78 | 79 | private fun EasyLauncherExtension.configureEasyLauncherExtension() { 80 | defaultFlavorNaming(true) 81 | 82 | productFlavors { 83 | create("devreal") { 84 | filters( 85 | customRibbon( 86 | label = "devreal", 87 | ribbonColor = "#55E74C3C", 88 | labelColor = "#FFFFFF", 89 | gravity = ColorRibbonFilter.Gravity.TOPRIGHT, 90 | ), 91 | ) 92 | } 93 | create("devmock") { 94 | filters( 95 | customRibbon( 96 | label = "devmock", 97 | ribbonColor = "#556600CC", 98 | labelColor = "#FFFFFF", 99 | gravity = ColorRibbonFilter.Gravity.TOPRIGHT, 100 | ), 101 | ) 102 | } 103 | create("stablereal") { 104 | enable(false) 105 | } 106 | create("stablemock") { 107 | enable(false) 108 | } 109 | } 110 | 111 | buildTypes { 112 | create("debug") { 113 | filters( 114 | customRibbon( 115 | label = "debug", 116 | ribbonColor = "#5574924", 117 | labelColor = "#FFFFFF", 118 | gravity = ColorRibbonFilter.Gravity.BOTTOM, 119 | ), 120 | ) 121 | } 122 | create("distribute") { 123 | enable(false) 124 | } 125 | create("release") { 126 | enable(false) 127 | } 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /plugins/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | gradlePluginPortal() 15 | } 16 | } 17 | 18 | include(":library") 19 | include(":sample") 20 | -------------------------------------------------------------------------------- /sample/README.md: -------------------------------------------------------------------------------- 1 | You can try this sample app via the following link. 2 | 3 | [Download to device](https://dply.me/o9wh0x#install) 4 | 5 | **We recommend you to uninstall our sample app (not DeployGate app!) after checking the behavior** 6 | 7 | We know most of users don't need the updates of our sample app! Please note that later updates will be notified to your device until uninstalling our sample app. 8 | 9 | > Sample apps being distributed through DeployGate's Distribution page feature. If you are interested in the Distribution page feature, please refer to https://intercom.help/deploygate/en/articles/4536501-what-is-distribution-page 10 | 11 | ## Doesn't sample app work? 12 | 13 | SDK works only when you have installed it via DeployGate client app. 14 | 15 | ref: https://play.google.com/store/apps/details?id=com.deploygate 16 | 17 | If you still have any trouble, please file an issue in this repository. 18 | 19 | ## Mock SDK behaviour 20 | 21 | Every operation does nothing. The following distribution link is available. 22 | 23 | [Download to device](https://dply.me/z8u657#install) 24 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.deploygate.plugins.sample-app' 2 | 3 | dependencies { 4 | devrealImplementation project(':sdk') 5 | devmockImplementation project(':sdkMock') 6 | 7 | def version = "4.10.0" 8 | stablerealImplementation "com.deploygate:sdk:$version" 9 | stablemockImplementation "com.deploygate:sdk-mock:$version" 10 | } -------------------------------------------------------------------------------- /sample/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -keep class * extends android.os.IInterface 22 | 23 | # to check the R8 results except obfuscation 24 | -keepnames class com.deploygate.sdk.** { 25 | *; 26 | } -------------------------------------------------------------------------------- /sample/src/devmock/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SDK (Mock/dev) サンプル 4 | これはDeployGate SDK (Mock/dev)のサンプルです。全てのコマンドで何も起きません。 5 | 6 | -------------------------------------------------------------------------------- /sample/src/devmock/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SDK (Mock/dev) Sample 3 | This is a sample app of DeployGate SDK (mock/dev). Every operation does nothing. 4 | -------------------------------------------------------------------------------- /sample/src/devreal/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /sample/src/devreal/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SDK (dev) サンプル 4 | これはDeployGate SDK(dev) のサンプルです。DeployGate からインストールした場合、コマンド操作が有効です。 5 | 6 | -------------------------------------------------------------------------------- /sample/src/devreal/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SDK (dev) Sample 3 | This is a sample app of DeployGate SDK(dev) that is initialized manually. Every operation should work if this has been installed by DeployGate client app. 4 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 33 | 39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /sample/src/main/java/com/deploygate/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.deploygate.sample; 2 | 3 | import android.app.Application; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import com.deploygate.sdk.CustomAttributes; 9 | import com.deploygate.sdk.DeployGate; 10 | import com.deploygate.sdk.DeployGateSdkConfiguration; 11 | 12 | import java.util.Locale; 13 | 14 | /** 15 | * THIS CLASS IS NOT USED BY DEFAULT. To use this implementation, you have to add on AndroidManifest.xml manually. 16 | */ 17 | public class App extends Application { 18 | public static final String TAG = "DeployGateSDKSample"; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | 24 | // In most cases you don't have to do this, but if you have multiple processes in your application, 25 | // or you want to customize initialize options, you can install DeployGate manually. 26 | // 27 | // Note that you also need to edit your AndroidManifest.xml to activate customized initializer. 28 | // Refer the comment on stableReal/AndroidManifest.xml included in this sample. 29 | 30 | DeployGateSdkConfiguration configuration = new DeployGateSdkConfiguration.Builder() 31 | // Please note that this callback is called iff you have removed the content provider. 32 | // For those who wanna use the content provider, SDK provides DeployGate#registerXXXCallback for your use-case. 33 | .setInitializeCallback(isServiceAvailable -> { 34 | if (isServiceAvailable) { 35 | Log.i(TAG, "SDK is available"); 36 | DeployGate.logInfo("SDK is available"); 37 | 38 | CustomAttributes attrs = DeployGate.getBuildEnvironment(); 39 | attrs.putString("build_type", BuildConfig.BUILD_TYPE); 40 | attrs.putString("flavor", BuildConfig.FLAVOR); 41 | } else { 42 | Log.i(TAG, "SDK is unavailable"); 43 | DeployGate.logInfo("SDK is unavailable"); // this fails silently 44 | } 45 | }) 46 | .setStatusChangeCallback((isManaged, isAuthorized, loginUsername, isStopped) -> { 47 | Bundle bundle = new Bundle(); 48 | bundle.putBoolean("isManaged", isManaged); 49 | bundle.putBoolean("isAuthorized", isAuthorized); 50 | bundle.putString("loginUsername", loginUsername); 51 | bundle.putBoolean("isStopped", isStopped); 52 | 53 | String message = String.format(Locale.US, "onStatusChanged(%s)", bundle); 54 | 55 | Log.i(TAG, message); 56 | DeployGate.logInfo(message); 57 | }) 58 | .setUpdateAvailableCallback((revision, versionName, versionCode) -> { 59 | Bundle bundle = new Bundle(); 60 | bundle.putInt("revision", revision); 61 | bundle.putString("versionName", versionName); 62 | bundle.putInt("versionCode", versionCode); 63 | 64 | String message = String.format(Locale.US, "onUpdateAvailable(%s)", bundle); 65 | 66 | Log.i(TAG, message); 67 | DeployGate.logInfo(message); 68 | }) 69 | .setCaptureCreateCallback((captureUrl, createdAtMillis) -> { 70 | String message = String.format(Locale.US, "onCaptureCreated(%s)", captureUrl); 71 | Log.i(TAG, message); 72 | DeployGate.logInfo(message); 73 | }) 74 | .setEnabledOnNonDebuggableBuild(true) 75 | .build(); 76 | 77 | DeployGate.install(this, configuration); 78 | 79 | // If you want to prevent the app distributed by someone else, specify your username on DeployGate 80 | // with setAuthor method when creating DeployGate SdkConfiguration. like: 81 | // 82 | // builder.setAuthor("YOURUSERNAME"); 83 | // 84 | // You can use DeployGate.isAuthorized() later to check the installation is valid or not. 85 | } 86 | 87 | @Override 88 | public void onConfigurationChanged(Configuration newConfig) { 89 | super.onConfigurationChanged(newConfig); 90 | 91 | CustomAttributes attrs = DeployGate.getRuntimeExtra(); 92 | attrs.putString("locale", newConfig.locale.toString()); 93 | attrs.putInt("orientation", newConfig.orientation); 94 | attrs.putFloat("font_scale", newConfig.fontScale); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /sample/src/main/java/com/deploygate/sample/BootBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.deploygate.sample; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | import com.deploygate.sdk.DeployGate; 9 | 10 | public class BootBroadcastReceiver extends BroadcastReceiver { 11 | 12 | @Override 13 | public void onReceive( 14 | Context context, 15 | Intent intent 16 | ) { 17 | Log.i(App.TAG, "Booted"); 18 | DeployGate.logInfo("Booted"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/src/main/java/com/deploygate/sample/DirectBootBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.deploygate.sample; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | import com.deploygate.sdk.DeployGate; 9 | 10 | public class DirectBootBroadcastReceiver extends BroadcastReceiver { 11 | 12 | @Override 13 | public void onReceive( 14 | Context context, 15 | Intent intent 16 | ) { 17 | Log.i(App.TAG, "Direct-booted"); 18 | DeployGate.logInfo("Direct-booted"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeployGate/deploygate-android-sdk/8ad73db5d118b5ed301b86b702d9c8de4063c09f/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeployGate/deploygate-android-sdk/8ad73db5d118b5ed301b86b702d9c8de4063c09f/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeployGate/deploygate-android-sdk/8ad73db5d118b5ed301b86b702d9c8de4063c09f/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/logo_w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeployGate/deploygate-android-sdk/8ad73db5d118b5ed301b86b702d9c8de4063c09f/sample/src/main/res/drawable-xhdpi/logo_w.png -------------------------------------------------------------------------------- /sample/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | サンプル 4 | 設定 5 | 6 | これはDeployGate SDKのサンプルです。 7 | DeployGateロゴ 8 | 9 | DeployGateはこの端末上に 10 | versionCode %1$dがインストールされています 11 | インストールされてません 12 | このアプリはDeployGateで 13 | リビジョン#%1$dとして管理されています 14 | リビジョン#%1$dが<a href="%3$s">%2$s</a>から配布されています 15 | 管理されていません 16 | このアプリの利用 17 | %1$s が利用しています 18 | ログインユーザーが不明のため利用できません 19 | 20 | アプリをクラッシュさせる 21 | カスタムログの送信 22 | これはテストです 23 | LogCatを取得して送信 24 | LogCatを取得して送信しています。deploygate.comの端末の詳細ページのLogCatタブをご確認ください。 25 | アップデート 26 | アップデートがあります: #%1$d, %2$s(%3$d) %4$s 27 | コメント一覧 28 | 投稿する 29 | 30 | -------------------------------------------------------------------------------- /sample/src/main/res/values-large/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |