もうすぐリリースのAndroid N 開発者がわくわくしちゃう新機能って?

2016/06/21

Simon Codrington

0

Articles in this issue reproduced from SitePoint
Copyright © 2016, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2016, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

Google I/Oで詳細が発表された次期「Android N」。大注目の「マルチウィンドウ」だけじゃない、開発者目線で注目したいポイントをあらためておさらい。

この記事は2016年5月に更新されており、Google I/Oにおいて発表された変更点を反映しています。

Android Nのなにを知っておくべきか?

これまでグーグルは、次世代のAndoroidを5月のI/Oカンファレンスで発表してきました。しかしAndroid Nの発表のスケジュールはこれまでと違っています。グーグルは、各開発会社が自社アプリをAndroid Nのリリース版に対応できるように前もって準備期間を提供するため、プレビュー版の継続的なリリースを5月から開始しました。Android Nの完成(およびその公式名称の決定)は、2016年度の第3四半期の前までには終わらないでしょう。

これまでのAndroidのアップデートと同様、Android Nも紹介するような広範囲にわたる新たな機能追加と改善点があります。

プレビュー版のダウンロードとインストール

Android Nを入手するには、グーグルのgetting startedのガイドで概説されている段階を踏む必要があります。しかし、この記事では、私の経験に基づいて概略を紹介します。

Android Studio 2.1のダウンロード

現時点ではプレビュー版であるAndroid Studio 2.1をダウンロードしてください。2.0はJava 8とぶつかりますのでダウンロードしないでください。現在のバージョンのAndroid Studio(たとえば1.5)をそのままにしてAndroid Nをテストしたい場合は、現在使っているバージョンの名称を変更してください。そうすれば両方が使えます。

Android Studioプレビュー版のインストールが終わったら、Canary Channelから自動アップデートを設定してください。Android Nのイメージをアップデートできます(そして正しくアップデートされます)。

Android Nイメージのダウンロード

SDKマネジャーを開いてAndroid Nのイメージを検索し、Intel x86 Atom System Imageを選択してください。私の場合は、テストの最初から最後まで作業用のIntel x86 Atom_64 System Imageを入手できませんでしたが、人によって状況は違うと思います。

Java 8 JDK / JREを入手する

Android N用のアプリをコンパイルする際、Android Studio 2.1の一部でJava 8開発キットJava 8実効環境が必要です。

両方のインストールが終わったら、何でもいいのでプロジェクトのファイルストラクチャーを開いてJava 8ランタイムに合わせてください。

グレイドルを更新する

Android Studio 2.1の中で新しいプロジェクトのbuild.gradleを開いて、Android Nがターゲットに設定されているかチェックしてください。どのテストでも、私が見つけたもっとも簡単な方法は、そのアプリがAndroid Nでしか実行できないことを確認する、というものでした。

android {
  compileSdkVersion 'android-N'
  buildToolsVersion 24.0.0 rc1
  ...

  defaultConfig {
     minSdkVersion 'N'
     targetSdkVersion 'N'
     ...
  }
  ...
}

この作業が終われば、作成したアプリがAndroid Nバージョンになっているという満足感が味わえますよ!

新たな機能

セキュリティーの改善

グーグルはセキュリティー上の悪評を払拭するために、次のように相当努力をしています。

  • ダイレクト起動:デバイスが起動していてもロック状態のときに使う新たなモードです。特定のアプリだけにアクセスができて、ほかのアプリにはアクセスできないように設定できます。
  • ファイルごとの暗号化:完全にディスクをブロックしてしまうのではなく、個々のフォルダやファイルを高精度に暗号化できます。
  • メディアフレームワークの強化:Stagefrightの脆弱性による恐怖を経験したあとなので、メディアフレームワークの取り扱いは、1つの大型コンポーネントに多くのデバイス操作の許可を与えるではなく、個々のコンポーネントに分散されました。

マルチウィンドウとフリーフォームモード

マルチウィンドウは、デバイスメーカーがずっとAndroidへの装備を要望してきた機能です。マルチウィンドウ(またはマルチタスキング)機能があれば、スクリーンを分割して2つ以上のアプリを同時に表示できます。

