20211008のAndroidに関する記事は5件です。

Play App Signingで登録されている署名のフィンガープリントとSHA1ハッシュを取得する方法

Play App Signingで登録されている署名のフィンガープリントとSHA1ハッシュを取得する方法がわからなかったので調べてみました。 まずは、Play Store ConsoleのPlay App Signingのページでアプリ署名鍵の証明書をダウンロード。 フィンガープリントの取得のコマンドは以下の通り。 keytool -list -printcert -file [ダウンロードした証明書(xxx.der)] 次にSHA1の取得は以下のとおり。 keytool -list -printcert -file [ダウンロードした証明書(xxx.der)] | openssl sha1 -binary | openssl base64
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Play App Signingで登録されている署名のフィンガープリントを取得する方法

Play App Signingで登録されている署名のフィンガープリントを取得する方法がわからなかったので調べてみました。 まずは、Play Store ConsoleのPlay App Signingのページでアプリ署名鍵の証明書をダウンロード。 フィンガープリントの取得のコマンドは以下の通り。 keytool -list -printcert -file [ダウンロードした証明書(xxx.der)]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Play App Signingで登録されている署名のSHA1のハッシュ値を取得する方法

Play App Signingで登録されている署名のSHA1のハッシュ値を取得する方法がわからなかったので調べてみました。 まずは、Play Store ConsoleのPlay App Signingのページに記載されているSHA1のフィンガープリントをバイナリーエディタに貼り付けて、保存。ここではファイル名をsha1.binとします。 その後以下のコマンドで、base64.txtにハッシュ値を出力します。 openssl base64 -in sha1.bin -out base64.txt 以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】WebViewをJetpack Composeで使った場合の、バックキーで前ページに戻る方法

