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

android.os.HandlerのpostとpostDelayedを使った際のQueueに積まれる順番について

AndroidでHandlerのpost(Runnable r)を複数回コールした場合は、postした順にQueueに積まれて、実行されます。post(Runnable r)postDelayed(Runnable r, long delayMillis)を使った場合にどうQueueに積まれるか調べてみました。

ソースコードを見ると、postとpostDelayedの内部では共にsendMessageDelayed(Message msg, long delayMillis)が呼ばれていました。

Handler.java
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

sendMessageDelayedの中ではsendMessageAtTime(Message msg, long uptimeMillis)が呼ばれており、引数のuptimeMillisにはSystemClock.uptimeMillis() + delayMillisが設定されています。

Handler.java
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

つまり、postDelayed(Runnable r, long delayMillis)はdelayMillis ミリ秒後にpost(Runnable r)をするのと同じことになります。
例えば、下記のコードを実行すると、

Sample.kt
        val handler = Handler()

        handler.postDelayed( Runnable { Log.d(TAG, "Runnable 1") }, 100)
        Log.d(TAG, "1 postDelayed")

        handler.post( Runnable {
            Log.d(TAG, "Runnable 2-1")
            Thread.sleep(100)
            Log.d(TAG, "Runnable 2-2")
        })
        Log.d(TAG, "2 post")

        handler.post( Runnable {
            Log.d(TAG, "Runnable 3-1")
            Thread.sleep(200)
            Log.d(TAG, "Runnable 3-2")
        })
        Log.d(TAG, "3 post")

ログは下記のようになります。

2020-02-08 20:08:07.385 22994-22994/Sample: 1 postDelayed
2020-02-08 20:08:07.385 22994-22994/Sample: 2 post
2020-02-08 20:08:07.385 22994-22994/Sample: 3 post
2020-02-08 20:08:07.490 22994-22994/Sample: Runnable 2-1
2020-02-08 20:08:07.591 22994-22994/Sample: Runnable 2-2
2020-02-08 20:08:07.592 22994-22994/Sample: Runnable 3-1
2020-02-08 20:08:07.793 22994-22994/Sample: Runnable 3-2
2020-02-08 20:08:07.950 22994-22994/Sample: Runnable 1

Runnable 1Runnable 3の後に実行されます。
また、postDelayedはdelayMillisを100msにしていますが、実際に実行されるのは565ms後です。
Runnable 1が、Runnable 3の前または、100ms後に実行されることを想定していると、思わぬ不具合を生むことになるので注意が必要です。

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

React Nativeでスマートフォンのカレンダーのデータを取得

調査内容

Android/iOS共にNativeコードではスマートフォンのカレンダーデータにアクセスする事ができますが、React Nativeでアクセスする方法について調査しました。今回はAndroidでのみ動作確認を行いましたが、iOSでも動作可能と思われます。
スクリーンショット 2020-02-08 17.40.18.png

設定手順

以下のモジュールを利用します。
https://github.com/wmcmahan/react-native-calendar-events

$ npm install --save react-native-calendar-events
$ react-native link

今回はAndroidで動作確認をしますので、AndroidのNative部分をいくつか修正します。

android/settings.gradleに以下を追加

include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')

AndroidManifest.xmlに以下を追加

    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />

MainActivity.javaに以下を追加

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        CalendarEventsPackage.onRequestPermissionsResult(requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

React Nativeのアプリケーションを記述

App.jsからMainScreen.jsを呼び出す形に変更して、MainScreen.jsに3箇所押せるようにTouchableViewを利用します。

/* App.js */
import React from 'react';
import { StyleSheet, Button, View, } from 'react-native';
import MainScreen from './src/screens/MainScreen'

const App: () => React$Node = () => {
  return (
    <View>
      <MainScreen />
    </View>
  );
};

const styles = StyleSheet.create({
});

export default App;
/* MainScreen.js */
class MainScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <TouchableHighlight style={styles.listItem} onPress={this.authorizeEventStore}>
          <Text style={styles.title}>パーミッションリクエスト</Text>
        </TouchableHighlight>
        <TouchableHighlight style={styles.listItem} onPress={this.findCalendars}>
          <Text style={styles.title}>カレンダー取得</Text>
        </TouchableHighlight>
        <TouchableHighlight style={styles.listItem} onPress={this.fetchEvents}>
          <Text style={styles.title}>イベント取得</Text>
        </TouchableHighlight>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    width: '100%',
    flex: 1,
  },
  listItem: {
    padding: 32,
    borderBottomWidth: 1,
    borderBottomColor: '#ddd',
    backgroundColor: '#fff',
    justifyContent: 'center',
  },
  title: {
    fontSize: 20,
  },
});