マルチウィンドウ、マルチタスクのアプリがあれば、複数のウィンドウを左右や上下に表示して、複数のタスク間を行き来できます。たとえば、ナビアプリを立ち上げたまま、Webブラウザーが開けるようになります。

Android Nのリリースによって、グーグルはマルチウィンドウを基本機能にしました。スクリーンショットは、スクリーンが分割されGoogleマップの横にChromeが表示されています。

マルチウィンドウアプリというアプローチのおかげで、2つのアプリは真ん中または中央からややずれたくらいの位置にうまくはめ込まれています。大型デバイスのメーカーは、ユーザーがマニュアルで各アプリのサイズを変更できる「フリーダムモード」を使えるように設計できます。

マルチウィンドウアプリのライフサイクルへの変更

どんなアプリも、変化していく過程で形成されるライフサイクル上のいくつかの段階を踏んでいくものです。たとえばAndroid Nでは、ユーザーがホームボタンを押すとアクティブなアプリは非アクティブ状態になります。こうした仕組みをうまく活用すれば、デベロッパーは自社アプリの新機能開発のきっかけにできます。

今や、2つ以上のアプリを一度に表示できる可能性が開けたのですから、この変化に対してアプリがどのように対応するべきかを考える必要があります。アプリが1つだけアクティブな場合でも、複数のアプリを表示しておけます。1つのアプリが「一時停止状態」でも、そのアプリの表示を消したり、スクリーン上の見た目を変えたりする必要はありません。アプリは「停止状態」で実行するだけです。

アプリをマルチウィンドウに適用する

アプリでマルチウィンドウモードを可能にするためには、<application>全体の、または適用させたい個々の<activity>のマニフェストファイルで、新たな属性であるandroid:resizeableActivitytrueに設定する必要があります。

アプリのtargetSdkVersionandroid-Nより以前の場合、属性はデフォルトでfalseになっています。そうではない場合はtrueです。

マルチウィンドウアプリのためのレイアウト属性

アプリをマルチウィンドウに対応するよう設定できたら、アプリがどのようにリサイズされるかを決めるためのレイアウト属性が使えます。

  • android:defaultWidth:アプリをフリーフォームモード(DPまたは一部)で起動したとき、デフォルトの幅を指定
  • android:defaultHeight:アプリをフリーフォームモード(DPまたは一部)で起動したとき、デフォルトの高さを指定
  • android:gravity:アプリを「フリーフォームモード」で起動したときのグラビティの初期値。スクリーンコンテナに関連させて、アプリをどこで分割すればよいかをシステムに指定します(例:top|startを使って下左部に分画表示する)。詳しくは、レイアウトグラビティを参照してください
  • android:minimumSize:アプリを「フリーフォームモード」、または標準的な「サイドバイサイド」マルチタスクモードで起動したとき、最小サイズを設定します(DP内でのみ高さと幅の最小サイズが設定可能)。

マルチウィンドウのためのクエリーメソッド

マルチウィンドウの一部として、現在のアクティビティーがマルチウィンドウモードなのか、ピクチャーインピクチャーモードなのかを確認できるように、activityクラスが拡張されました。レイアウトをカスタマイズしたり、マルチウィンドウでほかの計算を実行したりするときに役立ちます。

  • Activity.inMultiWindow():現在のアクティビティーがマルチウィンドウ内にあるかどうかを知らせます
  • Activity.inPictureInPicture():現在のアクティビティーがピクチャーインピクチャーモード内にあるかどうかを知らせます(通常はAndroid TVのためのアプリ開発用)
  • Activity.onMultiWindowChanged():アプリがマルチウィンドウの中または外に移動したときに呼び出されます。現在マルチウィンドウモード内にあればtrue、そうでなければfalseになります
  • Activity.onPictureInPictureChanged():ピクチャーインピクチャーの状態が変化したときに呼び出されます。現在ピクチャーインピクチャーモードであればtrue、そうでなければfalseになります

マルチウィンドウ状態に対応

UIを変更するために、これらのイベントをどのように使えば良いかについて、実例を紹介します。

アプリがマルチウィンドウモードに変わったとき、その垂直/水平の寸法は30%から70%の範囲で縮小します(ユーザーはアプリ表示を縮小または拡大するために、マルチウィンドウを再配置できるからです)。読みやすくするためにテキストサイズを拡大したい人にとって有用です。