WebViewでは、バックキー(戻るボタン)を押した場合に前のページに戻る(ブラウザバック)のが一般的な動作かと思います。 本記事では、WebViewをJetpack Composeで使った場合の、バックキーで前ページに戻る方法をメモとして投稿します。 コード @Composable fun MyWebView() { // 1 val backPressed = remember { mutableStateOf(false) } // 2 AndroidView( factory = { context -> WebView(context).apply { webViewClient = object : WebViewClient() {} }.also { it.loadUrl("https://qiita.com") } }, // 4 update = { webView -> if (backPressed.value) { backPressed.value = false if (webView.canGoBack()) { webView.goBack() } else { // 前のページがない場合の処理(画面を閉じるなど) } } }, ) // 3 BackHandler( enabled = true, onBack = { backPressed.value = true }, ) } コードのポイント ポイントとなる箇所1〜4を説明します。 バックキーを押したかどうかの状態 backPressed を定義します。 AndroidView を使って WebView を表示させます。 一番のポイントです。BackHandler を使って、バックキーのハンドリングを行います。バックキーが押されたタイミングで onBack のラムダが呼ばれるので、バックキーの押下状態を変更します。 状態を変化させると再コンポーズされ、 AndroidView の update が呼ばれるので、そこで backPressed をチェックし、押された状態であれば WebView の goBack() を呼び出し前のページに戻ります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MediaCodecでbitmap列から動画作成

動くコードと参考文献を載せておきます。kotlin&Coroutineに書き直しました。随所お好みにカスタマイズしてください。 jcodecの100倍くらいエンコード早かったです。さすがAndroid純正フレームワーク。 2021/10/08: エラーが出たので、scaled.recycle()をコメントアウトしました。   2021/10/10: 読み込ませるframeのwidthとheightが、偶数値でないとエラーになるバグを修正しました。 使い方.kt val frames: List<Bitmap> = .... val videoFile: File? = MediaCodecEncoder().encode(this, frames)// suspend function MediaCodecEncoder.kt // MediaCodecフレームワークを使ったエンコード class MediaCodecEncoder { private val MIME_TYPE = "video/avc" private val FRAME_RATE: Int = 30 private val BIT_RATE: Int = 16000000 private val I_FRAME_INTERVAL: Int = 1 // フレーム列を動画にエンコードする // フレーム列を動画にエンコードする override suspend fun encode(context: Context, frames: List<Bitmap>): File? = withContext(Dispatchers.Default) { if (frames.isEmpty()) { return@withContext null } // 2021/10/10: 寸法が偶数値になるように修正 val refEvenWidth = (frames[0].width / 2) * 2 val refEvenHeight = (frames[0].height / 2) * 2 if (refEvenWidth == 0 || refEvenHeight == 0) { return@withContext null } val outputFile = File(context.cacheDir, "${UUID.randomUUID()}.mp4") val mediaCodec = getInitialCodec(refEvenWidth, refEvenHeight) ?: return@withContext null val mediaMuxer = getInitialMuxer(outputFile) ?: return@withContext null mediaCodec.start() val TIMEOUT_USEC_IN = 500000L// 入力バッファの使用(?)制限時間 val TIMEOUT_USEC_OUT = 500000L// 出力バッファの使用(?)制限時間 var trackIndex = -1// muxerのトラック var count = 0// debug code for (i in frames.indices) { // debug code count += 1 Log.d("debug_so", "MediaCodecライブラリ エンコード進捗 $count 枚 / ${frames.size}枚(${frames.size / FRAME_RATE}秒分)") // エンコード用のフレームバイトデータを作成 val frame = frames[i]                         // 2021/10/10: 寸法が偶数値になるように修正 val evenFrameWidth = (frames[i].width / 2) * 2 val evenFrameHeight = (frames[0].height / 2) * 2 val byteFrame = getNV21(evenFrameWidth, evenFrameHeight, frame) // 入力バッファを取得 val inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_USEC_IN) val ptsUsec = computePresentationTime(i.toLong(), FRAME_RATE) if (inputBufferId < 0) { return@withContext null } val inputBuffer = mediaCodec.getInputBuffer(inputBufferId) ?: return@withContext null inputBuffer.clear(); inputBuffer.put(byteFrame) // 入力バッファにエンコードしたいバイトデータを流し込む mediaCodec.queueInputBuffer(inputBufferId, 0, byteFrame.size, ptsUsec, 0); // 出力バッファの取得 var bufferInfo = MediaCodec.BufferInfo() var outputBufferId = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC_OUT) if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){// 新しい入力フォーマットが設定された時(一番最初は必ず読み出される) // muxerの初期化 trackIndex = mediaMuxer.addTrack(mediaCodec.outputFormat); mediaMuxer.start() delay(500)// debug_so: なぜか時間をおくと、MediaCodec.BufferInfo().size がnon-0になってくれる。 // 出力バッファの再設定 bufferInfo = MediaCodec.BufferInfo() outputBufferId = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC_OUT) } if (outputBufferId < 0) {// 不正な出力バッファ Log.d("debug_so","invalid outputBufferId: $outputBufferId") return@withContext null } if (bufferInfo.size <= 0) {// バッファがない return@withContext null } // 1フレームのoutputBufferをエンコードして動画に加える val encodedData = mediaCodec.getOutputBuffer(outputBufferId) ?: return@withContext null encodedData.position(bufferInfo.offset) encodedData.limit(bufferInfo.offset + bufferInfo.size) mediaMuxer.writeSampleData(trackIndex, encodedData, bufferInfo) mediaCodec.releaseOutputBuffer(outputBufferId, false) } mediaCodec.stop() mediaCodec.release() mediaMuxer.stop() mediaMuxer.release() return@withContext outputFile } private fun getInitialCodec(width: Int, height: Int): MediaCodec? { // mediaCodec作成 val codecInfo = selectCodec(MIME_TYPE) ?: return null val colorFormat = selectColorFormat(codecInfo, MIME_TYPE) ?: return null val mc: MediaCodec try { mc = MediaCodec.createByCodecName(codecInfo.name) } catch (e: Exception) { return null } // 基礎設定 val mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, width, height) mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE) mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE) mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat) mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL) mc.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) return mc } private fun getInitialMuxer(outputFile: File) : MediaMuxer? { return try { MediaMuxer(outputFile.path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) } catch (e: Exception) { null } } private fun computePresentationTime(frameIndex: Long, framerate: Int): Long { return 132 + frameIndex * 1000000 / framerate } private fun selectCodec(mimeType: String): MediaCodecInfo? { val codecInfos = MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos for (codecInfo in codecInfos) { if (!codecInfo.isEncoder) { continue } val types = codecInfo.supportedTypes for (j in types.indices) { if (types[j].equals(mimeType, ignoreCase = true)) { return codecInfo } } } return null } private fun selectColorFormat(codecInfo: MediaCodecInfo, mimeType: String): Int? { val capabilities = codecInfo.getCapabilitiesForType(mimeType) for (colorFormat in capabilities.colorFormats) { if (isRecognizedFormat(colorFormat)) { return colorFormat } } return null } private fun isRecognizedFormat(colorFormat: Int): Boolean { return when (colorFormat) { CodecCapabilities.COLOR_FormatYUV420Flexible -> true else -> false } } private fun getNV21(inputWidth: Int, inputHeight: Int, scaled: Bitmap): ByteArray { val argb = IntArray(inputWidth * inputHeight) scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight) val yuv = encodeYUV420SP(ByteArray(inputWidth * inputHeight * 3 / 2), argb, inputWidth, inputHeight) // scaled.recycle() => 2021/10/08: エラーが出るのでコメントアウトしました。 return yuv } private fun encodeYUV420SP(yuv420sp: ByteArray, argb: IntArray, width: Int, height: Int): ByteArray { val frameSize = width * height var yIndex = 0 var uvIndex = frameSize var a: Int var R: Int var G: Int var B: Int var Y: Int var U: Int var V: Int var index = 0 for (j in 0 until height) { for (i in 0 until width) { a = argb[index] and -0x1000000 shr 24 // a is not used obviously R = argb[index] and 0xff0000 shr 16 G = argb[index] and 0xff00 shr 8 B = argb[index] and 0xff shr 0 Y = (66 * R + 129 * G + 25 * B + 128 shr 8) + 16 U = (-38 * R - 74 * G + 112 * B + 128 shr 8) + 128 V = (112 * R - 94 * G - 18 * B + 128 shr 8) + 128 yuv420sp[yIndex++] = (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() if (j % 2 == 0 && index % 2 == 0) { yuv420sp[uvIndex++] = (if (U < 0) 0 else if (U > 255) 255 else U).toByte() yuv420sp[uvIndex++] = (if (V < 0) 0 else if (V > 255) 255 else V).toByte() } index++ } } return yuv420sp } } 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む