こんな感じで表示されます。
スクリーンショット 2020-02-08 17.51.04.png

コードの記述。

それぞれに応じた関数を以下のように記述します。Androidではtarget SDK 23以降はパーミッションの確認が必要なので、RNCalendarEvents.authorizeEventStore()でパーミッションを取得します。

  authorizeEventStore() {
    console.log("confirmAuthorizationStatus");
    RNCalendarEvents.authorizeEventStore()
      .then((status) => {
        console.log(status);
      })
      .catch((error) => {
        console.log(error);
      });
  }

パーミッションリクエストを押すと以下のようなパーミッションの確認がOSから表示されます。
スクリーンショット 2020-02-08 17.53.06.png

後はCalendarの取得とイベントの取得を以下のように記述します。RNCalendarEvents.fetchAllEvents(startDate, endDate, calendars)ではcalendarsでカレンダーIDを指定すれば良いですが、今回は簡略化のためにハードコーディングしています。ボタンを押すと取得された結果がConsole上で出力されます。

  findCalendars() {
    RNCalendarEvents.findCalendars()
      .then((list) => {
        console.log(list);
      })
      .catch((error) => {
        console.log(error);
      });
  }

  fetchEvents() {
    RNCalendarEvents.fetchAllEvents('2019-01-01T17:24:00.000Z', '2019-12-31T17:24:00.000Z,'[2])
      .then((list) => {
        console.log(list);
      })
      .catch((error) => {
        console.log(error);
      })
  }

以上、react-native-calendar-eventsを使う事で簡単にスマートフォンのカレンダーにアクセスする事が可能となりました。

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

AndroidでBluetooth関係のAPIを使ってPAPERANGに接続してみる

はじめに

正月に秋葉原行ってじゃんぱらでPAPERANGを購入。
で、PAPERANGのAPIないかなと探したけど、Cordovaのプラグインしか見つからなかったので、プラグインのソースをAndroidStudioで普通にアプリに使えないか試してみる。
もし、もっと良い方法を知ってる方がいたらコメントください。

うまく使えたら、Androidの会で何か作るときや、ハッカソンなんかで使ってみたい。

CordovaのPAPERANG pluginを見つけて、その中のjarを使ってできないか試したが、なんか、メーカーからIDとかもらわないとできそうもないのであきらめ、別の方法を模索。
Androidの会 浜松支部の定例会で、話をしていたところ、よさげなページを見つたので、そこを参考に紙送りができるところまで確認できた。

良さげなページは以下のサイト
M5StickC(ESP32)からBluetooth小型ポータブルレシートプリンタ「PAPERANG」を制御する
Bluetooth小型ポータブルレシートプリンタ「PAPERANG」
miaomiaoji-tool
ちゃんと使っていくためには、miaomiaoji-toolのコードを確認していかないといけない。

AndroidManifest.xml

こんな感じ

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="krohigewagma.jp.paperangsample">
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

コード

とりあえずのお試し適当コード。
接続履歴を使って、PAPERANGを探して接続するダサいコード。
本当は、Bluetoothで接続されているデバイスの中から選ばないといけない
ボタンを押すと、紙送りされます。

public class MainActivity extends AppCompatActivity {

    // CRC
    private static byte[] crc = new byte[]{
            (byte)0x02, (byte)0x18, (byte)0x00, (byte)0x04,
            (byte)0x00, (byte)0x78, (byte)0x7A, (byte)0xCE,
            (byte)0x33, (byte)0x2C, (byte)0x89, (byte)0x80,
            (byte)0xF0, (byte)0x03
    };