//On Multi-Window, adjust text sizing
public void onMultiWindowChanged(boolean inMultiWindow) {
    super.onMultiWindowChanged(inMultiWindow);

    //find our UI items
    TextView title = (TextView) findViewById(R.id.appTitle);
    TextView subtitle = (TextView) findViewById(R.id.appSubtitle);
    TextView content = (TextView) findViewById(R.id.appContent);

    //read default text sizes (In Sp's from dimension resources) and divide by density to find its size
    Float titleSize = getResources().getDimension(R.dimen.titleTextSize) / getResources().getDisplayMetrics().density; //22 SP
    Float subtitleSize = getResources().getDimension(R.dimen.SubtitleSize) / getResources().getDisplayMetrics().density; //18 SP
    Float contentSize = getResources().getDimension(R.dimen.contentSize) / getResources().getDisplayMetrics().density; //15 SP

    //Entering Multi-window, adjust sizing up
    if(inMultiWindow == true){
        //adjust text size by 25%
        title.setTextSize(TypedValue.COMPLEX_UNIT_SP, (float) (titleSize + (titleSize * 0.25)));
        subtitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, (float) (subtitleSize + (subtitleSize * 0.25)));
        content.setTextSize(TypedValue.COMPLEX_UNIT_SP, (float) (contentSize + (contentSize * 0.25)));
    }
    //Exiting multi-window, adjust size back to normal
    else{
        title.setTextSize(TypedValue.COMPLEX_UNIT_SP, titleSize);
        subtitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, subtitleSize);
        content.setTextSize(TypedValue.COMPLEX_UNIT_SP, contentSize);
    }
}

上のコードによって次のようなことができます。

  1. アクティビティーの中に、新たなonMultiWindowChangedイベント監視機能ができます
  2. すべてのUIアイテムを探せます(この場合一連のTextViewになります)
  3. SPサイズをディメンションリソースファイルから入手し、スクリーンの解像度に応じて分割し、適切なサイズにできます
  4. inMultiWindowフラッグの値を調べます
  5. もしtrueならアプリがマルチウィンドウに変化することを意味するので、UI要素のテキストが25%拡大されます
  6. もしfalseならアプリがマルチウィンドウ内で表示されていることを意味するので、ディメンションファイルに基づいてデフォルト値に再設定します。

下の画像は実画面の比較です。左はテキストが拡大されてマルチウィンドウモードに変わったアプリです。右は通常のフルスクリーンモードで表示しているアプリです。

ここではどのようなロジックを使っても大丈夫です。そして、どちらのモードに変えたときでもUIがうまく表示されるかどうかをチェックするためには最適な場所です。

アップデートされた通知機能

通知機能はAndroid 4.1(Jellybean)以降、継続的に改善されてきました。そしてAndroid Nでは、直接通知とバンドル通知が追加されました。

バンドル通知

バンドル通知は、Android Wearからスマートフォンやタブレットに至るまで適用される素晴らしい機能です。

Wearを使えば、アプリが複数の通知を送信してきたとき(例:Gmailでの受信)、各アプリではそれらの通知を全部一緒に「バンドル」するかどうかを選べます。1通ずつの通知を受ける代わりに、ユーザーは画面トップに1つの通知を受けて、直近の通知を1通ずつ順番に見られます。このおかげで画面上の乱雑さを最小限に抑えられます。

Android Nなら、開発者は通知を1つのスタックにまとめられます。次の例では、複数の通知を送信するボタンを使ったシナリオを作ってみました。

//stacked notification example
public void startStackedNotifications(View view){

    final String NotificationKey = "notification_key"; //used to group notifications together

    //create 3 notifications
    for(int i = 1; i <= 3; i++){

        //build notification
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setContentTitle("Message " + i + " title");
        builder.setContentText("Content for message number " + i + ". This is some additional long content that will get trimmed off when it's too long");
        builder.setSmallIcon(R.drawable.ic_announcement);
        builder.setGroup(NotificationKey);

        //If on the last notification, set the notification group
        if(i == 3){
            Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_person);

            NotificationCompat.Builder summaryNotification = new NotificationCompat.Builder(this);
            summaryNotification.setContentTitle("You have messages");
            summaryNotification.setSmallIcon(R.drawable.ic_announcement);
            summaryNotification.setLargeIcon(largeIcon);
            summaryNotification.setGroup(NotificationKey);
            summaryNotification.setGroupSummary(true);

            int summaryNotificationID = 400;
            NotificationManagerCompat summaryManager = NotificationManagerCompat.from(this);
            summaryManager.notify(summaryNotificationID, summaryNotification.build());
        }

         //issue the notification
        int notificationID = (300 + i);
        NotificationManagerCompat manager = NotificationManagerCompat.from(this);
        manager.notify(notificationID, builder.build());
    }
}

