Goalist Developers Blog

食欲の秋をしのぐためのアプリを作ってみた

Trick or Treat!渡部です。

f:id:watabe1028:20171031123524p:plain

最近食欲がやばいです。
栗、芋、南瓜の魅力に勝てません。
かといって運動もしたくありません。
 
このままでは太ってしまいます。
DevからDebuになったらおしまいです。

 
せめて夕飯だけでも抑えねば!と思い
何か役立つアプリを作ってみようと考えました。

毎日食べたものを記録するアプリは作るのも
使うのも面倒です。

 
何もしなくても戒めてくれるアプリ・・・

 
そうや!飯時に通知がくるアプリ作ったろ!
ってことで夕飯時に戒めの言葉を送ってくるアプリを作ろうと思います。
割と忙しいし面倒なので最低限の簡単な構成で作ろう!

 
手順
・適当に画面を作る
・ローカル通知を実装
・実行

サクッといきます!

 

適当に画面を作る

今回は画面は必要ないので弊社サービスを牛耳るふろぐん(4回目)の
ハロウィンバージョンを表示させます。
特に意味はありません。

f:id:watabe1028:20171031120621p:plain

 
 

ローカル通知を実装

次に通知の実装です。
前回にリモート通知の実装をしたので詳しくはそちらを参照ください。
まずは3秒後に通知がくるように実装します。
以下のコードを丸々コピペすればおけ!です。
AppDelegate.swiftに書きます。

import UIKit
import UserNotifications    // 追加
import NotificationCenter   // 追加

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // 全ての通知を一括で削除
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
        
        // 通知許可ダイアログ
        UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in if granted {print("通知許可")}
        }
        
        // 通知の設定
        let notificationContent = UNMutableNotificationContent()
        notificationContent.title = "よう、デブ!"
        notificationContent.body = "飯食ってる場合じゃねーぞ!"
        notificationContent.sound = UNNotificationSound.default()
        
        // 3秒後に通知
        let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3, repeats: false)
        let request = UNNotificationRequest.init(identifier: "YoDebuNotification", content: notificationContent, trigger: trigger)
        
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.add(request)
        notificationCenter.delegate = self
        
        return true
    }
    
    // 通知の受信
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert,.sound])
    }
    
    // 通知に対する操作
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        
        // alertで表示
        let contentBody = response.notification.request.content.body
        let alert:UIAlertController = UIAlertController(title: "何も食べません!", message: contentBody, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
            (action:UIAlertAction!) -> Void in
            print("食べないことを誓いました。")
        }))
        self.window?.rootViewController?.present(alert, animated: true, completion: nil)
        
        completionHandler()
    }
}

 
 

実行

これを実行するとこんな感じで表示されます。

f:id:watabe1028:20171031122628p:plain

アプリを閉じていても
f:id:watabe1028:20171031122735p:plain

 
ふろぐんがにくたらしいですね!

 
 

次に時間を指定します。
だいたい21時ごろが夕飯の時間帯なので
21時に通知がくるように設定します。

    // 21時に設定
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "よう、デブ!"
    notificationContent.body = "飯食ってる場合じゃねーぞ!"
    notificationContent.sound = UNNotificationSound.default()
        
    let date = DateComponents(hour:21)
    let trigger = UNCalendarNotificationTrigger.init(dateMatching: date, repeats: true)
    let request = UNNotificationRequest.init(identifier: "YoDebuNotification", content: notificationContent, trigger: trigger)
        
    let notificationCenter = UNUserNotificationCenter.current()
    notificationCenter.add(request)
    notificationCenter.delegate = self

以上で完了です。
UNCalendarNotificationTrigger.init(dateMatching: date, repeats: true)
のrepeatsをtrueにしとけば毎日繰り返し通知がくるようになります。

 
 

おわり

モンブラン、かぼちゃの煮物、焼き芋に秋刀魚。。。
なかなか食欲を抑えるのは厳しいですが
こういったアプリで自分を律することにします!
できれば・・・ですが・・・

今日はハロウィンのせいか
お菓子が賑わっています。
早速律することをやめます。

f:id:watabe1028:20171031162810j:plain

コトリン入門開始!

読者の皆さん、 こんにちは!

ゴーリストのティンカーです!

いよいよコトリンの入門シリーズスタートです!
ここでちょこちょこコトリンの世界で一緒に迷い、遊び、楽しい時間を過ごすつもりなので、よろしくお願いします!

