ページ

2019年7月18日木曜日

UILabelをタップ可能にする(小ネタ)

UILabelは rx.tap が使えない

そこで、UITapGestureRecognizer を使ってタップに対応させる
swift
        @IBOutlet private var profileDescription: UILabel!

        ...

        let gesture = UITapGestureRecognizer()
        profileDescription.addGestureRecognizer(gesture)
        gesture.rx.event.asSignal()
            .emit(onNext: { [weak self] state in
                guard let self = self else { return }
                let viewController = XXXXX
                self.navigationController?.pushViewController(viewController, animated: true)
            }).disposed(by: disposeBag)
:warning: 事前に User Interaction Enabled をONにしとかないといけない!
image.png (60.1 kB)

2019年7月12日金曜日

最近よく使っているWebサービスやツール

久しぶりの投稿です。

このブログ自体、技術的な事をつらつら書いている事が多く
それだったらQiitaに書いた方が良いのかと思いそっちに書いていたのですが、
Qiitaに書く感じでも無い事や、小さめなやつとかをこのブログでまた書いて
行こうかと思います。

今回は「最近よく使っているWebサービスやツール」をまとめてみました。


Firefox Send



ログインしなくても、暗号化してファイル共有してくれる便利なサービスです。
有効期限や有効ダウンロード回数を設定できたり、パスワードで保護する事も可能です。ファイルをアップロードしたら後は相手にリンクを教えれば済むだけなので
ファイルサイズが大きなものを送る場合に重宝しています。


Github Link Card Creator

↓こんな感じのGithub リンクページのカードが作れる po3rin さんが作られている
サービスです。



サクッと作れるのでリンクを貼りたい時とかに作って貼っています。


縛りなしWiFi



名前の通り、2年縛りなどの契約期間で縛りがなく、解約金無しで純粋に
使った分だけ料金を支払って使うことができます。
今月は出張などで外で作業する事が多いから1月だけレンタル〜っという事も
できるので便利です。

StackShare



こちらは使うというよりは眺めてるだけのサイトですが、なかなか面白いです。
各ベンダーがどのような技術スタックでサービスを構成しているのか、
みてると新しい発見なんかもあったりします。


といった感じのあまり纏まりが無い感じでしたが、、
また更新があったりしたらブログに書こうかと思います。

2019年1月30日水曜日

【個人開発】ストップウォッチ+メモアプリを更新しました

テンプレートから選択できるようになりました

タイム表示を変更する際に自分でフォーマットを作成する方法と、
新たに「テンプレートから選択」できるようになりました。
ホーム画面から「編集」ボタンで↑の「ラップ表示をカスタマイズ」へ移動し
「テンプレートから追加」ボタンでテンプレートの一覧が表示されます。
お好きなテンプレートを1つ以上選択し、「追加する」をタップすると
「ラップ表示をカスタマイズ」の一覧に追加されます。
追加されたフォーマットは、ホーム画面のストップウォッチで選択し
使用する事ができます。




アプリのダウンロードはこちら

Google Play で手に入れよう

2019年1月18日金曜日

MaterialSearchViewを使う前に知っておいて欲しいこと

MaterialSearchViewを使う前に知っておいて欲しいこと

MaterialSearchViewとは?

虫ネガネアイコンを押すと、検索窓がニュッと出てきて検索可能にする便利なライブラリです。
イメージとしては↓の様な感じのもの(公式より)

公式ページ

💻環境構築


app/build.gradleに以下を追加しライブラリをプロジェクトに追加

implementation 'br.com.mauker.materialsearchview:materialsearchview:1.2.3'

次にSuggestions用にContentProviderを使っているっぽいので、
br.com.mauker パッケージを作成し、MsvAuthority.kt を作成

package br.com.mauker

object MsvAuthority {
    const val CONTENT_AUTHORITY: String = "${BuildConfig.APPLICATION_ID}.material_search_view"
}

AndroidManifestに以下を追加

    <application ... >
        <provider
        android:name="br.com.mauker.materialsearchview.db.HistoryProvider"
        android:authorities="${applicationId}.material_search_view"
        android:exported="false"
        android:protectionLevel="signature"
        android:syncable="true"/>
    </application>

STG/PRO用など、環境毎にアプリを共存させたいので以下の様に

android:authorities="${applicationId}.material_search_view"

BuildVariantで異なるアプリケーションIDをauthoritiesに含めて設定しておきます。
この時点でビルドして通ればOK 👌

