Skip to content

Home

如何使用Chrome遠程桌面在AWS實例上安裝Ubuntu桌面

在如今以雲端為中心的世界中,許多資源已被移至雲端,例如相片、檔案和伺服器等。那麼為何不將您的桌面環境也移到雲端呢?想像一下,不再需要帶著一台重型筆記本。您可以從任何薄型客戶端(例如帶有鍵盤的平板)在任何地方訪問您的計算能力。透過AWS實例,結合Ubuntu桌面和Chrome遠程桌面就可以實現這一點。雖然還有其他解決方法,如AWS工作區或VNC連接,但Chrome遠程桌面提供的延遲最低且性能最接近原生桌面。以下是如何設置的方法:

  1. 登錄AWS控制台,並使用Ubuntu Server AMI啟動一個實例。

  1. 實例啟動後,SSH登錄到您的Ubuntu服務器。更新包管理器並安裝wget:

sudo apt update sudo apt-get install --assume-yes wget

  1. 下載Chrome遠程桌面套裝軟體並安裝它:

wget https://dl.google.com/linux/direct/chrome-remote-desktop_current_amd64.deb sudo dpkg --install chrome-remote-desktop_current_amd64.deb sudo apt install --assume-yes --fix-broken

  1. 安裝桌面GUI環境。

在測試了各種不同的發行版本後,我發現Xfce在網路較慢的情況下表現最佳:

sudo DEBIAN_FRONTEND=noninteractive apt install --assume-yes xfce4 desktop-base

  1. 配置Chrome遠程桌面預設使用Xfce:

sudo bash -c 'echo "exec /etc/X11/Xsession /usr/bin/xfce4-session" > /etc/chrome-remote-desktop-session'

  1. 安裝xscreensaver作為預設的Xfce鎖屏程序的替代品,因為它不與遠程桌面兼容:

sudo apt install --assume-yes xscreensaver

  1. 禁用顯示管理器,因為沒有連接的顯示器:

sudo systemctl disable lightdm.service

  1. 將您的用戶賬戶添加到Linux群組,然後註銷:

sudo usermod -a -G chrome-remote-desktop $USER logout

  1. 在您的本地筆記本瀏覽器上,打開Chrome遠程桌面的無頭模式。按照步驟設置另一部電腦。您可能會在您的AWS實例中複製此類命令:

DISPLAY= /opt/google/chrome-remote-desktop/start-host --code="4/xxxxxxxxxxxxxxxxxxxxxxxx" --redirect-url="https://remotedesktop.google.com/_/oauthredirect" --name=

  1. 最後,您可以遠程連接到您的Ubuntu桌面了。

    額外的提示:您可以顯示屏幕保護程序,儘管這並不必要。

您現在可以使用任何設備,從任何地方訪問您的桌面環境。這種設置對於隨時需要訪問完整開發工具集的軟體工程師特別有用。

How to Install Ubuntu on an External Drive Using macOS

If you're using Mac hardware but also want to run Ubuntu, you can install Ubuntu on an external hard drive without risking the removal of your macOS installation. Here's how to do it without messing up your bootloader:

  1. Plug in your external SSD/HDD. Open Disk Utility and format it to MS-DOS (FAT).

  2. Open a Terminal window and run: diskutil list

This will help you find your external hard drive's IDENTIFIER, e.g., disk2 in this example.

  1. Download and install VirtualBox: VirtualBox Website

  2. Run the two commands below and start VirtualBox with admin rights:

sudo VBoxManage internalcommands createrawvmdk -filename bootcamp.vmdk -rawdisk /dev/disk2 sudo /Applications/VirtualBox.app/Contents/MacOS/VirtualBox

  1. Select "New" and then click on "Expert Mode." Choose "Use an existing virtual hard disk file."

  1. In the "System" tab, check the box for "Enable EFI (special OSes only)."

  2. In the "Storage" tab, mount the Ubuntu ISO file to the virtual machine. You can download the Ubuntu ISO file from here.

  3. Start the virtual machine and proceed with the Ubuntu installation.

  4. Once the installation is complete, you'll be ready to boot from the drive. For Mac, you'll also need to reduce the Security level and allow booting from external media. To do this, press and hold Command-R immediately after you see the Apple logo to start macOS recovery.

  5. Restart your Mac again and press the Option key. You can now choose your drive with the EFI label.

Enjoy using Ubuntu on Apple hardware! Note that you might have to sort out some driver issues, but hopefully, this guide makes the process easier for you.

How to Install Ubuntu on an External Drive Using macOS

Hello and welcome back to another episode of Continuous Improvement. I'm your host, Victor, and today we're going to talk about a unique topic - how to install Ubuntu on an external hard drive without risking the removal of your macOS installation.

Now, we know that many of you may be using Mac hardware but also want to experience the wonders of Ubuntu. So, let's dive into the step-by-step process of achieving this without messing up your bootloader.

The first step is to plug in your external SSD or HDD and open Disk Utility. Format the drive to MS-DOS (FAT) to ensure compatibility.

Next, open a Terminal window and run the command diskutil list. This will help you find the IDENTIFIER of your external hard drive, such as disk2 in our example.

Now that we have identified the external drive, it's time to download and install VirtualBox from the official website. VirtualBox is a powerful tool that allows us to create virtual machines on our Mac.

After the installation, run two commands in the Terminal window to create a virtual machine using your external drive as the raw disk. Type in sudo VBoxManage internalcommands createrawvmdk -filename bootcamp.vmdk -rawdisk /dev/disk2 and then sudo /Applications/VirtualBox.app/Contents/MacOS/VirtualBox. This will start VirtualBox with admin rights.

Now, in VirtualBox, click on "New" and select "Expert Mode". Choose the option "Use an existing virtual hard disk file" and browse for the bootcamp.vmdk file that we created earlier.

In the "System" tab, make sure to check the box for "Enable EFI (special OSes only)" to ensure compatibility with macOS.

Moving on to the "Storage" tab, we need to mount the Ubuntu ISO file to the virtual machine. You can download the Ubuntu ISO file from the official Ubuntu website.

Great! Now it's time to start the virtual machine and proceed with the Ubuntu installation. Follow the on-screen instructions and be patient as the installation process completes.

Once the installation is complete, you'll be ready to boot from the external drive. But before that, there's one more thing. For Mac users, you'll need to reduce the Security level and allow booting from external media.

To do this, restart your Mac and press and hold Command-R immediately after you see the Apple logo to start macOS recovery. From there, navigate to the Security options and allow booting from external media.

Fantastic! You're almost there. Restart your Mac again, but this time, press and hold the Option key. You'll now see a menu where you can choose your drive with the EFI label. That's your Ubuntu installation on the external drive.

And there you have it! You can now enjoy the wonders of Ubuntu on your Apple hardware without compromising your macOS installation. Just keep in mind that you might need to sort out some driver issues, but don't worry, the process we've discussed today will make it much easier for you.

That concludes today's episode of Continuous Improvement. I hope you found this guide helpful and that it encourages you to explore new possibilities in the world of technology. Remember, with continuous improvement, we can achieve great things.

Until next time, I'm Victor, signing off. Take care and stay curious!

如何使用macOS在外部驅動器上安裝Ubuntu

如果你在使用Mac硬體,但也想要運行Ubuntu,你可以在外部硬碟上安裝Ubuntu,而不會冒著刪除你的macOS安裝的風險。下面是如何在不搞砸你的引導裝載器的情況下進行操作:

  1. 將你的外部SSD/HDD插入。打開磁碟工具並將其格式化為MS-DOS(FAT)

  2. 打開終端窗口並運行: diskutil list