    // 紙送り
    byte[] data = new byte[]{
            (byte)0x02,                                     //
            (byte)0x1a, (byte)0x00,                         // 制御コード
            (byte)0x02, (byte)0x00,                         // データ長さ
            (byte)',' , (byte)0x01,                         // 紙送り量
            (byte)0x8b, (byte)'V', (byte)'#', (byte)'T',    // CRC
            (byte)0x03                                      //
    };

    private static String SPP = "00001101-0000-1000-8000-00805F9B34FB";

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Button btn = findViewById(R.id.btnInit);
        btn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Context appContext = getApplicationContext();
/*
                // Cordovaのプラグインのjarを使ってやったコード。
                // ダメだったけど。とりあえず記念の残しておくけど後で消す
                String packageName = appContext.getPackageName();
                    PaperangApi.init(appContext, packageName, new OnInitStatusListener() {
                        @Override
                        public void initStatus(boolean b) {
                            Log.e("PAPERANG", "b = " + b);
                        }
                    });
                    PaperangApi.registerBT(appContext);
                    scan();
//                PaperangApi.unregisterBT(appContext);
//                    if(register()){
//                        Log.i("PAPERANG", "register success");
//                        scan();
//                        disconnect();
//                    }else{
//                        Log.i("PAPERANG", "register faild");
//                    }
 */
                // お試しなので、イケてないコードになってしまった・・・
                BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
                if(bt.equals(null)){
                    Log.i("PAPERANG", "Bluetooth not support");
                   return false;
                }
                if(!bt.isEnabled()){
                    Log.i("PAPERANG", "disable bluetooth");
                    return false;
                }

                Set<BluetoothDevice> devices = bt.getBondedDevices();
                List<String> macAddressList = new ArrayList<String>();
                for(BluetoothDevice device : devices){
                    Log.i("PAPERANG", device.getAddress() + device.getName());
                    if(!"Paperang".equals(device.getName())){
                        continue;
                    }
                    macAddressList.add(device.getAddress());
                    BluetoothSocket socket = null;
                    try{
                        socket = device.createRfcommSocketToServiceRecord(UUID.fromString(SPP));
                        socket.connect();
                        Log.i("PAPERANG", "connected.");

                        OutputStream outst = socket.getOutputStream();
                        outst.write(crc);
                        outst.write(data);
                    }catch(IOException e){
                        Log.e("PAPERANG", e.getMessage());
                    }finally {
                        try{
                            if(socket != null){
                                socket.close();
                            }
                        }catch(IOException e2){
                            Log.e("PAPERANG", e2.getMessage());
                        }
                    }
                    return false;
                }
                return false;
            }
        });
    }
}

おわり

次は印刷したい!

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

Flutter MethodChannel APIの使い方

はじめに

FlutterはクロスプラットフォームなUIフレームワークです。UI特化のため、UI作る以外のプラットフォーム固有の機能を利用する場合には、公開されているライブラリ (プラグイン) を利用する以外にはFlutterの専用APIを利用してプラットフォーム側の実装を行う必要があります。

プラットフォーム固有機能の例は以下です。

  • Audio/Videoなどのメディアのデコード, エンコード, 再生
  • ランタイムパーミッションの表示や設定確認
  • Bluetoothや加速度センサーなどのハードウェア機能
  • WebView
  • ストレージやファイルアクセス

Dart⇆プラットフォーム双方向でまとまった情報が無かったため、ここにまとめることにしました。
今回はいくつかあるAPIのうち、最もよく使うMethodChannelについて使い方を解説します。

関連記事

Flutter (Dart) とプラットフォーム (Android/iOSなど) 間の通信/呼び出しAPIについてまとめています。

1. Flutter MethodChannelとは

Dartからプラットフォーム (Android/iOS等) のメソッドを呼び出すもしくは、プラットフォームからDartのメソッドを呼び出すためのAPIです。イメージ的には、AndroidのJNI (Java/KotlinからC/C++を呼び出すI/F) に近いですが、違いはとても簡単に呼び出せるのと非同期APIだという点です。

2. MethodChannelの仕組み

Flutterのアーキテクチャを以下に示します。
image.png

MethodChannel APIの使い方は後述しますが、呼び出し先のメソッド名と引数のデータの2つを引数として渡します。Flutter Framework内ではMethodChannelをBinaryMessagesという形に変換し、Flutter Engineとメッセージパッシングのやり取りを行います。