ではでは、
本日のトピックに関してなんですが、なんと。。。
開発環境の立ち上げとなります。。。!

”え。。。開発に手を加えたいのに。。”
と思ってる方もいらっしゃると思うんですが、大丈夫です!
来週から毎週新しい記事を出すつもりなので、バリバリやりますのでご安心ください!!

では早速なんですが、環境を立ち上げましょう!

今回の流れ

開発環境の設定:

  • 統合開発環境(IDE)、Android Studio 3.0のインストール(Mac上)。
  • 仮想端末(バーチャルデバイス)の作成。

それだけです!!

まずはダウンロードから行きます!

こちらのURL(アンドロイドの公式サイト)にてインストールの指示に従い、ダウンロードし、インストールしてください!

https://developer.android.com/studio/install.html (画面の一番下から言語を日本語に変更できます)

インストールが終わったら、新しいプロジェクトを作成しましょう!

f:id:TinkerJack:20171027123425p:plain
プロジェクト作成画面

Android Studio 3.0以降、コトリンのサポートが入っています!(Android Studio 3.0以前はわざわざライブライのプラグインをインストールしなきゃいけなかった)、 なので "Include Kotlin Support"のチェックボックスにチェックを入れます。

f:id:TinkerJack:20171027123059p:plain
Kotlin対応の導入

Activityの種類を選びましょう!(ハローコトリンを画面に出すだけなので、"Empty Activity"を選択しましょう)

f:id:TinkerJack:20171027123357p:plain
Activity種別の選択

Activityとlayout名を設定しましょう。こちらでコトリンの処理を書きます!

f:id:TinkerJack:20171027123401p:plain
Activity名を設定

これでプロジェクトの作成が完了しました!

ただ、依存関係が足りていないためにエラーが現れる可能性があります。ですが、Android Studioが必要な依存関係を教えてくれるので、エラーが出た場合はそれらをダウンロードしてgradleのプロジェクトをSynchronizeすれば解決する筈です!

プロジェクトを作成出来ました!

Androidの端末があると便利なんですが、やはり最近の端末がなかなか買いられないものなのです。 でも大丈夫です!Android Studioで仮想端末(Virtual Device)を作ることが出来ます! なので次はバーチャルデバイスの作成です!

アプリを実行(Playボタンを押すことで)したら、現在使われているデバイスの画面が表示されます! その時は "Create a New Virtual Device"を選択します!

f:id:TinkerJack:20171027123406p:plain
バーチャルデバイスの作成

ここで作りたい仮想端末を選択できます!

f:id:TinkerJack:20171027144507p:plain
バーチャルデバイスの選択

アプリのためのデフォルト画像を選択しなきゃいけません。 そのために画像をダウンロードする必要があります!

f:id:TinkerJack:20171027123353p:plain
アプリ画像

こちらをダウンロードして "finish" を押したら、バーチャルデバイスの作成は完了します!

これで使えるスケレトンコードとバーチャルデバイスの準備が整ったので、アプリを実行してみましょう!

f:id:TinkerJack:20171027145831p:plain
ハローコトリン!

おめでとうございます!これで初めてのコトリンのAndroidアプリの作成が完了しました!

お楽しみいただけたでしょうか!

次回までkotlinの勉強や開発でお楽しみください!!

ゴーリストエンジニア
イムラーン

Angularでng2-chartsを使ってグラフを描画する

おはようございます。 ゴーリスト新卒一年目、見習い開発者のバンナイです。

このブログの背景

先日、弊社で、AngularJSを使って書かれていたWEBアプリケーションをAngular4で書き換えるというプロジェクトに携わらせてもらいました。

初めてのフロントコーディング、初めてのフロントでの外部ライブラリの使用、初めてのAngular、ということでいろいろなことがありました。

その中でも比較的時間が割かれたグラフ機能に関して色々やったことをブログに書きたいと思います。

  1. 最初のグラフが描画されるまで
  2. グラフの見た目を整える
  3. グラフを動的に描画する

の三本立てで書きたいと思います。

準備

使用ライブラリ

valor-software.com

というライブラリを使いました。

早速 https://valor-software.com/ng2-charts/ に書いてあるInstallationとやAPI等を参考にして最初のグラフを描画してみたいと思います!

インストール

まずAngularCLIで新しいAngularプロジェクトを作ります。

その後、

Installation

に従い