このコードを使うと、アクティビティーの中にButtonウィジェットを作って、クリックハンドラーをstartStackedNotifications機能に設定できます。この関数の目的は通知を作って表示することです。そのあとこの関数は、ループを使って3つの通知を作ります。それぞれの通知の中では次のようなことが起こっています。

  1. NotificationCompat.Builderクラスを使って、新たに通知ビルダーを作ってください。このクラスが、メッセージのタイトルと内容を設定します
  2. setGroupメソッドをコールして、複数の通知をまとめるための通知キーを渡します(これが重要)
  3. 最後のループで、グループ化された通知のトップで使われる通知のサマリーを作ります。これは2番目と同じプロセスですが、setGroupSummaryメソッドをtrueに設定してAndroidに対してサマリーであることを伝えます
  4. NotificationManagerCompatクラスとその中のnotifyメソッドを使って通知を送信します
  5. 4番目と同様にNotificationManagerCompatクラスの中のnotifyメソッドを使って、3つの主要な通知を発行します

下の図のように、通知が積み上げられた場合は左画面のように、分割された場合は右画面のように見えます。

ダイレクト通知返信機能

ダイレクト通知返信機能によって、ユーザーは通知を受け取ったとき速やかに返信できます。

documentation for Android N notificationsは、アプリの実行中に通知を受信する方法について、順を追って説明している優れたガイドを提供しています。通知がどのように機能するのか、そして異なるデバイスやプラットフォームでどのように取り扱われるかを知るのはとても良いことです。それには、Androidの通知に関する導入編が役に立ちます。

データセーバー

Android Nを手始めとして、グーグルはユーザーのデータ使用を最小化するためにさらなるツールやオプションを提供しています。Android Nでは、ユーザーが異なるデバイス間でデータを使えるようにできます。そのためこれまでより少ないデータしか使わずにアプリに信号を送信できます。起動中には、データ保存モードを使ってバックグラウンドでのデータ使用をブロックします。

この改善はConnectivityManager APIへのアップデートの一環です。このAPIはネットワークの現状に関する情報にアクセスするためのインターフェイスを提供するとともに、ネットワークの状態に変更があった場合に通知します。

データ保存の優先順位を検知するために、開発者は次のように新たな3つの定数が使えます。

  • RESRICT_BACKGROUND_STATUS_DISABLED:データ保存は無効です
  • RESRICT_BACKGROUND_STATUS_ENABLED:データ保存が有効で、バックデータ使用がブロックされます。開発者は前面に出るデータを最小化するべきです
  • RESRICT_BACKGROUND_STATUS_WHITELISTED:データ保存は有効ですが、アプリはホワイトリストに入れられており、バックグラウンドデータは通常の手順で処理されます。前面のデータを最小化するべきです

データ保存の優先順位の変化を検知する

ユーザーがデータ保存優先順位を変更したとき、アプリはそれに沿って適合するべきです。ConnectivityManagerは必要なときに確認できるACTION_RESTRICT_BACKGROUND_CHANGEDイベントを表示してくれます。

アクティビティーの中では、クラスによって次のプライベートフィールドが追加されます。これは主にTextViewConnectivityManagerのアイテムへのアクセスを便利にするためのものです。

private TextView tv_dataSaverStatus;
private ConnectivityManager conManager;

 protected void onCreate(Bundle savedInstanceState) {

    //find the TextView and set up our connectivity manager
    tv_dataSaverStatus = (TextView) findViewById(R.id.dataSaverStatus);
    conManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
    displayDataSaverStatus(conManager, tv_dataSaverStatus);

    //new intent filter to look for (dataSaver)
    IntentFilter filter = new IntentFilter();
    filter.addAction(conManager.ACTION_RESTRICT_BACKGROUND_CHANGED);

    //new receiver, extending broadcast receiver
    DataSaverReceiver receiver = new DataSaverReceiver();

    //register receiver for us to use
    registerReceiver(receiver, filter);
}