この界面でAPIコールが非同期のメッセージパッシング (データ送受信) に変わります。

Flutter Engineはデータを受け取ると、そのデータをMethod ChannelのAPIの形に変更し、対象のプラットフォームのAPIをコールします。この仕組みを実現しているのがFlutter Engine内に存在するPlatform Channelsです。
スクリーンショット 2020-02-10 21.29.31.png

3. 基本フロー

実際のコードの説明の前に、APIの基本的なフローについて解説します。ただし、プラットフォーム毎に若干API名が異なる場合があるため (iOS) 、注意してください。

3.1 Dart → プラットフォーム

  • [プラットフォーム側] MethodChannel#setMethodCallHandlerでコールバックを登録
  • [Dart側] MethodChannel#invokeMethodで呼び出したいメソッド名とデータをセットして非同期でコール
  • [プラットフォーム側] 受け取ったデータ (メソッド名) を見て、対象の処理を実施し、Result#success (エラーの場合はerror) をコール
  • [Dart側] メソッドコールの結果を確認

スクリーンショット 2020-02-11 18.20.03.png

3.2 プラットフォーム → Dart

プラットフォーム側からDartを呼び出すことも出来ます。Dart → プラットフォームの場合と手順は同じです。

スクリーンショット 2020-02-11 18.20.33.png

4. サンプルプロジェクト

実際のAPIの使い方は後述しますが、サンプルプロジェクトを以下に用意しています。そのまま動作確認可能ですので、参考にしてください。
ただし、正式に何かの機能を実装する場合は、プラグインの形で組み込んだ方が良いです。
https://github.com/Kurun-pan/flutter-methodchannel-example

5. Flutter + Android (Kotlin) のコード解説

5.1 Dartからプラットフォーム (Kotlin) を呼び出すケース

5.1.1 Dart側の実装

通信チャンネル作成

MethodChannelで通信チャンネルを作成します。引数に指定する文字列は他のアプリケーションと被らず、一意に決まるようにするために慣例で"アプリパッケージ名/チャンネル名"とするのが一般的な様子です。

プラットフォーム側の呼び出し

チャンネル作成後はそのチャンネル#invokeMethodを非同期でコールすることで、Kotlin側を呼び出すことが出来ます。
APIの仕様は引数の仕様は以下のようになっています。

  • 第一引数に呼び出したいメソッド名の文字列を指定
  • 第二引数には呼び出しメソッドの引数に指定するデータを指定。
    • 型はプリミティブ型で使える型に制約あり (StandardMessageCodec)
    • 複数の引数を指定指定したい場合、JSON形式のようにMapで記述するのが便利かと
invokeMethod仕様
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async
サンプルコード

実際のサンプルコードを以下に示します。

main.dart
import 'package:flutter/services.dart';

class _MyHomePageState extends State<MyHomePage> {
  static const MethodChannel _channel = const MethodChannel('com.example.methodchannel/interop');

  static Future<dynamic> get _list async {
    final Map params = <String, dynamic> {
      'name': 'my name is hoge',
      'age': 25,
    };
    final List<dynamic> list = await _channel.invokeMethod('getList', params);
    return list;
  }

  @override
  initState() {
    super.initState();

    // Dart -> Platforms
    _list.then((value) => print(value));
  }

5.1.2 プラットフォーム側の実装

通信チャンネル作成

Dart側と同じように、MethodChannelで通信チャンネルを作成します。引数の文字列はDart側と同じにする必要があります。

メソッド名の取得

チャンネル作成後、MethodCallHanderのコールバックを設定します。コールバックメソッドの引数methodCall.metodに、Dart側のinvokeMethodの第1引数の文字列が格納されているため、それを見て適切な処理を行います。

引数の取得

Dart側のinvokeMethodの第2引数は、methodCall.argumentから取得出来ます。ただし、Dartとプラットフォーム間で受け渡し可能な型には限りがあるため、詳細はこちらを参照して下さい。もし非サポートの型を指定すると、実行時にエラーになります。

サンプルコード
Kotlinコード
class MainActivity: FlutterActivity() {
    companion object {
        private const val CHANNEL = "com.example.methodchannel/interop"
        private const val METHOD_GET_LIST = "getList"
    }