npm install ng2-charts --save
npm install chart.js --save

ng2-chartsとchart.jsをインストール。

ng2-chartsだけではなくchart.jsもインストールするんですね。

後はindex.htmlのheadタグの中に

<script src="../node_modules/chart.js/src/chart.js"></script>

を書き込みました。

弊社都合によりバージョンを固定します。 以下のようにpackage.jsonのバージョンの部分を書き変えます。

"chart.js": "~2.5.0
"ng2-charts": "~1.5.0"

この後バージョンを反映させるために

npm install

を使います。

import

APIのUsage

を参考に

import { ChartsModule } from 'ng2-charts';

// In your App's module:
imports: [
   ChartsModule
]

をここでも言われるがままに適宜書きます。 僕はapp.module.tsに書きました。

Canvasタグ

僕が描画したいのは棒グラフなので

Bar Chart

の部分にあるソースコードをコピります。

<div>
  <div style="display: block">
    <canvas baseChart
            [datasets]="barChartData"
            [labels]="barChartLabels"
            [options]="barChartOptions"
            [legend]="barChartLegend"
            [chartType]="barChartType"
            (chartHover)="chartHovered($event)"
            (chartClick)="chartClicked($event)"></canvas>
  </div>
  <button (click)="randomize()">Update</button>
</div>

をhtmlファイルに

import { Component } from '@angular/core';
 
@Component({
  selector: 'bar-chart-demo',
  templateUrl: './bar-chart-demo.html'
})
export class BarChartDemoComponent {
  public barChartOptions:any = {
    scaleShowVerticalLines: false,
    responsive: true
  };
  public barChartLabels:string[] = ['2006', '2007', '2008', '2009', '2010', '2011', '2012'];
  public barChartType:string = 'bar';
  public barChartLegend:boolean = true;
 
