調整 Bootstrap 4 按鈕的顏色 (Change Bootstrap 4 button color)

Bootstrap 4 的按鈕配色個人覺得比較硬,想改回 Bootstrap 3 的按鈕配色於是就用覆蓋樣式的方式依照 Boostrap 4 的樣式設定調整顏色。

( LESS 格式,因為 WordPress bug 把 & 給 escape 了,複製後得把 & 改回 & )

#bundle() {
	.btnStyle {
		color: @color;
		background-color: @background-color;
		border-color: @border-color;
		&:hover {
			color: @color;
			background-color: @background-color-hover;
			border-color: @border-color-hover;
		}
		&.focus, &:focus {
			box-shadow: @box-shadow;
		}
		&.disabled, &:disabled {
			color: @color;
			background-color: @background-color;
			border-color: @border-color;
		}
		&:not(:disabled):not(.disabled).active
		, &:not(:disabled):not(.disabled):active
		, .show > &.dropdown-toggle {
			color: @color;
			background-color: @background-color-hover;
			border-color: @border-color-hover;
		}
		&:not(:disabled):not(.disabled):active:focus
		, &:not(:disabled):not(.disabled).active:focus
		, .show > &.dropdown-toggle:focus {
			box-shadow: @box-shadow;
		}
	}
}

.btn-primary {
	@color: #fff;
	@background-color: #0275d8;
	@border-color: #0275d8;
	@background-color-hover: #025aa5;
	@border-color-hover: #01549b;
	@box-shadow: 0 0 0 .2rem rgba(2, 117, 216, .5);
	#bundle.btnStyle();
}

.btn-secondary {
	@color: #292b2c;
	@background-color: #fff;
	@border-color: #ccc;
	@background-color-hover: #e6e6e6;
	@border-color-hover: #adadad;
	@box-shadow: 0 0 0 .2rem rgba(204, 204, 204, .5);
	#bundle.btnStyle();
}

.btn-info {
	@color: #fff;
	@background-color: #5bc0de;
	@border-color: #5bc0de;
	@background-color-hover: #31b0d5;
	@border-color-hover: #2aabd2;
	@box-shadow: 0 0 0 .2rem rgba(91, 192, 222, .5);
	#bundle.btnStyle();
}

.btn-success {
	@color: #fff;
	@background-color: #5cb85c;
	@border-color: #5cb85c;
	@background-color-hover: #449d44;
	@border-color-hover: #449d44;
	@box-shadow: 0 0 0 .2rem rgba(92, 184, 92, .5);
	#bundle.btnStyle();
}

.btn-warning {
	@color: #fff;
	@background-color: #f0ad4e;
	@border-color: #f0ad4e;
	@background-color-hover: #ec971f;
	@border-color-hover: #eb9316;
	@box-shadow: 0 0 0 .2rem rgba(240, 173, 78, .5);
	#bundle.btnStyle();
}

.btn-danger {
	@color: #fff;
	@background-color: #d9534f;
	@border-color: #d9534f;
	@background-color-hover: #c9302c;
	@border-color-hover: #c12e2a;
	@box-shadow: 0 0 0 .2rem rgba(217, 83, 79, .5);
	#bundle.btnStyle();
}

.btn-light {
	@color: #212529;
	@background-color: #eceeec;
	@border-color: #eceeec;
	@background-color-hover: #d8dcd8;
	@border-color-hover: #d1d6d1;
	@box-shadow: 0 0 0 .2rem rgba(206, 208, 207, 0.5);
	#bundle.btnStyle();
}

.btn-dark {
	@color: #fff;
	@background-color: #343a40;
	@border-color: #343a40;
	@background-color-hover: #23272b;
	@border-color-hover: #1d2124;
	@box-shadow: 0 0 0 .2rem rgba(82, 88, 93, 0.5);
	#bundle.btnStyle();
}
 
分類: Web technology | 在〈調整 Bootstrap 4 按鈕的顏色 (Change Bootstrap 4 button color)〉中留言功能已關閉

用 PowerShell 自動備份 Windows 10 的焦點影像

Title: Windows Spotlight auto backup script by PowerShell

Windows 10 的鎖定畫面如果背景選用 "Winodws 焦點" 的話會每天變換影像,系統下載的這些影像並不會一直保留而是會刪除舊的,因此若是想要將好看的風景保留下來的話必須自己備份,於是我就寫一小段碼來自動化備份這些風景。

把以下程式碼存檔後用工作排程器安排每天執行或登入時執行即可,記得把 $to 的路徑改成自己的備份路徑:

$from = "$env:USERPROFILE\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets\"
$to = "C:\Users\Your_Name\Pictures\Windows Spotlight\"