onCreate関数の中で、現在のデータ保存設定を表示するTextViewを探して、関数を表示するためのConnectivityManagerオブジェクトを作ってください。

インテントフィルターを作ってACTION_RESTRICT_BACKGROUND_CHANGEを適用してください。ユーザーがイベントがトリガーされたことを知りたいと、Androidに伝えます。これらの設定が変わる時に実行されるDataSaverReceiverクラスから新しいオブジェクトを作成します(以降のページで説明します)。最後に、レシーバーを登録してコントロールします。

ここに示したonCreateメソッドで、BroadcastReceiverクラスを拡張した新たなクラスが作れます。アプリが開かれたときやデータ保存設定が変更されるたびに実行される拡張ブロードキャストです。以下の例は、表示を変更する関数をコールしています。

//custom class for capturing the dataSaver broadcast
public class DataSaverReceiver extends BroadcastReceiver{

    //trigger as we receive the event
    public void onReceive(Context context, Intent intent) {
        //update the dataSaver display
        displayDataSaverStatus(conManager, tv_dataSaverStatus);
    }
}

この関数は、ユーザーがモバイルネットワークに接続しているかどうかを確認します(これはisActiveNetworkMeteredを通じて確認します)。そして接続している場合には、それがデータ保存機能によるものかどうかを確認します。

知らされた設定に基づいて、TextViewのテキストと背景色を変更します。

//writes the current status of the dataSaver setting to the UI
public void displayDataSaverStatus(ConnectivityManager conManager, TextView tv_dataSaverStatus){

     //cellular network
     String message = "";
     int color = R.color.dataDefault;
     if(conManager.isActiveNetworkMetered()){

         int restrictBackgroundStatus = conManager.getRestrictBackgroundStatus();
         if(restrictBackgroundStatus == conManager.RESTRICT_BACKGROUND_STATUS_ENABLED) {
             message = "Datasaver Status: Enabled \nNo background processing allowed while data saver enabled!";
             color = R.color.dataSaverEnable;
         }else if(restrictBackgroundStatus == conManager.RESTRICT_BACKGROUND_STATUS_DISABLED) {
             message = "Datasaver Status: Disabled \nBackground processing allowed. Continue as normal";
             color = R.color.dataSaverDisabled;
         }else if(restrictBackgroundStatus == conManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED) {
             message = "Datasaver Status: Enabled \nHowever, app is whitelisted so background data will work still.";
             color = R.color.dataSaverEnabledWhitelist;
         }
     }
     //normal network
     else{
         message = "Datasaver Status: Irrelevant \n Currently connected to a non-meters network so all good";
         color = R.color.dataDefault;
     }

     tv_dataSaverStatus.setText(message);
     tv_dataSaverStatus.setBackgroundColor(getResources().getColor(color));
 }

シンプルな例ですが、データセーバーのステータスを表示したり、変更点を探したりできるので、アプリを新しいデータ保存ポリシーに合わせられるようになりました。

Dozeモードのアップデート

Android Marshmallowはバッテリーの使用効率と使用可能時間を改善するDozeモードをリリースしました。この改善は、バックグラウンドでの同期作業やデバイスが充電中でないときのタスク、静止状態、スクリーンがオフの状態といったアクティビティー速度を下げることで実現されています。Dozeは、使っているデバイスの状態に応じて、2つのフェーズで動作します

デバイスのスクリーンがオフ、バッテリーはパワーオン、静止状態ではないとき、バッテリー使用削減の第1段階は使用電力を最小化するために、作業やバックグラウンドでの進行速度を下げることです。

1458558059AndroidN_doze1

グーグルはAndroid Nで、デバイスが静止状態になった途端、またある時間内で最初のDozeモードになった途端に起動する、よりアグレッシブなDozeモードを発表しました。2番目の状態では、さらに無効化が進みます。具体的には、GPS/WiFiスキャンやウェイクロックを無効化します。

1458558140AndroidN_Doze2