  public barChartData:any[] = [
    {data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A'},
    {data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B'}
  ];
 
  // events
  public chartClicked(e:any):void {
    console.log(e);
  }
 
  public chartHovered(e:any):void {
    console.log(e);
  }
 
  public randomize():void {
    // Only Change 3 values
    let data = [
      Math.round(Math.random() * 100),
      59,
      80,
      (Math.random() * 100),
      56,
      (Math.random() * 100),
      40];
    let clone = JSON.parse(JSON.stringify(this.barChartData));
    clone[0].data = data;
    this.barChartData = clone;
    /**
     * (My guess), for Angular to recognize the change in the dataset
     * it has to change the dataset variable directly,
     * so one way around it, is to clone the data, change it and then
     * assign it;
     */
  }
}

をtsファイルに貼り付けます。

バグ?

これでグラフが描画される…はずだったのですが描画されません。 コンソールを見てみると f:id:bbbbbbbbb9:20171019174912p:plain

なんだかこれが原因かなというエラーが出ていました。

まったく意味が不明だったのでこのメッセージでそのままググってみました。結果。

index.htmlのheadタグに貼り付けた

<script src="node_modules/chart.js/src/chart.js"></script>

を消して

angular-cli.jsonのscripts項目に

"scripts": [
        "../node_modules/chart.js/src/chart.js"
],

と書くと上手くいくみたいです。謎です。

他にもいろいろな解決策が書いてありましたが僕はこれでうまくいきました。

これでグラフが描画されました。

f:id:bbbbbbbbb9:20171019175454p:plain

よかった~。

次のブログではオプションなどをいじってグラフの見た目等を色々といじっていきます。

最終的にグラフが、様々な数値やグラフの本数で動的に描画されるようになるのですが、その際にどんな条件でも見た目が崩れないようにする等、予想外にやることが沢山ありました。

AWS RDSにJDBCでSSL接続する

こんにちは。ゴーリスト開発の飯尾です。
AWS RDS for MySQLに、SSL証明書を使ってJDBC接続する時に行ったことをまとめました。

前提

  • RDSのセキュリティグループで自IPからのアクセスを許可しておく
  • 2017年10月の試行メモ

手順

1. 証明書をゲットだぜ

$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

2. 証明書をjksファイルに変換する

YOUR_JKS_PASSWORDのところはお好みで変えてください。

Macなのでこう

$ split -p "-----BEGIN CERTIFICATE-----" rds-combined-ca-bundle.pem individual-
$ find . -iname 'individual*' -exec keytool -import -file {} -alias {} -storepass YOUR_JKS_PASSWORD -keystore amazon.jks \;

Linuxならcsplitで分割して同様にひとつのjksファイルにまとめる。

このへんを見ました。
Using Java to establish a secure connection to MySQL Amazon RDS (SSL/TLS) - Stack Overflow

3. 作成したjksファイルを任意のフォルダに移動

# mkdir /opt/jks
# mv amazon.jks /opt/jks

4. 接続URLに各パラメータを追加する

  • useSSL=true
  • requireSSL=true
  • verifyServerCertificate=true
  • trustCertificateKeyStoreUrl=file:///opt/jks/amazon.jks # 作成したjksファイルのパス
  • trustCertificateKeyStoreType=JKS
  • trustCertificateKeyStorePassword=YOUR_JKS_PASSWORD # jksファイル作成時に設定したパスワード

こんなかんじになりました

jdbc:mysql://YOUR_SERVER/YOUR_DB?characterEncoding=UTF8&useSSL=true&requireSSL=true&verifyServerCertificate=true&trustCertificateKeyStoreUrl=file:///opt/jks/amazon.jks&trustCertificateKeyStoreType=JKS&trustCertificateKeyStorePassword=YOUR_JKS_PASSWORD

5. あとはいつものとおりDriverManager.getConnection

参考URL

qiita.com

blog.cles.jp

stackoverflow.com

MySQL :: MySQL Connector/J 5.1 Developer Guide :: 5.5 Connecting Securely Using SSL

JKS 形式のキーストアの作成 (SSL をサポートする Java CAPS の構成)

EC2上のTomcatからRDSにSSL接続する方法 | infoScoop開発者ブログ

ひとこと

真面目さがアフレデル〜

【GoogleMapsAPI】Javaで住所から緯度経度を取得する

こんにちは。ゴーリスト開発の飯尾です。

住所から緯度経度を割り出そ〜というときにはどうしたものか。

国交省のデータを使うという手もありますが
田舎は詳細データなくてちょっと精度がこころもとないし、
表記ぶれにも弱くて使いづらいところがあります。

そこで天下のGoogle御大よ
GoogleMapsAPIのジオコーダーを使うとかんたんに詳細な緯度経度を取得できます。

そんなわけでJavaで住所から緯度経度を取得する方法のメモです。

手順

  1. APIキーを取得
  2. GoogleMapsAPIのJavaクライアントライブラリを入れる
  3. つかう

説明

1. APIキーを取得

以下リンクからはじまり
キーの取得、認証  |  Google Maps Geocoding API  |  Google Developers

「キーを取得する」ボタンをポチッとな
f:id:y-iio:20171012184920p:plain

てきとうなプロジェクト名で登録
f:id:y-iio:20171012185007p:plain

取得完了
f:id:y-iio:20171012185113p:plain

やったね

2. GoogleMapsAPIのJavaクライアントライブラリを入れる

公式がライブラリ出しているので遠慮なく使わしてもらう

Google マップ ウェブサービスのクライアント ライブラリ  |  Google マップ ウェブサービス API  |  Google Developers

github.com

GradleプロジェクトなんでMavenからひっぱってきました

build.gradle

dependencies {
    compile 'com.google.maps:google-maps-services:0.2.2'
    compile 'org.slf4j:slf4j-nop:1.7.25'
}

3. つかう

雑だけどこんなんでいけるんじゃないか

public class Geocoder {

    private static GeoApiContext context = new GeoApiContext.Builder()
            .apiKey("YOUR_API_KEY") // さっき取得したAPIキー
            .build();

    public static void main(String[] args) {
        GeocodingResult[] resluts = getResults("東京都千代田区神田須田町1-18");
        if (results != null && results.length > 0) {
            LatLng latLng = results[0].geometry.location; // とりあえず一番上のデータを使う
            System.out.println("緯度 : " + latLng.lat);
            System.out.println("経度 : " + latLng.lng);
        }
    }

    public static GeocodingResult[] getResults(String address) throws ApiException, InterruptedException, IOException {
        GeocodingApiRequest req = GeocodingApi.newRequest(context)
                .address(address)
                // .components(ComponentFilter.country("JP"))
                .language("ja");

        try {
            GeocodingResult[] results = req.await();
            if (results == null || results.length == 0) {
                // ZERO_RESULTSはresults.length==0の空配列がsuccessful扱いで返ってくるっぽい
                System.out.println("zero results.");
            }
            return results;
        } catch (ApiException e) {
            // ZERO_RESULTS以外のApiExceptionはこっちで
            System.out.println("geocode failed.");
            System.out.println(e);
            return null;
        } catch (Exception e) {
            System.out.println("error.");
            System.out.println(e);
            return null;
        }
    }

}

所感

きになったところ

その1

ステータスコードZERO_RESULTSのときは
ApiExceptionにひっかからないで空の配列が返ってくるっぽかった

その2

GeocodingApiRequest でコンポーネントフィルタかけてリザルトを国内のみに限定したら
.components(ComponentFilter.country("JP"))のところ)
ヘンテコな住所送った時にZERO_RESULTSにならずに
「日本」のジオコード結果が返ってきてしまって不便だったのでコメントアウトしている…

よいところ

その1

リザルトオブジェクトとか用意してくれてるのがうれし〜

その2

プレミアムプランのときの認証が楽
リクエストURL署名までしてくれるのがうれし〜

一言

快適なジオコーディングでQOLをあげていく( ◜◡^)

おわり

グラデーションをコードで書いてみた@Swift4

こんちは。渡部です。

現在社内アプリを(勝手に)作っています。
が、徐々にモチベーションが下がってきたので
気分が乗るようにデザイナーに画面作ってくれ!と依頼しました。

そして上がってきた画面がこれ
f:id:watabe1028:20171010164511p:plain

あらオシャレ。
女性のデザイナーなのですがブッチャーあたりを入れてくるのはさすがです。
きっとみんな知りません。
そんな方はこちら(閲覧注意)。
 
 
イマドキっぽいデザインで良いのですが、
機能が少々追加されてますね。。。
 
 
それにデザイナーはみんな忙しそうです。
あと7画面、複数サイズ、素材の切り出しとか依頼したら
ヒールホールドで足を破壊されそうです。
 
 
仕方ない、自分でやるか。と思いSketchを立ち上げましたが
普通にやっても面白くありません。というかSketchが使いこなせません。
 
そうだ!京都に行こうコーディングしよう!
だって一応コーダーですし。おすし。
 
ということでこんな感じでやりました。
(前置き長ぇ)
 

 

白黒でグラデーションを書いてみる

適当にプロジェクトを用意してviewDidLoad内にこんな感じでコードを書きます。

override func viewDidLoad() {
    super.viewDidLoad()
        
    // 白黒半々になる
    let startColor = UIColor(white: 1, alpha: 0).cgColor
    let endColor = UIColor(white: 0, alpha: 1).cgColor

    let layer = CAGradientLayer()
    layer.colors = [startColor, endColor]
    layer.frame = view.bounds

    view.layer.addSublayer(layer)
}

実行するとこんな感じです。
f:id:watabe1028:20171010171027p:plain

シンプルですね。

 

それっぽい色でやってみる

次にもらったデザインの色でやってみます。
同じく以下のようにコードを書きます。

override func viewDidLoad() {
    super.viewDidLoad()
    // もらったデザインの色を指定
    // 02ADB0 ~ 00CDAC
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    let startColor = UIColor(red: 2/255.0, green: 173/255.0, blue: 176/255.0, alpha: 1.0).cgColor
    let endColor = UIColor(red: 0/255.0, green: 205/255.0, blue: 172/255.0, alpha: 1.0).cgColor

    // viewの左上から右下へ
    gradientLayer.colors = [startColor, endColor]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
    gradientLayer.frame = view.bounds

    view.layer.addSublayer(gradientLayer)
}

実行すると・・・
f:id:watabe1028:20171010171058p:plain

それっぽくなりました。
値の調整をすれば良い感じになりそうです。

 

昔のインスタのログイン画面も
f:id:watabe1028:20171010171203p:plain

こんな感じでやると

override func viewDidLoad() {
    super.viewDidLoad()
    // 昔のインスタ風
    //97347F ~ 873BA0
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    let startColor = UIColor(red: 151/255.0, green: 52/255.0, blue: 127/255.0, alpha: 1.0).cgColor
    let endColor = UIColor(red: 135/255.0, green: 59/255.0, blue: 160/255.0, alpha: 1.0).cgColor

    // viewの左から右へ
    gradientLayer.colors = [startColor, endColor]
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
    gradientLayer.frame = view.bounds

    view.layer.addSublayer(gradientLayer)
}

こう!
f:id:watabe1028:20171010171227p:plain

うぇい!
 
 

まとめ

コーダーは全部コードでやりたいですよね。
Storyboardができる以前のInterfaceBuilderは使い物になりませんでした使いこなせませんでした。
なのでボタンから何から全部コードで書いたのも懐かしいです。
今回はソースを見てもらえばわかる通り、
CAGradientLayerを使えば一発でグラデーションが描画できます、という記事です。

この書き方は単色のグラデーションなら可能ですが
複数色の場合は諦めて画像作ってもらいましょう!

Angular@4.1.2のCSSカプセル化について

こんにちは、ゴーリスト開発のモリツグです。

最近Angular向けのWijmo5をためしているのですが、Wijmo5の中の人はFlex愛好家だと思います。
全国に30人はいるであろうFlex愛好家が海外にも居たんだと勝手に思っています。
ブログ投稿のたびに書いてますが、Angularは悲しみに暮れるFlex愛好家にはピッタリなのでお勧めです。

今回はAngularのCSSカプセル化について書いてみたいと思います。
とはいえ、すでに先達の方々がこちらのリンクなどで詳しく説明されているので、styleUrlsまわりに絞って紹介したいと思います。
デモソースのplunkerはこちらです。

styleUrls

とにかくCSSをカプセル化したい!という場合はcssを用意してstyleUrlsで指定します。

@Component({
  selector: 'my-app',
  styleUrls : [
    `./my-app.css`,
  ],
  // encapsulation:ViewEncapsulation.Emulated
  template: `
    <div>demo</div>
    <child-comp></child-comp>
    <child-comp class="test-red"></child-comp>
  `
})

コメントアウトされているencapsulationについて説明します。
encapsulationに設定できる値はViewEncapsulation.None/Emulated/Nativeの3種類があります。
・None: カプセル化を行わない。
・Emulated: カプセル化を行う。
・Native: Shadow DOMを使う。人類にはまだちょっと早い。

encapsulationのデフォルト値はstyleUrlsの有無によって以下のように変わります。
・styleUrlsを定義しない場合: None
・styleUrlsを定義した場合: Emulated

したがって、先の例ではstyleUrlsが定義されているので、encapsulation:ViewEncapsulation.Emulatedをコメントアウトしてもしなくても同じです。

CSSカプセル化の実現方法

先ほどのコンポーネントがhtmlになった状態を見ると、以下のように<head>の中に<style>でmy-app.cssが埋め込まれており、_nghost-*や_ngcontent-*が各要素と.titleに付与されています。

<head>
 ...
  <style>
    @charset "UTF-8";
  