Get-ChildItem -Path $from | `
Foreach-Object {
    If ($_.length -gt 300kb) {
        $newFilename = $to + $_.BaseName + ".jpg"
        If (-not (Test-Path $newFilename)) {
            Write-Host "Copy:" $_.BaseName
            Copy-Item $_.FullName $newFilename
        }
    }
}
分類: 資訊技術相關雜記 | 在〈用 PowerShell 自動備份 Windows 10 的焦點影像〉中留言功能已關閉

給 Android Studio (Windows) 用的預設 .gitattributes

Android Studio 建立新專案的時候並沒有預設的 .gitattributes,Project 裡面有些預設檔案的 EOL 是 LF,在 Windows 環境下當 git 設定 core.autocrlf true 且 core.safecrlf true 的時候,直接做 git add 操作的話會跳出 fatal error,因此要稍微補充一下設定。

.gitattributes

* text=auto !eol

gradlew			text eol=lf
.idea/*.xml		text eol=lf
分類: 軟體開發 | 標籤: | 在〈給 Android Studio (Windows) 用的預設 .gitattributes〉中留言功能已關閉

把 Cordova Webview 嵌入 Android Native App

Steps:

1. 把 Requirements 都下載安裝好

2. 建立 Cordova Android Project

如果 Cordova projects 放置的位置與 Android App projects 是不同的目錄的話,test_cordova 可以改成與 android app project 相同名稱,這樣會比較好知道 cordova project 是對應到哪個 android project,此外 com.example.hello 也可以改成跟 android app 一樣。

cordova create test_cordova com.example.hello HelloWorld
cordova platform add android
cordova build

在 build 之前可以視情況加入需要的 plugins,例如:

cordova plugin add cordova-plugin-device
cordova plugin add cordova-plugin-x-toast

3. 建立 Android App Project

用預設的即可,有一個 Empty Activity (MainActivity)。

4. 安裝 Cordova Project 環境到 Android Project

Compile Cordova Lib

參考 Android WebViews - Apache Cordova 步驟 1~3,把 cordova-x.x.x.jar 複製到 /libs 之後,編輯 app/build.gradle,加上:

compile files('libs/cordova-x.x.x.jar')

Example:

dependencies {
	compile fileTree(include: ['*.jar'], dir: 'libs')
	testCompile 'junit:junit:4.12'
	compile 'com.android.support:appcompat-v7:23.4.0'
	compile files('libs/cordova-5.2.2.jar')
}

複製依存檔案 (左邊是 Cordova project 的 path,右邊是 Android project 的 path)

www folder

platforms/android/assets/www -> src/main/assets/www

plugins (com/example/hello 以外的目錄,安裝 plugins 多出來的那些)

platforms/android/src/plugin_folder -> src/main/java/

config.xml

platforms/android/res/xml/config.xml -> src/main/res/xml/

然後 sync 一下 Gradle 情報。

5. 建立基本運作 Activity

把 Cordova Webview 嵌入 Android Native App 的基本作業到第四步驟就完成了,接下來可以建立基本運作的 Activity,來測試看看是不是正常運作。

建立以下兩個 Empty Activity: TestCordovaActivity, TestCordovaWithLayoutActivity,然後編輯 java 及 layout 如下:

TestCordovaActivity

public class TestCordovaActivity extends CordovaActivity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		super.init();
		// Load your application
		launchUrl = "file:///android_asset/www/index.html";
		loadUrl(launchUrl);
	}
}

@Override
public void onDestroy() {
	ViewGroup viewGroup = (ViewGroup)this.findViewById(android.R.id.content);
	SystemWebView webView = (SystemWebView) viewGroup.getChildAt(0);
	viewGroup.removeView(webView);
	webView.removeAllViews();
	super.onDestroy();
}

TestCordovaWithLayoutActivity

public class TestCordovaWithLayoutActivity extends CordovaActivity {

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_test_cordova_with_layout);
		super.init();

		// Load your application
		launchUrl = "file:///android_asset/www/index2.html";
		loadUrl(launchUrl);

	}

	@Override
	protected CordovaWebView makeWebView() {
		SystemWebView webView = (SystemWebView)findViewById(R.id.cordovaWebView);
		return new CordovaWebViewImpl(new SystemWebViewEngine(webView));
	}

	@Override
	protected void createViews() {
		// Why are we setting a constant as the ID? This should be investigated
		/*
		appView.getView().setId(100);
		appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
			ViewGroup.LayoutParams.MATCH_PARENT,
			ViewGroup.LayoutParams.MATCH_PARENT));
		setContentView(appView.getView());
		*/

		if (preferences.contains("BackgroundColor")) {
			int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
			// Background of activity:
			appView.getView().setBackgroundColor(backgroundColor);
		}

		appView.getView().requestFocusFromTouch();
	}

	@Override
	public void onDestroy() {
		SystemWebView webView = (SystemWebView)findViewById(R.id.cordovaWebView);
		((ViewGroup)webView.getParent()).removeView(webView);
		webView.removeAllViews();
		// If we called webView.destory(), we will get 'mWebViewCore is null' error.
		//webView.destroy();
		super.onDestroy();
	}

}

activity_test_cordova_with_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:paddingBottom="@dimen/activity_vertical_margin"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	tools:context="com.fandratec.fssfordksh.TestCordovaWithLayoutActivity">

	<TextView
		android:layout_width="match_parent"
		android:layout_height="100dp"
		android:background="#FF0000"
		android:textColor="#FFFFFF"
		android:gravity="center"
		android:text="This is native text view"
		/>

	<org.apache.cordova.engine.SystemWebView
		android:id="@+id/cordovaWebView"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		/>

</RelativeLayout>

MainActivity

public class MainActivity extends AppCompatActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void startCordovaActivity(View view) {
		Intent intent = new Intent(this, TestCordovaActivity.class);
		startActivity(intent);
	}

	public void startCordovaActivityWithLayout(View view) {
		Intent intent = new Intent(this, TestCordovaWithLayoutActivity.class);
		startActivity(intent);
	}

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:paddingBottom="@dimen/activity_vertical_margin"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	tools:context="com.fandratec.fssfordksh.MainActivity">

	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="Hello World!"
		android:id="@+id/textView"/>

	<Button
		android:text="Start Cordova Activity Without Layout"
		android:onClick="startCordovaActivity"
		android:layout_width="200dp"
		android:layout_height="100dp"
		android:id="@+id/button"
		android:layout_below="@+id/textView"
		android:layout_alignParentStart="true"/>

	<Button
		android:text="Start Cordova Activity With Layout"
		android:onClick="startCordovaActivityWithLayout"
		android:layout_width="200dp"
		android:layout_height="100dp"
		android:layout_below="@+id/button"
		android:layout_alignParentStart="true"/>

</RelativeLayout>

以上都弄好後,就可以 run 了。

6. 延伸

如果這個 Android App 要連線到 Internet 或是取得 GPS 座標,記得在 AndroidManifest.xml 加入權限需求宣告,如果沒加上的話會出現錯誤。(例如 loadUrl(REMOTE_URL) 就會直接獲得 "Application 錯誤")

<manifest>
	<uses-permission android:name="android.permission.INTERNET" />
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

另外,現在版本的 Cordova project 預設啟用 whitelist 機制,想從 CordovaWebView 連到local (/assets/www) 外的內容並在其中呈現,得在 config.xml 加上設定,要不然會另外呼叫預設瀏覽器:

<widget>
	<allow-navigation href="https://YOUR.SITE/*" />
</widget>

References:

分類: 軟體開發 | 標籤: | 在〈把 Cordova Webview 嵌入 Android Native App〉中留言功能已關閉

在 Linux (Ubuntu) 導入 Let’s Encrypt (for Apache)

文章更新時間:2019-03-13

環境:

OS: Ubuntu 18.04 LTS
Web server: Apache: 2.4

初始化:

  1. 如果還沒裝 git 的話:
    sudo apt-get install git
  2. cd /opt
  3. sudo git clone https://github.com/certbot/certbot
  4. cd certbot
  5. 自動化安裝依存套件以建立起執行環境:
    ./certbot-auto

單一主機名稱 (Single host) 步驟:

  1. 開始自動化設定 (for apache)
    ./certbot-auto --apache
  2. 選擇要簽發憑證的主機名稱
  3. 選擇未來的連線方式
    1. HTTP, HTTPS 並存
    2. 全部導向 HTTPS (會自動在該主機設定檔加上 rewrite rule)
  4. 測試是否正常簽發
    https://www.ssllabs.com/ssltest/analyze.html?d=[主機名稱]

Wildcard Certificate 步驟:

  1. 採用手動設定,挑戰模式採用 DNS TXT 紀錄:
    ./certbot-auto certonly --manual --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --email yourname@example.com -d "*.yourdomain.com,yourdomain.com"
  2. 接下來會出現要加入 DNS TXT 的內容:
  3. 加入後用以下指令確認是否已經生效:
    [Linux] dig _acme-challenge.yourdomain.com txt
    [Windows] nslookup -q=txt _acme-challenge.yourdomain.com
  4. 生效後按下 Enter
  5. 由於本範例登錄了 *.yourdomain.com 及 yourdomain.com 兩個域名,所以 2~3 步驟會跑兩次(請注意那個挑戰用的值 xxxxxxxx 每次都不同,此外,不要刪除前一個 TXT Record 而是建立同名的第二個 TXT Record)
  6. 等第二個生效等了一段時間,因為要看主機的 TTL
  7. 以後還是得使用以上相同流程來更新憑證
  8. 由於此方式僅申請憑證但沒有自動設定到 Web Server,所以後續要自己手動設定

維護:

  • 列出所有憑證:
    ./certbot-auto certificates
  • 更新所有憑證:
    ./certbot-auto renew
  • 更新單一憑證:
    ./certbot-auto renew --cert-name [憑證名稱]
  • 刪除憑證:
    ./certbot-auto delete --cert-name [憑證名稱]

Reference:

Issues:

分類: Web technology | 在〈在 Linux (Ubuntu) 導入 Let’s Encrypt (for Apache)〉中留言功能已關閉