    private lateinit var channel: MethodChannel

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
            if (methodCall.method == METHOD_GET_LIST) {
                val name = methodCall.argument<String>("name").toString()
                val age = methodCall.argument<Int>("age")
                Log.d("Android", "name = ${name}, age = $age")

                val list = listOf("data0", "data1", "data2")
                result.success(list)
            }
            else
                result.notImplemented()
        }
    }
Dart側に結果を返す

プラットフォーム側からDart側に結果を返す場合は、MethodCallHanderのコールバックメソッドの第2引数のMethodChannel.Resultクラスインスタンスをコールすることで実現できます。サンプルコードのようにリターン値を渡すことも可能です。

受け取ったResultインスタンスをローカルに保存し、処理を行った後で結果を返すことも可能ですが、その場合には必ずUIスレッドで結果を返す必要があります。

参考のために以下にResultクラスの各メソッドのAPI仕様を掲載しておきます。

MethodChannel.Result.success仕様
@UiThread
void success(@Nullable Object result)
MethodChannel.Result.error仕様
@UiThread
void error(String errorCode,
                     @Nullable
                     String errorMessage,
                     @Nullable
                     Object errorDetails)
MethodChannel.Result.notImplemented仕様
@UiThread
void notImplemented()

5.2 プラットフォーム (Kotlin) からDartを呼び出す

基本的にDartからプラットフォームを呼び出す方法と同じです。このメソッドの実行は必ずUIスレッドから行ってください。

プラットフォーム側の実装

作成したチャンネルに対して、invokeMethodをコールするだけです。
引数もプリミティブ型で渡すことが可能で、Dart側からの結果もMethodChannel.Resultのコールバックで受け取ることが可能です。不要なら指定なしでOKです。

Kotlinコード
channel.invokeMethod("callMe", listOf("a", "b"), object : MethodChannel.Result {
    override fun success(result: Any?) {
        Log.d("Android", "result = $result")
    }
    override fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
        Log.d("Android", "$errorCode, $errorMessage, $errorDetails")
    }
    override fun notImplemented() {
        Log.d("Android", "notImplemented")
    }
})
result.success(null)

Dart側の実装

作成したチャンネル#setMethodCallHandlerでMethodCallを設定します。

メソッド名の取得

コールバック引数のcall.methodでプラットフォーム側から指定された第1引数の値を取得出来ます。

引数の取得

call.argumentsに格納されています。

結果のリターン

成功 (通常) の場合は、Future.valueで結果を返します。
Future.errorを利用すると、プラットフォーム側のMethodChannel.Result.errorがコールされます。
MethodChannel.Result.notImplementedの指定方法は、よく分かっていません。知っている方がいたら教えてください…。

サンプルコード
Dartコード
  Future<dynamic> _platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case 'callMe':
        print('call callMe : arguments = ${call.arguments}');
        return Future.value('called from platform!');
        //return Future.error('error message!!');
      default:
        print('Unknowm method ${call.method}');
        throw MissingPluginException();
        break;
    }
  }

  @override
  initState() {
    super.initState();

    // Platforms -> Dart
    _channel.setMethodCallHandler(_platformCallHandler);
  }

6. 参考文献

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

Android でもとりあえず Ubuntu のデスクトップ環境を使いたい(UserLAnd 版)

はじめに

先に公開した Android Studio を使うための最低限のデスクトップ環境の構築方法を記載します。
日本語入力もありませんが、その代わり最短で構築できると思います。

Android の上で最も手軽に Ubuntu の環境を構築するなら UserLAnd が一番楽だと思われます。
Termux でもできますが、UserLAnd の方が手間が少なくて済みます。
その代わり、Termux より動作速度が遅いです。
どちらが良いかはお手持ちのスマートフォンの速度と好みで選ぶと良いと思います。

注意点

デスクトップ環境(LXDEやXfce)のインストールは数時間かかります。
時間を確保してから実施するほうが良いと思います。

インストール

Play ストア で UserLAnd と XSDL をインストールしてください。
UserLAnd は Debian や Ubuntu といった Linux 環境を簡単に構築するためのアプリケーションになります。
XSDL は画面を担当するアプリケーションです。
UserLAnd はコマンドラインまでしかサポートしませんので、XSDL で画面をサポートしてもらいます。

