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

JSONArray、JSONObjectの判定

JSONArray、JSONObjectの判定

JSONファイルの中身が配列なのか単体なのか?ってこと

というわけで、パッとおもいつきでかいたのがこんなかんじ。
1文字目が [ だったらとりあえずJSONArrayだなって判断する。
その文字列がパーサ通るかは別としてね。

if(jsonStr.charAt(0) == '[') {
  //JSONArray
} else if(jsonStr.charAt(0) == '{') {
  //JSONObject
}

もっとスマートなやり方ないかなーって探してたらこういうのがあったよ

Object object = new JSONTokener(data).nextValue();
if (object instanceof JSONArray) {
  //JSONArray
} else if (object instanceof JSONObject) {
  //JSONObject
}

こっちのほうががいいかんじ

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Espressoのperform(click())とはなにか

Espressoとは、Googleが公開しているAndroid用のUIテスティングフレームワークです。

https://developer.android.com/training/testing/espresso

使い方は以下のように、onViewでViewを指定して、performで操作を行って、checkで照合します。

Sample.java
@Test
public void greeterSaysHello() {
    onView(withId(R.id.name_field)).perform(typeText("Steve"));
    onView(withId(R.id.greet_button)).perform(click());
    onView(withText("Hello Steve!")).check(matches(isDisplayed()));
}

わかりやすい仕組みなので導入は簡単なのですが、
使用頻度の高いperform(click())を使っていると

perform(click())どういう仕組みなんだ..?」

とモヤモヤしたのでperform(click())のソースコードの処理を追ってみました。
Android SDK28、EspressoのバージョンはAndroidXの3.1.1で調べています。

click()の種類

https://developer.android.com/reference/android/support/test/espresso/action/ViewActions

androidx.test.espresso.action.ViewActionsにはclickメソッドが3つあります。
サンプルコードにあるのは一番目の引数なしのものです。

  • click()
  • click(int inputDevice, int buttonState)
  • click(ViewAction rollbackAction)

APIリファレンスにはclick()は以下と同様と書いてあります。
click(InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY)
二つの引数の概要は以下となります。

InputDevice

https://developer.android.com/reference/android/view/InputDevice.html

InputDeviceはAndroid本体の定数で入力デバイスを表す定数群です。
SOURCE_の前置詞がつくものははビットフラグになっており、SOURCE_UNKNOWN他にSOURCE_KEYBOARD, SOURCE_DPAD,
SOURCE_TOUCHSCREENなど入力元を表していることがわかります。

click()ではSOURCE_UNKNOWNなので入力元を特定していないようです。

MotionEvent

https://developer.android.com/reference/android/view/MotionEvent.html

MotionEventはAndroid本体の定数で入力デバイスを表すビットフラグです。
BUTTON_の前置詞を持つものはボタン用の定数で以下7つがあります。

Key Description
BUTTON_PRIMARY Button constant: Primary button (left mouse button).
BUTTON_SECONDARY Button constant: Secondary button (right mouse button).
BUTTON_TERTIARY Button constant: Tertiary button (middle mouse button).
BUTTON_BACK Button constant: Back button pressed (mouse back button).
BUTTON_FORWARD Button constant: Forward button pressed (mouse forward button).
BUTTON_STYLUS_PRIMARY Button constant: Primary stylus button pressed.
BUTTON_STYLUS_SECONDARY Button constant: Secondary stylus button pressed.

click()ではBUTTON_PRIMARYなのでマウスの左クリックに相当する、通常のクリック処理のようです。

click()の処理

ViewActionsにあるclick()は以下の箇所になります。
各設定を指定したGeneralClickActionのインスタンスを作成し、アサートを行ったあとに返しています。

:
public static ViewAction click() {
    return actionWithAssertions(
        new GeneralClickAction(
            Tap.SINGLE,
            GeneralLocation.VISIBLE_CENTER,
            Press.FINGER,
            InputDevice.SOURCE_UNKNOWN,
            MotionEvent.BUTTON_PRIMARY));
}
:

GeneralClickActionの引数

第1引数: Tap

以下3つがあります。

Key Description
SINGLE シングルタップ
LONG ロングタップ
DOUBLE ダブルタップ

定数ではなくEnumです。Tapperをimplementし、
それぞれperform時の処理が書かれています。

Tap.java
public enum Tap implements Tapper {
  SINGLE {
    @Override
    public Tapper.Status sendTap(
        UiController uiController, float[] coordinates, float[] precision) {
      return sendTap(uiController, coordinates, precision, 0, 0);
    }
    :

第2引数: GeneralLocation

Key Description
TOP_LEFT Viewの左上
TOP_CENTER Viewの上中央
TOP_RIGHT Viewの右上
CENTER_LEFT Viewの左中央
CENTER Viewの中央
CENTER_RIGHT Viewの右中央
BOTTOM_LEFT Viewの左下
BOTTOM_CENTER Viewの下中央
BOTTOM_RIGHT Viewの右下
VISIBLE_CENTER 見える範囲のViewの中央

こちらも定数ではなくEnumです。CoordinatesProviderをimplementしています。
click()に使用されているVISIBLE_CENTERだけ見える範囲と特殊ですが基本的にはViewの座標を取得できるようになっています。

GeneralLocation.java
public enum GeneralLocation implements CoordinatesProvider {
  TOP_LEFT {
    @Override
    public float[] calculateCoordinates(View view) {
      return getCoordinates(view, Position.BEGIN, Position.BEGIN);
    }
  },
  :

第3引数: Press

Key Size (mm) Comment
PINPOINT 1x1
FINGER 16x16 average width of the index finger is 16 – 20 mm.
THUMB 25x25 average width of an adult thumb is 25 mm (1 inch).

同じくEnumです。クリックエリアを返すためのPrecisionDescriberをimplementしています。
click()ではFINGERなので16x16mmのタップとして認識されるようです。
上を見るに親指も選択できます。
16mmは若干大きすぎる気がします..。

public enum Press implements PrecisionDescriber {
  PINPOINT {
    @Override
    public float[] describePrecision() {
      float[] pinpoint = {1f, 1f};
      return pinpoint;
    }
  },
  :

第4引数、第5引数は上述なので省略。
次はGeneralClickActionの処理を追ってみます。

perform(click())の処理

前編 - クリック処理の概要

https://developer.android.com/reference/android/support/test/espresso/ViewAction
https://developer.android.com/reference/android/support/test/espresso/action/GeneralClickAction.html

GeneralClickActionのperform処理のソースコードには
RPGの置き手紙イベントのような大きめのコメントがあります。

スクリーンショット 2019-03-02 13.13.20.png

コメント部分を訳してみました。

ネイティブイベントの注入はかなり面倒なプロセスです。
「タップ」は実際には、システムに2つ別々に注入する必要があるモーションイベントです。
注入はテスト対象のアプリからAndroidシステムサーバーにRPC呼び出しを行います。
システムサーバーはどのウィンドウ層にイベントを配信するかを決定し、そのウィンドウ層にRPCを作成し、
そのウィンドウレイヤーはイベントを正しいUI要素、アクティビティ、またはウィンドウオブジェクトに配信します。 
今回はダウン・アップのためにこれを2回繰り返します。
あっ、ダウンイベントはイベントが長押しか短押しかどうかを検出するためにタイマーをトリガします。 
アップイベントを受信した瞬間にタイマーが削除されます。
(注:eventTimeが将来になる可能性はほとんどのモーションイベントプロセッサでは完全に無視されます)

ふぅ。

この結果として、通常のタップを実行したい場合に、
何らかの理由でタップのアップイベント(後半)が長押しタイムアウト(システム負荷による)の後に配信され、
長押しの動作が表示されることがあります。(例:コンテキストメニューの表示)
これを優雅に回避または処理する方法はありません。 
また、長押しの動作はアプリ/ウィジェット固有のものです。 
短押しとは異なる長押しの動作がある場合は、実行時に長押しの効果を元に戻す「'RollBack' ViewAction」を渡すことができます。

要するにclick()では指をつける-はなすの2つのイベントをすばやく発行することで
「クリック操作」をしているとのこと。

以下3つのコードから、
MotionEvent#obtainでAndroidのモーションイベントを作成し、
UiController#injectMotionEvent でイベントを注入している事がわかります。

  • 1. androidx.test.espresso.action.GeneralClickAction
  • 2. androidx.test.espresso.action.Tap
  • 3. androidx.test.espresso.action.MotionEvents

スクリーンショット 2019-03-05 7.31.35.png

呼び出し階層をまとめると以下のようになります。
下記の中でMotionEventがAndroidSDKの処理。その他はEspressoの処理となります。

- GeneralClickAction#perform
  - Tap#sendSingleTap
      - MotionEvents#sendDown
        - MotionEvent#obtain //ダウンイベントの作成
        - UiController#injectMotionEvent //ダウンイベントの注入
      - MotionEvents#sendUp
        - MotionEvent#obtain //アップイベントの作成
        - UiController#injectMotionEvent //アップイベントの注入

ここまででclick()を実現するための要素がわかりました。

後編 - イベントの注入方法

実際にinjectMotionEvent部分でどのようにイベントを注入しているか見ていきます。

Espressoは内部でDaggerを使用しており、UiControllerはインターフェイスで外から注入する形となっています。
そのためinjectMotionEvent呼び出し以降のコードは少し追いづらくなっています。
以下の順でソースコードを追いました。

  • 1. androidx.test.espresso.base.UiControllerModule
  • 2. androidx.test.espresso.base.UiControllerImpl
  • 3. androidx.test.espresso.Espresso
  • 4. androidx.test.espresso.BaseLayerComponent (classes.jar)
  • 5. androidx.test.espresso.GraphHolder (classes.jar)
  • 6. androidx.test.espresso.base.BaseLayerModule
  • 7. androidx.test.espresso.base.InputManagerEventInjectionStrategy

上記から実処理がある BaseLayerModuleInputManagerEventInjectionStrategy に絞って説明します。

BaseLayerModule

以下はBaseLayerModuleで、EventInjectorを提供しているprovideEventInjectorメソッドを抜粋したものです。
このクラスはDaggerのModuleに属するため、エントリポイントのEspressoクラスからBaseLayerComponent経由で呼ばれます。
MotionEventをinjectするためのEventInjectorに必要なEventInjectionStrategyを作成しています。

SDK Version16以上ではInputManager、7から15ではWindowManagerを使用する処理になっています。

BaseLayerModule.java
:
  @Provides
  @Singleton
  public EventInjector provideEventInjector() {
    // On API 16 and above, android uses input manager to inject events. On API < 16,
    // they use Window Manager. So we need to create our InjectionStrategy depending on the api
    // level. Instrumentation does not check if the event presses went through by checking the
    // boolean return value of injectInputEvent, which is why we created this class to better
    // handle lost/dropped press events. Instrumentation cannot be used as a fallback strategy,
    // since this will be executed on the main thread.
    int sdkVersion = Build.VERSION.SDK_INT;
    EventInjectionStrategy injectionStrategy = null;
    if (sdkVersion >= 16) { // Use InputManager for API level 16 and up.
      InputManagerEventInjectionStrategy strategy = new InputManagerEventInjectionStrategy();
      strategy.initialize();
      injectionStrategy = strategy;
    } else if (sdkVersion >= 7) {
      // else Use WindowManager for API level 15 through 7.
      WindowManagerEventInjectionStrategy strategy = new WindowManagerEventInjectionStrategy();
      strategy.initialize();
      injectionStrategy = strategy;
    } else {
      throw new RuntimeException(
          "API Level 6 and below is not supported. You are running: " + sdkVersion);
    }
    return new EventInjector(injectionStrategy);
  }
:

昨今はSDK Version16以上が一般的なので
InputManagerEventInjectionStrategyの処理を追います。

InputManagerEventInjectionStrategy

このクラスに実際のイベントを実行する処理が書いてあります。
以下のメソッドを抜粋します。

  • initialize
  • innerInjectMotionEvent

InputManagerEventInjectionStrategy#initialize

InputManagerEventInjectionStrategyの初期化処理です。
InputManagerの非公開メソッドである
android.hardware.input.InputManager#injectInputEvent
をリフレクションにより呼び出せるようにしています。

:
  void initialize() {
    if (initComplete) {
      return;
    }

    try {
      Log.d(TAG, "Creating injection strategy with input manager.");

      // Get the InputManager class object and initialize if necessary.
      Class<?> inputManagerClassObject = Class.forName("android.hardware.input.InputManager");
      Method getInstanceMethod = inputManagerClassObject.getDeclaredMethod("getInstance");
      getInstanceMethod.setAccessible(true);

      instanceInputManagerObject = getInstanceMethod.invoke(inputManagerClassObject);

      injectInputEventMethod =
          instanceInputManagerObject
              .getClass()
              .getDeclaredMethod("injectInputEvent", InputEvent.class, Integer.TYPE);
      injectInputEventMethod.setAccessible(true);

      // Setting event mode to INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH to ensure
      // that we've dispatched the event and any side effects its had on the view hierarchy
      // have occurred.
      Field motionEventModeField =
          inputManagerClassObject.getField("INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH");
      motionEventModeField.setAccessible(true);
      syncEventMode = motionEventModeField.getInt(inputManagerClassObject);

      if (Build.VERSION.SDK_INT >= 28) {
        // Starting from android P it is not allowed to access this field with reflection, hardcoded
        // this value as workaround.
        asyncEventMode = 0;
      } else {
        Field asyncMotionEventModeField =
            inputManagerClassObject.getField("INJECT_INPUT_EVENT_MODE_ASYNC");
        asyncMotionEventModeField.setAccessible(true);
        asyncEventMode = asyncMotionEventModeField.getInt(inputManagerClassObject);
      }

      setSourceMotionMethod = MotionEvent.class.getDeclaredMethod("setSource", Integer.TYPE);
      initComplete = true;
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    } catch (NoSuchFieldException e) {
      throw new RuntimeException(e);
    }
  }
:

InputManagerEventInjectionStrategy#innerInjectMotionEvent

実際にモーションイベントを注入しているメソッドinnerInjectMotionEventが以下です。

以下箇所で、initialize()で準備したInputManager#injectInputEvent
の実行を確認することができました。

injectInputEventMethod.invoke(instanceInputManagerObject, motionEvent, eventMode);

# ちなみにisFromTouchpadInGlassDeviceはGoogleグラスの判定です(!)

InputManagerEventInjectionStrategy.java
:
private boolean innerInjectMotionEvent(MotionEvent motionEvent, boolean shouldRetry, boolean sync)
      throws InjectEventSecurityException {
    try {
      // Need to set the event source to touch screen, otherwise the input can be ignored even
      // though injecting it would be successful.
      // TODO: proper handling of events from a trackball (SOURCE_TRACKBALL) and joystick.
      if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0
          && !isFromTouchpadInGlassDevice(motionEvent)) {
        // Need to do runtime invocation of setSource because it was not added until 2.3_r1.
        setSourceMotionMethod.invoke(motionEvent, InputDevice.SOURCE_TOUCHSCREEN);
      }
      int eventMode = sync ? syncEventMode : asyncEventMode;
      return (Boolean)
          injectInputEventMethod.invoke(instanceInputManagerObject, motionEvent, eventMode);
    } catch (IllegalAccessException e) {
      : //例外処理が長く続くので省略
}
:

上記処理がUiController#injectMotionEvent呼び出し時の処理となり
perform(click())が実現されるようです。

まとめ - Espressoのperform(click())とは

どういうものなのか

以下の5つの条件に当てはまる操作

  • 入力デバイスについては特定しない (InputDevice.SOURCE_UNKNOWN)
  • マウスの左クリックに相当するボタンを押すアクション (MotionEvent.BUTTON_PRIMARY)
  • ロングタップやダブルタップではい (Tap.SINGLE)
  • 見える場所の中央座標を対象としている (GeneralLocation.VISIBLE_CENTER)
  • 範囲は16x16mmの人差し指相当 (Press.FINGER)

どういう処理なのか

  • click() ではGeneralClickActionを作成しアサートしている
  • perform() 時にGeneralClickActionからMotionEvent#obtainでイベント作成し本体アプリに注入している
    • DOWNイベントとUPイベントの2つのイベントを発行している
  • 注入方法はandroid.hardware.input.InputManager#injectInputEvent のリフレクション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】スワイプ、フリックで分岐するSafari風スクロール【C#】

【Unity】スワイプ、フリックで分岐するSafari風スクロール【C#】

はじめに

スワイプした分スクロールできて、かつフリックすると速度をもってスクロールできる、iOS標準ブラウザのSafari風のUXのスクロール方法を実装したい!

ついでに2本指での拡大と縮小も実装しました。(なぜ?)
応用時はクランプなど用いてスクロール範囲を制御してください。

荒い実装ではありますが、そこそこ良い動きになったので備忘録かつ共有。

開発環境

OS: MacOS Mojave
開発環境: Unity 2018.2.14f1 personal
開発言語: C#

ソースコードとその説明

SwipeManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SwipeManager : MonoBehaviour {
    //フリック時のスピード
    private float x_speed = 0;
    private float y_speed = 0;

    //カメラ拡大縮小時のスピード(実装環境によって要調整)
    private float cameraSpeed = 0.4f;

    //初期位置
    private Vector2 startPos;

    //最初のタップからの経過時間
    private float duration = 0;

    //オブジェクトの移動比率を操作する変数(実装環境によって要調整)
    private float moveRatio = 0.5f;

    //スワイプするオブジェクト(この場合スクロールビューのコンテンツ)の外部参照
    public GameObject obj;
    //拡大縮小用カメラの外部参照
    public Camera cam;

    void Update()
    {
        //経過時間の計算
        duration += Time.deltaTime; 

        //一本指での操作(スワイプ、フリック)
        if(Input.touchCount == 1){
            //タップ情報の取得
            Touch touch = Input.GetTouch(0);

            //タップ状態の分岐
            switch(touch.phase){
                //タップ開始
                case TouchPhase.Began:
                    //タッチ開始座標、時間取得
                    this.startPos = Input.mousePosition;
                    duration = 0;
                    break;

                //タップ中、指が動いている
                case TouchPhase.Moved:

                    //オブジェクト移動(スワイプ)
                    Vector3 nowPosi = obj.transform.localPosition;
                    nowPosi.x = nowPosi.x - touch.deltaPosition.x*moveRatio;
                    nowPosi.y = nowPosi.y - touch.deltaPosition.y*moveRatio;
                    obj.transform.localPosition = nowPosi;

                    break;

                //タップ終了
                case TouchPhase.Ended:

                    //タップ終了位置取得
                    Vector2 endPos = Input.mousePosition;

                    //触れていた秒数でフリックとスワイプ分岐
                    if(duration <= 0.5){
                        float x_flickLength = endPos.x - this.startPos.x;
                        float y_flickLength = endPos.y - this.startPos.y;

                        // フリックの長さを速度に変換する
                        this.x_speed = x_flickLength / 500.0f;
                        this.y_speed = y_flickLength / 500.0f;
                    }else{

                        //フリック判定じゃない場合はcameraのTranslate速度を0にする
                        this.x_speed = 0;
                        this.y_speed = 0;
                    }
                    break;
            }
        }

        //オブジェクト移動(フリック)
        obj.transform.Translate(this.x_speed, this.y_speed, 0);

        //毎フレーム減速させる
        this.x_speed *= 0.8f;
        this.y_speed *= 0.8f;

        //2本指での操作(ピンチイン、アウト)
        if (Input.touchCount == 2){

            // 両方のタップ情報を取得
            Touch touchZero = Input.GetTouch(0);
            Touch touchOne = Input.GetTouch(1);

            // 前フレームでのタップ位置
            Vector2 touchZeroPrePos = touchZero.position - touchZero.deltaPosition;
            Vector2 touchOnePrePos = touchOne.position - touchOne.deltaPosition;

            // 各フレームのタッチ間の距離
            float preTouchDeltaMag = (touchZeroPrePos - touchOnePrePos).magnitude;
            float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;

            //各フレーム間の距離の差
            float deltaMagnitudeDif = preTouchDeltaMag - touchDeltaMag;
            //タッチ間の距離の変化からカメラの平行投影サイズを変更
            cam.orthographicSize += deltaMagnitudeDif * cameraSpeed;

            // 平行投影サイズは0以上になるようにする(クランプの範囲は要調整)
            cam.orthographicSize = Mathf.Clamp(cam.orthographicSize, 0f , 2.0f);
        }        
    }
}

終わりに

TouchScriptなどの便利なアセットを使用するとおそらく早かったのですが、スクリプトで実装してもそこまで時間がかからないのと、自分でスクリプトを書いた方が拡張性に富むと思ったのでこのような実装をしました。どなたかの助けになれば幸いです。

参考文献

「フリックとスワイプの違い」
https://enjoy.sso.biglobe.ne.jp/archives/swipe_flick/
「Unity C#で時間の取得」
http://webbeginner.hatenablog.com/entry/2015/09/04/053623
「ピンチによる拡大」
https://unity3d.com/jp/learn/tutorials/topics/mobile-touch/pinch-zoom
「【Unity2D】スワイプでオブジェクトを上下左右自由自在に動かす方法」
https://miyagame.net/swipe/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

flutter doctor で [✗] Android toolchain - develop for Android devices の対処

~/Library/Android/sdk/tools/bin/sdkmanager --licenses で all y をやったら動いた.

android つらい

参考

https://github.com/flutter/flutter/issues/16280#issuecomment-380447597

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Debian noroot 環境に plank を導入する

はじめに

Debian noroot とは、 Android OS 上において root 権限を取ることなく Debian 環境を構築するためのアプリケーションです。

CPU の性能とメモリ容量が潤沢にある Android OS 端末であれば、 Debian noroot の導入によって Android OS 端末上で非常に軽快な Debian 環境を実現することが出来ます。

Debianのデスクトップ環境において、 Dock ランチャーソフトウェアを導入する事は、通常頻繁に使用するソフトウェアの起動を容易にする等、デスクトップ環境の利便性の向上に関して非常に有用となります。

ここで、Debianのデスクトップ環境において、軽量な Dock ランチャーソフトウェアの1つとして plank が挙げられます。

しかし、 Debian Jessie をベースにした Debian noroot 環境においては、 plank を導入するための適切な deb パッケージが存在しません。

そこで、 plank をソースコードからビルドし、MATE 環境の起動時において自動起動を行うように設定した所、Debian noroot 環境上における各種デスクトップ環境において plank を使用する事が出来るようになりました。

本稿では、 Debian noroot 環境における plank の導入手法について述べます。

本稿では最初に、 "plank の導入" の章において、軽量な Dock ランチャーソフトウェアである plankDebian noroot 環境への導入手法について述べます。

次に、 "デスクトップ環境への導入" の章において、前章で導入した plankDebian noroot 環境上の各種デスクトップ環境へ導入する手法について述べます。

そして、 "plank の問題点" の章において、現時点での Debian noroot 環境に導入した plank の問題点について述べます。

最後に、 "結論" の章において、本稿の結論について述べます。

plank の導入

本章においては、最初に、 "plank に依存するパッケージの導入" の節において、 Debian noroot 環境plank を導入する際に、予め導入するべき plank の依存パッケージの導入について述べます。

次に、 "plank の実行ファイル等のビルド" の節において、 plank 本体のソースコードの取得と、 Debian noroot 環境上で plank 本体のソースコードから plank の実行ファイル等のビルドを行う手法について述べます。

最後に、 "plank のインストール" の節において、前節においてソースコードからビルドした plank の実行ファイル等を Debian noroot 環境上にインストールする手法について述べます。

plank に依存するパッケージの導入

まず最初に、 Debian noroot 環境において plank をビルドする上で、 plank に依存する Debian パッケージの導入を以下の通りに行います。

$ sudo apt-get install automake gnome-common intltool pkg-config valac libbamf3-dev libdbusmenu-gtk3-dev libgdk-pixbuf2.0-dev libgee-dev libglib2.0-dev libgtk-3-dev libwnck-3-dev libx11-dev libgee-0.8-dev

また、上記に加えて、パッケージ xstow の導入も以下の通りに併せて行います。これは、ソースコードからビルドした plank の実行ファイル等をディレクトリ /usr/local 以下に導入する際に plank の実行ファイル等の管理を容易にする為です。

$ sudo apt-get install xstow

plank の実行ファイル等のビルド

次に、 plank 本体のソースコードをplank の公式ページより取得します。なお本稿では、 plank のバージョン 0.11.4 のソースコードである plank-0.11.4.tar.xz を以下の通りに取得します。

plank のソースコードの取得が完了した後は、 plank のソースコードである plank-0.11.4.tar.xz を適切なディレクトリに展開します。

  $ cd /path/to/build   # (ここに、 /path/to/build は、 plank の実行ファイル等をビルドするための作業ディレクトリ。)
  $ wget https://launchpad.net/plank/1.0/0.11.4/+download/plank-0.11.4.tar.xz
  $ tar -Jxvf plank-0.11.4.tar.xz

そして、カレントディレクトリを ./plank-0.11.4 に移動して、以下の通りにソースコードから実行ファイル等をビルドします。

ここで、plank のインストール先となるディレクトリは、前節で述べた xstow によって plank の管理を容易にする為に /usr/local/opt/plank-0.11.4 とします。

  $ cd ./plank-0.11.4
  $ ./configure --prefix=/usr/local/opt/plank-0.11.4 --disable-silent-rules
  $ make

plank のインストール

前節によって、正常に plank の実行ファイル等のビルドが完了した後は、コマンド make install を実行して、 plank の実行ファイル等を /usr/local/opt/plank-0.11.4 にインストールします。

  $ make install

そして、カレントディレクトリを /usr/local/opt に移動して以下のコマンドを実行します。コマンド xstow -v ./plank-0.11.4 の実行により、 plank の実行ファイル等のシンボリックリンクがディレクトリ /usr/local 以下に張られます。

  $ cd /usr/local/opt
  $ xstow -v ./plank-0.11.4
...(略)...
  $

最後に、以下の通りに plank コマンドを実行して、正常に plank が起動する事を確認します。

$ plank

なお、もし plank のソースコードをコンパイルする事が煩わしい場合や何らかの問題で困難である場合は、次に示す URL に置かれているシェルスクリプトを用いて、以下のように plank のソースコードのダウンロード及びビルドとインストールを自動的に行っても構いません。

  $ bash -c "$(wget -q -O - https://git.io/debian-noroot-build-plank.sh)"
  ...(略)...
  $ plank    # (plank の動作確認を行う。)

デスクトップ環境への導入

本章では、前章で述べた手法でインストールした plankDebian noroot 環境上の各種デスクトップ環境の起動時において自動的に起動するように設定する手法について述べます。

まず、 "MATE への導入" の節において、 Linux MINT において採用されているデスクトップ環境である MATEplank を導入する手法について述べます。

次に、 "LXDE への導入" の節において、軽量なデスクトップ環境の1つである LXDEplank を導入する手法について述べます。

最後に、 "xfce4 への導入" の節において、 Debian noroot 環境におけるデフォルトのデスクトップ環境である xfce4plank を導入する手法について述べます。

なお、以降で述べる設定手法で示す各画像において表示されている plank については、 plank の動作確認時において plank の各種設定を修正したものであり、デフォルトの plank の表示とは若干異なることに留意して下さい。

MATE への導入

まず、 plank をデスクトップ環境 MATE において自動的に起動するように設定する手法について述べます。

  • まず最初に、以下の画像のようにして [設定] のメニューを開き、 [コントロールセンター] の項目を選択します。 (Setting of MATE)
  • 次に、以下の画像のように [コントロールセンター] のパネルにおいて、 [自動起動するアプリ] のボタンをクリックします。 (ControllCenterof MATE)
  • そして、以下の画像のように [自動起動するアプリの設定] のウィンドウにおいて、 [追加]のボタンをクリックします。 (Auto start app of MATE)
  • その後、以下の画像のように [自動起動するプログラムの編集] のウィンドウにおいて、 plank の自動起動についての各項目を指定します。ここで、plank の実行ファイルの絶対パスは、必ず /usr/local/bin/plank とする必要があります。 (Add Plank to Auto start app)

以上の設定が完了した後、 Debian noroot 環境を再起動して、以下の画像の通りに plank が自動的に起動する事を確認します。

(Plank in MATE)

LXDE への導入

次に、 plank を軽量なデスクトップ環境 LXDE において自動的に起動するように設定する手法について述べます。

  • まず最初に、以下の画像のようにして [設定] のメニューを開き、 [LXSession のデフォルトのアプリケーション] の項目を選択します。 (LXSession default app of LXDE)
  • ここで、 [LXSession Configuration] のウィンドウが開くので [Autostart] の項目を選択します。
    (LXSession Configuration of LXDE)
  • すると、以下の画像のように Manual Autostart Application のリストの末尾に [追加] のボタンと、その右隣にてテキストエリアが表示されているので、ここでテキストエリア内に @plank と入力して [追加] ボタンを押します。 (LXSession Configuration of LXDE)
  • [追加] ボタンを押した後、以下の画像のように Manual Autostart Application のリストの末尾に @plank が登録されていることを確認します。 (LXSession Configuration of LXDE)

以上の設定が完了した後、 Debian noroot 環境を再起動して、 plank が自動的に起動する事を確認します。

fxce4 への導入

そして、plankDebian noroot 環境のデフォルトのデスクトップ環境である [fxce4 環境][FXCE]において自動的に起動するように設定する手法について述べます。

  • まず最初に、以下の画像のようにして [設定] のメニューを開き、 [セッションと起動] の項目を選択します。 (Setting Manager of fxce4)
  • そして、以下の画像のように [セッションと起動] のウィンドウにおいて、[自動開始アプリケーション] の項目を選択します。 (Session and Start of fxce4)
  • すると、以下の画像のように自動起動するアプリケーションの一覧が表示されます。ここで、未だ plank が一覧に登録されていない場合は [追加] ボタンを押し、登録されている場合は plank が登録されている行をクリックして [編集] ボタンを押します。 (Session and Start of fxce4)
  • その後、以下の画像のように [アプリケーションの編集] のウィンドウにおいて、 plank の自動起動についての各項目を指定します。ここで、plank の実行ファイルの絶対パスは、必ず /usr/local/bin/plank とする必要があります。 (Edit app of fxce4)

以上の設定が完了した後、 Debian noroot 環境を再起動して、 plank が自動的に起動する事を確認します。

plank に関する問題点

本章において、 plank について、現時点で判明してい問題点について述べます。

まず、何らかの理由で plank のプロセスに SIGKILL 等の SIGTERM でないシグナルが送られる事により異常終了した場合等において、再度 plank を起動した場合に以下のエラーメッセージを出力して plank が起動しない場合があります。

  $ plank
  ...(略)...
  [ERROR 01:22:33.123456] [Utils:42] GSettingsSchema 'net.launchpad.plank' not found
  Trace/breakpoint trap
  $

この場合は以下のコマンドの実行により、 plank が起動しない問題を回避します。

  $ sudo glib-compile-schemas /usr/local/share/glib-2.0/schemas

上記のコマンドの実行によっても、問題が回避できない場合は、 plank のビルド及びインストールを再度行って下さい。

結論

本稿では、 "plank の導入" の章において、 Debian noroot 環境上で、 plank のソースコードから、実行ファイル等をビルドし、ディレクトリ /usr/local 以下にインストールする手法について示しました。

そして、 "デスクトップ環境への導入" の章において、 "plank の導入" の章でインストールした plank を、 Debian noroot 環境上で動作する各種デスクトップ環境の起動時に自動的に起動させるように設定を行う手法について示しました。

以上で示した手法によって Debian noroot 環境plank を導入することにより、 Debian noroot 環境の起動時に軽量な Dock ランチャーソフトウェアである plank が自動的に起動して、高頻度で使用するソフトウェアの起動が容易になることが示されました。

また、デスクトップ環境 MATE が導入された Debian noroot 環境において、 plank を左端の中央に配置する設定を行うことにより、 Unity ライクなデスクトップ環境の外観が実現でき、多くの PC 端末と同様の快適性を持つデスクトップ環境が実現できることが判りました。

謝辞

本稿の記述に当たって、 Android OS 端末上で非常に軽快な Debian 環境を実現することを可能にした Debian noroot 環境の開発者である pelya 氏に心より感謝致します。

なお、 plank 本体をソースコードから実行ファイル等へビルドし、インストールを行う手法に関しては、techbar 氏による "Installing Plank Dock on Debian Jessie | Techbear" を参考にしました。 techbar 氏に心より感謝致します。

そして最後に、 Debian noroot 環境Android OS 及び Debian 環境の全ての事に関わる全ての皆様に心より感謝致します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む