LoginSignup
6
3

More than 5 years have passed since last update.

Androidのアプリのフィードバックをslackに投稿する

Last updated at Posted at 2017-01-06

Androidアプリのフィードバック

最近グッドパッチさんから良い感じのサービスが出ているのを発見した。

Balto

アプリのフィードバックを素早く投稿して、エンジニアに共有することができる。また投稿はgithubに投稿されissue化されるのでかなり便利。でもAndroidは5.0以上らしい・・・(Baltoを少しだけ触ってみた

おしい。4系はまだシェア的に10%ほどいて、切れないのでとりあえずSlackにpostするのを作った。
Activityのキャプチャをとって、postするやつだ。

キャプチャしてSlackに投稿する

まずキャプチャする。


fun AppCompatActivity.captureScreen():Bitmap{
    val rootView = window.decorView.rootView
    val screenView = rootView.getRootView()
    screenView.setDrawingCacheEnabled(true)
    val bitmap = Bitmap.createBitmap(screenView.getDrawingCache())
    screenView.setDrawingCacheEnabled(false)
    return bitmap
}

ActivityのExtensionです。

Fragment


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@android:color/white"
    android:orientation="vertical"
    android:clickable="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
            <LinearLayout
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:textSize="17dp"
                android:text="*どこからでも送信できます!!"/>
            <RadioGroup
                android:orientation="horizontal"
                android:id="@+id/buttongroup_feedback"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <RadioButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="バグ"
                    android:id="@+id/radioButton" />

                <RadioButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="改善"
                    android:layout_gravity="right" />

                <RadioButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="イイね!"
                    android:layout_gravity="center_horizontal" />
                <Button
                    android:id="@+id/button_feedback"
                    android:layout_marginLeft="50dp"
                    android:layout_gravity="right"
                    android:text="送信!!!"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />

            </RadioGroup>

            <EditText
                android:padding="10dp"
                android:hint="フィードバックを入力してください"
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:id="@+id/editText_feedback" />

            <ImageView
                android:id="@+id/imageView_feedback"
                android:layout_width="match_parent"
                android:layout_height="300dp" />
        </LinearLayout>

    </ScrollView>
</LinearLayout>
import android.graphics.Bitmap
import android.os.Bundle
import android.support.v4.app.Fragment
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.google.repacked.apache.commons.io.FileUtils
import jp.co.rarejob.R
import jp.co.rarejob.lib.util.AlertDialogUtil
import okhttp3.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer

/**
 * Created by kentaro.haneda on 16/12/21.
 */
class FeedbackFragment(var bitmap:Bitmap) : Fragment() {

    lateinit var categoryButtonGroup:RadioGroup
    lateinit var feedbacEditText: EditText
    lateinit var captureImage:ImageView

    var selectedCategoryName = "その他"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onDestroy() {
        super.onDestroy()
    }

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val view = inflater!!.inflate(R.layout.fragment_feedback, container, false)
        captureImage = view.findViewById(R.id.imageView_feedback) as ImageView
        captureImage.setImageBitmap(bitmap)

        (view.findViewById(R.id.buttongroup_feedback) as RadioGroup).setOnCheckedChangeListener { radioGroup, i ->
            when(i){
                0 -> { selectedCategoryName = "バグ" }
                1 -> { selectedCategoryName = "改善" }
                2 -> { selectedCategoryName = "イイね" }
            }
        }
        (view.findViewById(R.id.button_feedback) as Button).setOnClickListener {
            postToSlack()
        }
        feedbacEditText = view.findViewById(R.id.editText_feedback) as EditText
        feedbacEditText.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable) {
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                (view.findViewById(R.id.button_feedback) as Button).isEnabled = feedbacEditText.text.toString().count() > 0
            }
        })

        return view
    }

    fun postToSlack(){

        val byteArrayOutputStream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
        val requestBodyBitmap = RequestBody.create(MediaType.parse("image/png"), byteArrayOutputStream.toByteArray())

        val service = SlackServiceGenerator.createService(SlackFeedbackAPI::class.java)

        // set post file name
        val body = MultipartBody.Part.createFormData("file", "feedback_image.png", requestBodyBitmap)

        // for slack
        // get from here https://XXXXXXXX.slack.com/apps/new/
        val token = RequestBody.create(
                MediaType.parse("multipart/form-data"), "XXXXXXXXXXXXX")
        val channel = RequestBody.create(
                MediaType.parse("multipart/form-data"), "feedback")
        val title = RequestBody.create(
                MediaType.parse("multipart/form-data"), selectedCategoryName)
        val comment = RequestBody.create(
                MediaType.parse("multipart/form-data"), feedbacEditText.text.toString())
        val pretty = RequestBody.create(
                MediaType.parse("multipart/form-data"), "1")

        // finally, execute the request
        val call = service.upload(token, channel, pretty, title, comment, body)
        call.enqueue(object : Callback<ResponseBody> {
            override fun onResponse(call: Call<ResponseBody>,
                                    response: Response<ResponseBody>) {
                Log.v("Upload", "success")
            }

            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
                Log.e("Upload error:", t.message)
            }
        })
    }
}

interface SlackFeedbackAPI{
    @Multipart
    @POST("/api/files.upload")
    fun upload(@Part("token") version:RequestBody,
               @Part("channels") name:RequestBody,
               @Part("pretty") category:RequestBody,
               @Part("title") title:RequestBody,
               @Part("initial_comment") comment:RequestBody,
               @Part file: MultipartBody.Part ): Call<ResponseBody>
}

object SlackServiceGenerator {

    val API_BASE_URL = "https://slack.com"

    private val httpClient = OkHttpClient.Builder()

    private val builder = Retrofit.Builder().baseUrl(API_BASE_URL).addConverterFactory(GsonConverterFactory.create())

    fun <S> createService(serviceClass: Class<S>): S {
        val retrofit = builder.client(httpClient.build()).build()
        return retrofit.create(serviceClass)
    }
}

retrofit2とokhttpを使っています。トークンは https://XXXXXXXX.slack.com/apps/new/ こんな感じのURLで発行されると思います。

Baltoが4k対応したら、iOSもまとめたい!

コードはこちらにも

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3