這將幫助你找到你的外部硬碟的標識符,例如,在這個示例中是disk2。

  1. 下載並安裝VirtualBox: VirtualBox網站

  2. 運行以下兩條命令並以管理員權限啟動VirtualBox:

sudo VBoxManage internalcommands createrawvmdk -filename bootcamp.vmdk -rawdisk /dev/disk2 sudo /Applications/VirtualBox.app/Contents/MacOS/VirtualBox

  1. 選擇“新建”然後點擊“專家模式”。選擇“使用現有的虛擬硬碟文件。”

  1. 在“系統”標籤中,勾選“啟用EFI(僅限特殊OS)”。

  2. 在“儲存”標籤中,將Ubuntu ISO文件掛載到虛擬機。 你可以從這裡下載Ubuntu ISO文件。

  3. 啟動虛擬機並進行Ubuntu安裝。

  4. 一旦安裝完成,你就可以從驅動器啟動了。 對於Mac,你也需要降低安全級別並允許從外部媒體啟動。要做到這一點,在你看到蘋果標誌後立即按住Command-R以啟動macOS恢復。

  5. 重新啟動你的Mac並按住選項鍵。你現在可以選擇你的驅動器擁有EFI標籤。

享受在Apple硬體上使用Ubuntu!請注意,你可能需要解決一些驅動問題,但希望這個指南能讓你的過程更容易。

How to Fix the "Access Denied" Error in an AWS Amplify Angular App

When you deploy your Angular app on AWS Amplify, the first landing page loads without any issues. However, if you have defined a path in your router and attempt to access it, you may encounter an "Access Denied" error:

To resolve this issue, navigate to the AWS Console and select "Rewrites and Redirects." Next, add a new rewrite and redirect rule. Click on "Open Text Editor" and insert the following rule:

[
  {
    "source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$)([^.]+$)/>",
    "target": "/index.html",
    "status": "200",
    "condition": null
  }
]

After adding this rule, try accessing your URL again. It should work as expected now.

How to Fix the "Access Denied" Error in an AWS Amplify Angular App

Welcome back to Continuous Improvement, the podcast where we explore tips and tricks for enhancing your software development experience. I'm your host, Victor. In today's episode, we'll be discussing a common issue encountered when deploying an Angular app on AWS Amplify.

Have you ever deployed an Angular app on AWS Amplify and faced an "Access Denied" error when trying to access a defined path in your router? Well, fear not, because I have a solution for you.

The first step to resolving this issue is navigating to the AWS Console. Once you're there, select "Rewrites and Redirects" in your Amplify app settings.

Now, in order to allow access to the defined path, we'll need to add a new rewrite and redirect rule. Click on "Open Text Editor."

Here comes the crucial part: inserting the correct rewrite and redirect rule. Don't worry, I'll provide you with the necessary code.

In the text editor, paste the following code:

[
    {
        "source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$)([^.]+$)/>",
        "target": "/index.html",
        "status": "200",
        "condition": null
    }
]

This code snippet will ensure that any request to your defined path is redirected to the index.html file, allowing access without encountering the "Access Denied" error.

After you've added this rule, give it a try by accessing your URL again. Voila! It should now work as expected, and you can navigate to your defined path without any issues.

And that's it for today's episode of Continuous Improvement. Remember, don't let deployment challenges hinder your development progress. Stay tuned for more valuable tips and tricks to enhance your software development journey.

Thank you for listening to Continuous Improvement. I'm your host, Victor, and until next time, keep improving!

如何修復 AWS Amplify Angular App 中的“拒絕存取”錯誤

當您在 AWS Amplify 上部署您的 Angular 應用程式時,第一個登陸頁面可以正常載入。然而,如果您在您的路由器中定義了一條路徑並嘗試存取它,您可能會遇到一個“拒絕存取”的錯誤:

要解決這個問題,首先導航至 AWS 控制台,然後選擇“重寫和重定向”。接著,加入一條新的重寫和重定向規則。點擊“開啟文字編輯器”並插入以下的規則:

[
  {
    "source": "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf)$)([^.]+$)/>",
    "target": "/index.html",
    "status": "200",
    "condition": null
  }
]

添加這條規則後,嘗試再次存取您的 URL。現在應該可以按預期工作了。

Writing Your Android App to Run in Background Mode as a Service

In some scenarios, you might face an issue where your app's functionality is lost after several minutes when users place it in background mode or lock their screens. This can lead to poor user experience, such as Bluetooth devices disconnecting from your app or data sync to the server getting interrupted. Users may then complain about the connectivity and stability of your app.

First, to address this issue, you need to understand the Android app Activity lifecycle. When a user switches to another app, like Facebook, instead of using yours, your app's activity is terminated and the onDestroy() method is triggered. While this behavior is beneficial for the Android system as it helps free up unused memory and manage battery life, it can be problematic for your app because its states and functionalities get lost.

The next step is to refactor your app to separate background functionalities, like Bluetooth connectivity, into a service. A service is an application component capable of performing long-running operations in the background, independent of the user interface. Here's a code example for a service:

import android.app.Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
      // Place your service logic here
    }

    override fun onDestroy() {
      // Clean up your service logic here
    }

}

To use this service, you also need to define it in your AndroidManifest.xml. Here is an example (replace the name with your service's package name):

<application>
    ...
    <service android:enabled="true" android:name="com.victorleungtw.myapp.services.MyService"></service>
</application>

Furthermore, to start this service in your activity (for example, in Activity.onCreate()), add the following line:

startService(Intent(this, MyService::class.java))

And where you wish to stop the service, include this line:

stopService(Intent(this, MyService::class.java))

After implementing these changes, your app will be better structured but won't yet be able to run indefinitely in the background. To achieve that, you also need to add two more methods within your service:

class MyService : Service() {
    // ... existing code

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val channelId =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    createNotificationChannel("my_service", "My Background Service")
                } else {
                    // If it's an earlier version, the channel ID is not used
                    ""
                }

        val notification: Notification = Notification.Builder(this, channelId)
                .setContentTitle("Title")
                .setContentText("Text")
                .build()
        startForeground(2001, notification)

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val channel = NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(channel)
        return channelId
    }
}

Here, a notification is created to inform the user that the app continues to run in the background. It might look something like this:

That's it. Now your users can multitask—like browsing Facebook—while your app continues to run in the background, maintaining Bluetooth connectivity, syncing user data, playing music, or performing other tasks depending on your app's features.

Writing Your Android App to Run in Background Mode as a Service

Welcome back to another episode of Continuous Improvement, the podcast where we explore tips and strategies to enhance your app development skills. I'm your host, Victor. In today's episode, we're going to talk about a common issue that many developers face – maintaining app functionality in the background. Have you ever experienced your app losing its functionality when users switch to other apps or lock their screens? It can be quite frustrating, right? Well, worry no more because we have some solutions for you!

But before we dive into that, let's quickly talk about the Android app Activity lifecycle. When a user switches away from your app, the activity is terminated, and the onDestroy() method is triggered. While this behavior is necessary for memory management and battery life, it can cause your app's states and functionalities to be lost.

To overcome this challenge, we recommend refactoring your app by separating background functionalities into a service. A service is an application component capable of performing long-running operations in the background, independent of the user interface.

Let me give you an example of how to create a service in Kotlin:

import android.app.Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
      // Place your service logic here
    }

    override fun onDestroy() {
      // Clean up your service logic here
    }

}

Once you've created your service, remember to define it in your AndroidManifest.xml file to ensure it is recognized by the system:

<application>
    ...
    <service android:enabled="true" android:name="com.victorleungtw.myapp.services.MyService"></service>
</application>

Now, if you want to start the service in your activity, simply add this line of code:

startService(Intent(this, MyService::class.java))

And, to stop the service, include this line:

stopService(Intent(this, MyService::class.java))

These changes will improve the structure of your app, but your app still won't be able to run indefinitely in the background. To achieve that, you need to add two more methods within your service. Here's an example:

class MyService : Service() {
    // ... existing code

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val channelId =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    createNotificationChannel("my_service", "My Background Service")
                } else {
                    // If it's an earlier version, the channel ID is not used
                    ""
                }

        val notification: Notification = Notification.Builder(this, channelId)
                .setContentTitle("Title")
                .setContentText("Text")
                .build()
        startForeground(2001, notification)

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val channel = NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(channel)
        return channelId
    }
}

By creating a notification, you can inform users that your app continues to run in the background. This notification can be customized to match your app's branding and provide relevant information.

With these changes, your app can now run seamlessly in the background, maintaining functionalities like Bluetooth connectivity, data syncing, or playing music.

And that brings us to the end of today's episode of Continuous Improvement. I hope you found these tips helpful in enhancing your app's functionality in the background. Remember, a seamless user experience is crucial for app success. If you have any questions or suggestions for future episodes, feel free to reach out to me. Until then, keep coding and keep improving!

如何將您的Android應用程式寫入後台執行模式作為服務

在某些情況下,您可能會遇到一個問題,那就是當用戶將應用程式置於背景模式或鎖定屏幕後幾分鐘,您的應用程式的功能就會消失。這可能導致用戶體驗變差,例如藍牙設備從您的應用程式中斷開,或者數據同步到服務器中斷。用戶可能會抱怨您的應用程式的連接性和穩定性。

首先,為了解決這個問題,您需要了解Android應用程式活動生命周期。當用戶切換到另一個應用程式,例如Facebook,而不是使用您的,您的應用程式活動將被終止,並觸發 onDestroy() 方法。然而,這種行為對於Android系統是有利的,因為它可以幫助釋放未使用的記憶體和管理電池壽命,但對於您的應用程式來說,可能會有問題,因為其狀態和功能可能會丟失。

下一步是重構您的應用程式,將如藍牙連接等背景功能分離為服務。服務是一種應用程式組件,能夠在後台執行持久運作,獨立於使用者介面。下面是一個服務的代碼範例:

import android.app.Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
      // 在此擺放你的服務邏輯
    }

    override fun onDestroy() {
      // 在此清理你的服務邏輯
    }

}

要使用此服務,您還需要在您的 AndroidManifest.xml 中定義它。這是一個例子(將名稱替換為您的服務的套件名稱):

<application>
    ...
    <service android:enabled="true" android:name="com.victorleungtw.myapp.services.MyService"></service>
</application>

此外,要在您的活動中啟動此服務(例如在 Activity.onCreate() 中),請添加以下行:

startService(Intent(this, MyService::class.java))

並在您希望停止服務的地方,包括此行:

stopService(Intent(this, MyService::class.java))

實施這些更改後,您的應用程式將有更好的結構,但還不能在背景中無限期運行。為了實現這一點,您還需要在您的服務中添加兩個方法:

class MyService : Service() {
    // ... 現有的代碼

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val channelId =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    createNotificationChannel("my_service", "My Background Service")
                } else {
                    // 如果是較早的版本,則不使用頻道ID
                    ""
                }

        val notification: Notification = Notification.Builder(this, channelId)
                .setContentTitle("Title")
                .setContentText("Text")
                .build()
        startForeground(2001, notification)

        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotificationChannel(channelId: String, channelName: String): String {
        val channel = NotificationChannel(channelId,
                channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(channel)
        return channelId
    }
}

在這裡,創建了一個通知,通知用戶該應用程式繼續在背景中運行。它可能看起來像這樣:

就是這樣。現在您的用戶可以進行多任務操作,例如瀏覽Facebook,同時您的應用程式繼續在後台運行,保持藍牙連接,同步用戶數據,播放音樂或執行其他取決於您的應用程式功能的任務。