この背景にあるのは、デバイスが完全に静止状態にあるというのは、電話がデスクに置かれてまったく使されていないのと変わらない、という考え方です。Androidは、パフォーマンスを提供しながらもより多くのバッテリーをセーブするためにこうした時間を活用します。

留意点:Doze状態になっている場合でも、デバイスは定期的に起動して、アプリがタスクを実行したり、アップデートや通知機能が働いたりするようにしています。

Java 8用の諸機能

Android NはJava 8のサポートと、Java 8用の諸機能を発表しました。アプリがAndroid Nをターゲットとしているなら、Java 8用の諸機能を使って有利に開発を進められます。しかし、必ずAndroidの旧バージョンへ戻せるようにしておいてください。

Java 8用の諸機能には以下のものが含まれています。

ジャストインタイム(JIT)コンピレーション

Androidバージョン5.0のAndroid Runtime(ART)は、完全にahead-of-time(AOT)コードコンピレーションに変更されました。そしてAndroid Nでは新たなJITコンピレーションがリリースされ、アプリの最初のインストールのあとの実行やOSのアップデートが大変速くなりました。

Android for Workの改良

Android for workはAndroidを仕事に使うために作られた、グーグルの企業向けモデルです。

Android for workは、Android for Workのアプリを通じて、Android 4.0のときから使えました。Android 5.0と仕事用プロファイル(Work Profile)のリリース以来、ITマネージャーはAndroidを使った企業ソリューションの管理を支援してくれるような、従来より優れたツールやコントロール機能を手にしたのです。

Android Nに伴う改良点は以下のとおりです。

  • 作業モードの切り替え:ユーザーは仕事用プロファイルをオフにできます。たとえばバックグラウンドでの同期、通知や仕事用プロファイルアプリが再度有効化されるまでの間、一時的にオフにします
  • VPN常時オン:セキュリティーの改善のため、仕事で使うユーザーがアプリや情報にアクセスするとき、強制的にVPN経由にする機能です
  • アプリへのアクセス無効化:IT管理者は仕事用アプリへのアクセスを保留させて、企業データの安全を確保できます。アクセスはあとで有効化できます
  • モート起動:権限を持ったユーザーはデバイスをリモート起動できます。POS端末のように、常時起動しておきたいデバイスのユーザーにとっては大変役立ちます

興味がわいた人は、プレビューWebサイトにある全機能解説を参照してください。

スクリーンズーム

Androidにはズーム機能がサポートされており、ときには、ほかのアクセシビリティー機能より重要な場合があります。というのも、ユーザーはスクリーン上のテキストやほかの要素のサイズを選べるからです。Android Nは比率を変えずに、すべての要素を拡大したり縮小したりできる完全なUIを備えています。ズーム機能を使うと自分の作ったインターフェイスがどのように見えるか、テストすることをお勧めします。

最後に

Android Nには、これまでAndroidが提供してきた多くの機能に改良が加えられ、フレキシビリティーや安定性が強化されています。今回のリリースでデベロッパーにとって重要な機能は以下のとおりです。

  • マルチウィンドウ:2つ以上のアプリを並列的マルチタスク環境で使えます
  • データセーバー:モバイルデバイスユーザーの使用データを削減してバッテリーを長持ちさせます
  • バンドル通知および直接通知:より直感的でフレキシブルな通知機能によりUIを改善しています

自分のアプリで1つ1つの機能に関するテストを計画し実施し、Androidの開発ガイドラインを順守した上でアプリが動作することを確認してください。

今後数か月にわたって、グーグルはAndroid Nのさらなるアップデート版をリリースする予定で、うまくいけば2016年の第3四半期中には正式名称の発表とAndroid Nプラットフォームの公式版がリリースされます。

私は、新機能を使って自分のアプリを改善したいと思います。

(原文:What Can Developers Expect in Android N?

[翻訳:島田理彩]
[編集:Livit

Copyright © 2016, Simon Codrington All Rights Reserved.

Simon Codrington

Simon Codrington

デザイナー、開発者で、Webが大好きです。Webやデザインに関することはなんにでも興味があって、クライアントに驚くようなWebサイトを作ることに情熱を注いでいます。WordPressにフォーカスして、テーマ、プラグイン、Web Bird Digitalのチームと解決策を練っていきます。

Loading...