📝 実装


シンプルな検索画面を作成する

1 SearchActivityの作成

  • 空のActivityを作成
    2 スタイルの設定
    <style name="MaterialSearchViewStyle">
        <item name="searchBackground">@color/white_ish</item>
        <item name="searchVoiceIcon">@drawable/ic_action_voice_search</item>
        <item name="searchCloseIcon">@drawable/ic_action_navigation_close</item>
        <item name="searchBackIcon">@drawable/ic_action_navigation_close</item>
        <item name="searchSuggestionBackground">@color/search_layover_bg</item>
        <item name="historyIcon">@drawable/ic_history_white</item>
        <item name="suggestionIcon">@drawable/ic_action_search_white</item>
        <item name="listTextColor">@color/white_ish</item>
        <item name="searchBarHeight">?attr/actionBarSize</item>
        <item name="voiceHintPrompt">@string/hint_prompt</item>
        <item name="android:textColor">@color/black</item>
        <item name="android:textColorHint">@color/gray_50</item>
        <item name="android:hint">@string/search_hint</item>
        <item name="android:inputType">textCapWords</item>
    </style>

3 レイアウトファイルにMaterialSearchViewを追加

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="xxxxxxxxx.SearchActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/searchToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:theme="@style/ToolbarTheme" />

    <br.com.mauker.materialsearchview.MaterialSearchView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/MaterialSearchViewStyle"/>

</androidx.constraintlayout.widget.ConstraintLayout>

4 res/menu/search_menu.xml の作成

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_action_search_white"
        android:orderInCategory="100"
        android:title="@string/abc_search_hint"
        app:showAsAction="always" />
</menu>

5 SearchActivity の実装

class SearchActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_search)

        initToolbar()
    }

    private fun initToolbar() {
        setSupportActionBar(searchToolbar)
        val actionBar = supportActionBar
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true)
            actionBar.setHomeButtonEnabled(true)
            actionBar.setTitle(R.string.back)
            searchToolbar.setNavigationOnClickListener {
                finish()
            }
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.search_menu, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when (item?.itemId) {
            R.id.action_search -> {
                searchView.openSearch()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onBackPressed() {
        if (searchView.isOpen) {
            searchView.closeSearch()
        } else {
            super.onBackPressed()
        }
    }
}

ここまで実装が終えたら動かしてみると、検索結果は何も表示されず
検索窓だけある画面が表示されます。✨
search.gif (59.9 kB)


2018年10月4日木曜日

Google I/O 2018:Android の新機能のAndroid App Bundle を少しだけ触る

Android App Bundle

  • 新しい公開フォーマット
  • アプリのサイズが劇的に削減される
  • ユーザーがアプリをダウンロードするときにユーザーの端末に一致する コードとリソースのみが配信される

手順

https://developer.android.com/studio/projects/dynamic-delivery
基本Android Stduio3.2以上であれば自動でAndroid App Bundleが適用されていそう。
  • 複数APK作成しなくてもよくなった
  • Dynamic feature modules
    • 特定のフィーチャとリソースをアプリの基本モジュールから分離して、アプリバンドルに含めることができる

Dynamic feature modules

試しに作成してみる:eyes:
Android Studio 3.2で適当にプロジェクトを作成し、「File」>「New」>「New Module」で
「Dynamic Feature Module」を選択
image.png (145.8 kB)
image.png (143.0 kB)
image.png (102.1 kB)
↑ユーザーが見るモジュール名を設定
app/build.gradleに以下が追加される
dynamicFeatures = [":dynamic_feature"]
  • 作成されたdynamic feature moduleのbuild.gradle
gradle
apply plugin: 'com.android.dynamic-feature'
android {
    compileSdkVersion 27
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':app')
}
  • 作成されたdynamic feature moduleのAndroidManifest.xml
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    package="com.example.dynamic_feature">

    <dist:module
        dist:onDemand="true"
        dist:title="@string/title_dynamic_feature">
        <dist:fusing dist:include="true" />
    </dist:module>
</manifest>


image.png (234.2 kB)
テストする際にdynamic feature moduleを含めるかは Run/Debug Configurationdsで設定できる

参考

2018年9月24日月曜日

GASをclasp+typescriptで実装してみる

概要

  • claspがtypescriptサポートした 🎉
  • 筆者がエディッタをVSCodeに乗り換えたので試したい!
  • claspをもう少し詳しく触ってみる