Ubuntu のインストール

UserLAnd を立ち上げて、Ubuntu と書かれた項目をタップします。
ユーザーIDとパスワードを入力して CONTINUE をタップします。
接続の種類に SSH, VNC, XSDLを選択するように言われますので、SSH を選択して CONTINUE をタップします。

環境によりけりですが、1時間は待つことになると思います。

Ubuntu デスクトップ環境のインストール

ここからは Ubuntu での操作になります。

まずは apt でパッケージ(Linux アプリケーションの倉庫のようなもの)を更新します。

sudo apt update
sudo apt upgrade -y

次にデスクトップ環境をインストールします。
調べた限りでは、LXDE, Xfce の2種類が使えます。
LXDE の方が軽いそうですが、Xfce の方が見た目が良くLXDE程ではないですけれども十分な軽さを持っているそうです。
ここは好みで選択すれば良いと思います。

なお、ここでネットワークやスマートフォンの速度にもよると思いますが数時間掛かります。

・ LXDE のインストール

sudo apt install -y lxde

・ XFCE のインストール

sudo apt install -y xfce4

環境の設定

XSDL を立ち上げてしばらく待って下さい。
最後なにやら文字列を表示している画面が出るので以下をメモしてください。

上から2行目に以下の文字列が出るはずです。DISPLAY のポート番号をメモしてください。

export DISPLAY=(何かのIPアドレス):(ポート番号)

上から3行目に以下の文字列が出るはずです。同様に PULSE_SERVER のポート番号をメモしてください。

export PULSE_SERVER=(何かのIPアドレス):(ポート番号)

IPアドレスはメモする必要はないです。
再び UserLAnd の Ubuntu に戻って下さい。
XSDL に接続するために、設定ファイルに先程メモしたポート番号とローカルのIPアドレスを記載します。

# テキストエディタ vim をインストールしていない人はインストール
sudo apt install -y vim

# startXDSL にXDSLの接続設定を記載する
vim /support/startXSDLServerSteo2.sh

4行目、8行目、16行目辺りを修正します。

# 4行目辺り。4721 の値をメモを行った DISPLAY のポート番号に書き換えます。
  DISPLAY=:4721
    ↓ 書き換え後
  DISPLAY=:(さっきメモした DISPLAY のポート番号)

# 8行目辺り。4721 の値をメモを行った PULSE_SERVER のポート番号に書き換えます。
  PULSE_SERVER=localhost:4721
    ↓ 書き換え後
  PULSE_SERVER=localhost:(さっきメモした PULSE_SERVER のポート番号)

# 16行目辺り。/usr/bin/twm の値を exec startlxde または、exec startyxfce4 に書き換えます。exec を付けないと動作しないので付けて下さい。
  echo '/usr/bin/twm' > /home/$INITIAL_USERNAME/.xinitrc
    ↓ 書き換え後
  # LXDE を使う場合
  echo 'exec startlxde' > /home/$INITIAL_USERNAME/.xinitrc

  # Xfce を使う場合
  echo 'exec startxfce4' > /home/$INITIAL_USERNAME/.xinitrc

vim の操作方法を知らない人は以下だけ覚えればなんとかなります。

キーボードのキー 役割
i コマンドモードから入力モードに入る
ESC 入力モードを抜けてコマンドモードに戻る
:wq 保存して終了する。コマンドモードで操作すること。

デスクトップ環境の起動

UserLAnd に戻って Ubuntu を長押しし、Stop App を選んで終了して下さい。
(終了しない場合には、下の Sessionsにある Ubuntu を長押しして Stop Session を選択して下さい。)
再び Ubuntu を長押しし、App Info を選択し XSDL を選択してください。
下の Apps を押してから Ubuntu を押して起動してください。
しばらくすると Ubuntu のデスクトップ環境が立ち上がります。

UserLAnd の場合には XSDL の立ち上げまで行ってくれるので、手動で XSDL を立ち上げる必要はありません。

参考サイト

https://www.hiroom2.com/2018/05/06/ubuntu-1804-xfce-ja/

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