    .title[_ngcontent-c0] {
      color: red;
    }
  </style>
  ...
</head>
...
<my-app _nghost-c0="" ng-version="4.1.2">
  <div _ngcontent-c0="" class="title">demo</div>
  <child-comp _ngcontent-c0="" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
  <child-comp _ngcontent-c0="" class="test-red" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
</my-app>

Angularではこのように<style>を埋め込んでcssセレクタで_nghost-*や_ngcontent-*が一致する要素にだけcssが反映されてカプセル化を実現しているようです。

呼び出したコンポーネントの見た目を場合によって変えたい

今回の場合だと<child-comp class="test-red">のtest-redが効くようにしたい場合があると思います。
方法としては以下があります。
①:host-context疑似セレクタを使う。(今回は説明しません)
②:host疑似セレクタを使う。
③::ng-deep(以前は/deep/)セレクタを使う。(Shadow-Piercing自体があらゆるブラウザで無かったことにされるようなので使わないほうが良さそう、Angularのこの件についての公式説明はこちら)

①か②の方法が良さそうですが、今回は②の方法を紹介したいと思います。
なんか感覚的には呼び出し元のmy-app.cssで以下のように書けばcssが効きそうですが、効きません。

 /* my-app.css */
.test-red div {
  color: red;
}

正しくは<child-comp>のcssで以下のように指定します。

 /* child-comp.css */
:host(.test-red) div {
  color: red;
}

先ほど_nghost-*や_ngcontent-*という要素が挿入されていましたが、:hostは自分自身の_nghost-*に対応します。
詳しくはこちらのplunkerで確認してください。

まとめ

今回はAngularのCSSカプセル化についてザックリと紹介しました。
冒頭のQiitaのリンクが詳しくて参考になります。
本当は③の方法が一番綺麗な気がしますが、①か②の方が安全かなぁと思います。