環境

折角なので一から環境作ってみる
$ node -v
v9.5.0

$ npm i @google/clasp -g
古い環境をいったんログアウト
$ clasp logout
※この時古い~/.clasprc.jsonは削除された
再度ログイン
$ clasp login
どのアカウントでログインされているか分からない場合、
$ clasp list
で直近5件分のプロジェクトが表示される

簡単なサンプル作成

$ mkdir ts-sample
$ cd ts-sample
$ clasp create ts-sample
※この時Google Apps Script APIが無効になっていると以下エラーが出る。
Error: Permission denied. Enable the Apps Script API:
https://script.google.com/home/usersettings
表示されているURLへいきGoogle Apps Script APIを有効にする
image.png (25.3 kB)

その後pull
$ clasp pull
必要なパッケージをインストール
$  yarn add typescript @types/google-apps-script
$ yarn add tslint --dev
$ ./node_modules/.bin/tslint --init
.clasp.jsonrootDirを追加
josn
{
  "scriptId":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "rootDir": "src"
}
シンプルにアラートが表示される

push

以下のコマンドでjsへトランスパイルしpushしてくれます。便利!
$ clasp push

エラー

実行時に以下のエラーが表示された場合
image.png (146.4 kB)
「詳細」を開いて、承認を行えばOK
image.png (98.2 kB)

2018年7月25日水曜日

CircleCI2.0上でDockerHubへPushする

概要

  • Docker Buildを行う
  • Serverspecでテストを行う
  • Docker HubへPushを行う

環境

↓環境毎のディレクトリにDockerfileとspec用のディレクトリがある
.
├── android
│   └── spec
├── gcc_cmake
│   └── spec
├── rails
│   └── spec
...
├── specfiles #spec用の共通して使用するファイル
│   └── spec
テストを行う場合、各環境のDocker Imageをビルドし、それぞれのテストをServerspecで行う。

Dockerfile

version: 2
jobs:
  build:
    working_directory: ~/workspace
    branches:
      only:
        - develop
    docker:
      - image: circleci/ruby:2.4
    steps:
      - checkout
      - setup_remote_docker

      # Download and cache dependencies
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "Gemfile.lock" }}
          - v1-dependencies-

      # Install dependencies
      - run:
          name: bundle install
          command: bundle install --path vendor/bundle

      - save_cache:
          paths:
            - vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      # Test
      - run:
          name: run test
          command: ./run-tests.sh

      # Deploy
      - run:
          name: deploy docker images
          command: ./deploy.sh
  • ポイント
    • setup_remote_docker を設定
      • dockerやdocker-composeコマンドが使えるようになる
      • 詳しくはこちら
    • gemに関してはキャッシュしてます

テスト

run-tests.sh
#!/usr/bin/env bash

WORKDIR=$PWD

# Find target directory.
for entity in `find . -type d -mindepth 1 -maxdepth 1 -not -name ".git"`; do

  if [ $entity = "./specfiles" ]; then
    # Ignore directory
    continue
  fi

  # Only exist spec directory.
  if [ -e $entity/spec ]; then
    # Copy specfiles.
    cp $WORKDIR/specfiles/.rspec $entity/.
    cp -r $WORKDIR/specfiles/spec $entity/.

    # Build docker image.
    cd $entity
    ./docker-build.sh
    cd $WORKDIR

    # Exec rspec
    bundle exec rspec --default-path $entity/spec
  fi
done
対象となるディレクトリ配下に spec ディレクトリがある環境だけ
Docker Imageをビルドし、テストを行なっています。
sh
   cp $WORKDIR/specfiles/.rspec $entity/.
   cp -r $WORKDIR/specfiles/spec $entity/.
↑は共通して使用するテスト用のファイルを各環境へコピーしてます。

Docker Hub へ Push

deploy.sh
#!/usr/bin/env bash

docker login -u $DOCKER_USER -p $DOCKER_PASS

# Android
docker push slowhand/android:1.0

# Rails
docker push slowhand/rails:1.0

# gcc + cmake
docker push slowhand/gcc_cmake:1.0
こちらは単純にloginしてpushしているだけです。
$DOCKER_USER などの環境変数はCircleCI上で定義してます。

dockerfiles

今回書いた分は
https://github.com/Slowhand0309/dockerfiles
こちらのリポジトリで公開してます。何かツッコミ等あれば指摘頂けると助かります。