2014/11/12

android app sending simple data to other app

在Project appA的MainActivity.java onCreate Method 中為按鈕填入事件

Button btnCall = (Button) findViewById(R.id.btnCall);
btnCall.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {

    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setComponent(new ComponentName("com.example.appB", "com.example.topdf.MainActivity"));
    startActivity(intent);
}
=======
建立 Project appB
將appA 及 appB 程式deploy到測試裝置或模擬器試試,執行appA,按下按鈕就會叫用appB 但如果沒有安裝appB則會異常結束。
另一種方式是如果只知道package而不知用哪個Activity, 則可以用這個寫法,但如果沒有安裝appB仍然會異常結束。
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.example.appB");
startActivity(LaunchIntent);
也可以用傳送資料的方式叫用其他的app
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "Hello I'm appA.");
sendIntent.setType("text/plain");
startActivity(sendIntent);
這樣即使沒有安裝appB,也可以選擇叫用其他app


2014/11/10

在 CentOS 6 安裝 redmine 專案管理工具

原本公司內是使用 bugzilla 做專案的 task/issue tracking 工具,但是 bugzilla 卻沒有 gantt chart 的功能。我們需要的是一個可以取代 bugzilla 的專案管理工具,這要分兩個部份來看,programmer 必須要能使用 Eclipse Mylyn 直接連結、查看、處理跟自己相關的 task/issue,專案管理者要能看到 gantt chart,再加上一些其他的管理工具,survey 後就選擇跟 trac 相近的 redmine。

雖然 redmine 在安裝上並不像 php 專案那麼簡單,但因為內建提供了 gantt chart,網頁使用者界面又比較簡潔,因此就試著安裝 redmine,接下來進行試用,最後就是把 bugzilla 退役。

準備工作

安裝 redmine 之前,必須把 CentOS 基本的套件裝好,通常我們會把開發者工具、kernel 的開發套件都裝上去,還會裝上 EPEL、rpmforge 這兩個 package repository。

redmine 的官方網頁目前提供了兩個版本 2.5.3 以及 2.6.0,我先把兩個版本的原始程式碼取回來。

安裝 redmine

首先把 Ruby on Rails 相關的套件裝好。

yum -y install ruby ruby-devel ImageMagick ImageMagick-devel rubygem-rake

wget http://download.opensuse.org/repositories/home:csbuild:centosextra/CentOS_CentOS-6/home:csbuild:centosextra.repo
mv home:csbuild:centosextra.repo /etc/yum.repos.d/

yum -y install rubygem-bundler

在 MariaDB(或是 MySQL)建立 redmine DB 跟使用者資料。

mysql --user=root --password=dbpassword
create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'dbpassword';
grant all privileges on redmine.* to 'redmine'@'localhost';
quit;

解壓縮 redmine 原始程式碼,一開始我們是先試著安裝 2.6.0 版,但因為到後面一直遇到 email 的設定問題,嘗試多個解決方案的過程中,又使用 2.5.3 版重新安裝了一次,基本上 2.6.0 以及 2.5.3 的安裝過程沒有什麼很大的差異。

tar zxvf redmine-2.6.0.tar.gz
mv redmine-2.6.0 /var/www/redmine

cd /var/www/redmine/config
cp database.yml.example database.yml

設定資料庫使用者名稱、密碼。

vi database.yml

把
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: root
  password: ""
  encoding: utf8

改為

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "dbpassword"
  encoding: utf8

調整 Gemfile 的相依性套件,增加 mongrel, dispatcher

vi /var/www/redmine/Gemfile

在
gem "rbpdf", "~> 1.18.1"
這一行的後面增加兩行

gem 'mongrel', '>= 1.2.0.pre2'
gem 'dispatcher'

安裝 ROR 相關套件,在鍵入 bundle install 這一個步驟的指令之後,console 的畫面會停住,看起來很像是當掉的狀態,而且會停著大約10~20分鐘,搜尋解法後,大家回應只說這可能是 https://rubygems.org/ 的網路問題,所以這個步驟就只能等,沒有別的解決方法。

cd /var/www/redmine
bundle install --without development test

安裝 redmine 資料庫

rake generate_secret_token
RAILS_ENV=production rake db:migrate
# 載入預設資料
RAILS_ENV=production rake redmine:load_default_data

# 輸入 zh-TW

如果先裝了 2.6.0 再重裝 2.5.3,在上面這個步驟,就會遇到 rake 升級後的 版本錯誤,這時候就改用下面這些指令

bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake redmine:load_default_data

調整 redmine 資料夾

mkdir -p tmp tmp/pdf public/plugin_assets
chown -R apache:apache files log tmp public/plugin_assets
chown -R apache:apache /var/www/redmine
chmod -R 755 files log tmp public/plugin_assets

基本上這樣就裝好了,可以直接用 webrick 啟動 redmine。

ruby script/rails server webrick -e production

redmine 網頁會在

http://localhost:3000/

跟 Apache 整合

因為要用Apache當作我的Web Server,所以要透過 Phusion passenger 做和 Ruby on Rail的處理。

首先要安裝Phusion passenger

yum -y install make zlib-devel ruby-devel rubygems ruby-libs apr-devel apr-util-devel httpd-devel mod_dav_svn subversion subversion-ruby automake autoconf curl-devel darcs hg bzr

cd /var/www/redmine/
gem install passenger
passenger-install-apache2-module

畫面看起來很奇怪時, 就鍵入 ! ,因為預設就選擇了 ruby python, 所以就直接 enter。

在/etc/httpd/conf.d/加上redmine.conf,並編輯redmine.conf加入以下的設定。

vi /etc/httpd/conf.d/redmine.conf

LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-4.0.53/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
    PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-4.0.53
    PassengerDefaultRuby /usr/bin/ruby
</IfModule>

RailsBaseURI /redmine
<Directory /var/www/redmine/public>
    # This relaxes Apache security settings.
    AllowOverride all
    Options -MultiViews
</Directory>

在DocumentRoot的路徑下加上redmine的symbolic link

ln -s /var/www/redmine/public /var/www/html/redmine

這個設定過程很重要,因為 redmine 官方只提供了以 virtual host 的方式跟 apache 整合,但我們希望用 http://localhost/redmine/ 的網址方式,連結 redmine,努力兩天後,得到上面的解決方案。

設定 email

首先取得設定檔

cd /var/www/redmine/config
cp configuration.yml.example configuration.yml

因為我們是使用 gmail 帳號,同時也使用了 google apps 代管公司的 email,在設定 email 的時候,會遇到很多狀況,基本上再努力兩天,得到下面的解決方案。

修改 Gemfile.lock 的 mail 版本號碼, 由 2.5.4 改為 2.5.3

vi /var/www/redmine/Gemfile.lock

修改第 7 行
    mail (~> 2.5.3)

修改第 50 行
    mail (2.5.3)

修改後要執行

bundle install --without development test

如果是使用 gmail 帳號,要把 POP3 的功能打開,然後填寫設定

vi /var/www/redmine/config/configuration.yml
    delivery_method: :smtp
    smtp_settings:
       enable_starttls_auto: true
       address: "smtp.gmail.com"
       port: 587
       domain: "smtp.gmail.com"
       authentication: :login
       user_name: "maxkit@gmail.com"
       password: "youremailpassword"

如果是使用 google apps 代管 email 帳號,要把 POP3 的功能打開,然後填寫設定

vi /var/www/redmine/config/configuration.yml
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      enable_starttls_auto: true
      address: "smtp.gmail.com"
      port: 587
      domain: "maxkit.com.tw"
      authentication: :login
      user_name: "maxkit@maxkit.com.tw"
      password: "youremailpassword"

讓 eclipse mylyn 可以使用 redmine

Redmine官網的HowToMylyn裡提到Redmine的V2.x以後需要使用Redmine-Mylyn Connector。但是這個只是服務端的plugin。客戶端的Eclipse plugin有好幾個git repo的clone。目前最活躍的是這兩個。

服務端的Redmine plugin的版本庫地址: https://github.com/danmunn/redmine_mylyn_connector

客戶端的Eclipse plugin的版本庫地址: https://github.com/ljader/redmine-mylyn-plugin

安裝過程如下

cd /var/www/redmine/plugins
git clone git://github.com/danmunn/redmine_mylyn_connector.git
cd ..
bundle install --without development test

Eclipse 的部份則是先下載 plugin: net.sf.redmine_mylyn.p2repository-0.4.0-SNAPSHOT.zip

接下來在 Eclipse -> Help -> Add New Software -> Add -> Archive ,選取 net.sf.redmine_mylyn.p2repository-0.4.0-SNAPSHOT.zip 後就可以安裝 plugin,剩下的部份,就跟一般設定 mylyn repository 一樣了。

結語

雖然 redmine 的界面簡潔,但要安裝 remine 會遇到不少困難,而這些問題,在官方網站卻找不到明確的幫助,反而得要自己慢慢地 google 搜尋,然後測試每一個人說的到底對或錯,才能解決這些奇怪的問題。

2014/11/03

scala: 不在迴圈中使用變數

通常在撰寫迴圈時,最直覺的寫法,就是帶入變數,隨時在迴圈中檢查變數的值,也可以利用變數控制,是否要跳出迴圈,但 functional style programming 的重點,除了希望 programmer 能用更易讀的方式撰寫程式,同時希望程式的效能,可以得到顯著的提昇,因此消滅不必要的迴圈與變數,成了 functional programming 的另一項重要任務,而 java programmer 要學習的,是忘記那些 OO Design Patterns,讓程式碼更精簡。

Java 的定位是商用語言,以往的歷史也證實,在 Server Side 的運算環境中,採用 Java J2EE solution 是個很好的選擇,但也因為是 Server Side 的語言,java programmer 比較不在意使用了多少變數,消耗了多少記憶體,著眼點通常放在要把功能做好,模組切割要合理,系統要穩定,只要選個好一些 Server 多一些的記體體就可以運作了。

Function Programming 讓我重新回到撰寫 C 語言程式碼的心情,C 語言追求卓越的速度,在意自己使用了多少記憶體,有時候甚至還要動用 assembly,相容於 Java 的 Scala 是個雙面人,我們該沿用 Java 帶來的物件導向分析習慣,將系統模組化,接下來在實作時,思考如何撰寫高效率的 FP 程式。

if expression

  var filename = "default.txt"
  if (!args.isEmpty)
    filename = args(0)

如果換個寫法,就可以不需要使用 var,程式也比較短,使用 val 也比較接近 functional style,要使用 variable 之前,要先想一下是不是可以 改寫成 expression,另外要盡可能使用 val,可讓程式更容易 refactor。

  val filename =
    if (!args.isEmpty) args(0)
    else "default.txt"

while loops

unit value 寫成 (),() 的存在,就是跟 java void 不同的地方,因為 greet() 會回傳 Unit, 所以 greet() 就會等於 ()。

  scala> def greet() { println("hi") }
  greet: ()Unit

  scala> greet() == ()
  hi
  res0: Boolean = true

以 java 的習慣,可能會這樣寫

  var line = ""
  while ((line = readLine()) != "")
    println("Read: "+ line)

但因為 line = readLine() 的結果是 () Unit,而 Unit 一定不會等於 "",所以就會永遠是 true

  var line = ""
  do {
    line = readLine()
    println("Read: "+ line)
  } while (line != "")

使用 recursion 取代 while

如果要計算 g.c.d 可以這樣寫

  def gcdLoop(x: Long, y: Long): Long = {
    var a = x
    var b = y
    while (a != 0) {
      val temp = a
      a = b % a
      b = temp
    }
    b
  }

改用 functional style: recursion 撰寫 gcd, 同時可以省去很多不需要的vars。

  def gcd(x: Long, y: Long): Long =
    if (y == 0) x else gcd(y, x % y)

generator

  val filesHere = (new java.io.File(".")).listFiles

  for (file <- filesHere)
    println(file)

file <- filesHere 的語法稱為 generator

如果要對一堆檔案重複進行某個運算,也許我們會很直覺地這樣寫

  for (i <- 0 to filesHere.length - 1)
    println(filesHere(i))

這種寫法的缺點是需要產生一個變數 i,因此這種寫法,在 scala 是不常見的,我們必須修改成,直接對 file collection 做 iteration,而 iteration 可能有下面四種問題:filtering、nested iteration、mid-stream variable binding、producing a new collection。

filtering

取得檔名是 .scala 結尾的 檔案

  val filesHere = (new java.io.File(".")).listFiles
  for (file <- filesHere if file.getName.endsWith(".scala"))
    println(file)

也可以使用兩個以上的 filter

  for (
    file <- filesHere
    if file.isFile
    if file.getName.endsWith(".scala")
  ) println(file)

nested iteration

在 for 裡面,也可以同時使用多個 generator

  def fileLines(file: java.io.File) = 
    scala.io.Source.fromFile(file).getLines().toList

  def grep(pattern: String) =
    for (
      file <- filesHere
      if file.getName.endsWith(".scala");
      line <- fileLines(file)
      if line.trim.matches(pattern) 
    ) println(file +": "+ line.trim)

  grep(".*gcd.*")

mid-stream variable binding

上面的程式碼,重複執行了 line.trim,如果想要只運算一次,就要產生一個變數 trimmed = line.trim,trimmed 用了兩次, 一次在 if, 一次在 println。

  def grep(pattern: String) =
    for {
      file <- filesHere
      if file.getName.endsWith(".scala")
      line <- fileLines(file)
      trimmed = line.trim
      if trimmed.matches(pattern)  
    } println(file +": "+ trimmed)

  grep(".*gcd.*")

producing a new collection

用 yield, 可以把過濾後的結果 array 紀錄起來,結果會得到 Array[File], 因為 filesHere 是 array, file 是 File。yield 必須放在 for 的外面,語法為 for clauses yield body。

  def scalaFiles =
    for {
      file <- filesHere
      if file.getName.endsWith(".scala")
    } yield file

try - catch - finally

在 finally 中關閉 file, 確保檔案一定有被關閉,scala 可以在 finally 裡面 return value, 但會把結果覆蓋掉。

  val file = new FileReader("input.txt")
  try {
    // Use the file
  } finally {
    file.close()
  }

match expression

類似 java 的 switch,預設是 _ ,每個 case 裡面不需要寫 break,不只可以用 int, enum, 也可以用 string,可直接將 match 結果,儲存到變數中。

  val firstArg = if (!args.isEmpty) args(0) else ""

  val friend =
    firstArg match {
      case "salt" => "pepper"
      case "chips" => "salsa"
      case "eggs" => "bacon"
      case _ => "huh?"
    }

  println(friend)

no break and continue

因為 scala 沒有 break 跟 continue,最簡單的方式,就是把 continue 換成 if,把 break 換成 boolean 變數。

  var i = 0
  var foundIt = false

  while (i < args.length && !foundIt) {
    if (!args(i).startsWith("-")) {
      if (args(i).endsWith(".scala"))
        foundIt = true
    }
    i = i + 1
  }

更好的寫法是 recursive 呼叫 seachFrom

  def searchFrom(i: Int): Int =
    if (i >= args.length) -1
    else if (args(i).startsWith("-")) searchFrom(i + 1) 
    else if (args(i).endsWith(".scala")) i
    else searchFrom(i + 1)

  val i = searchFrom(0)

Variable scope

scala 的 scoping rule 跟 java 大部分都一樣
只有一點不同,scala 可在 nested scopes 中定義同樣的 variable name。

  val a = 1;
  {
    val a = 2 // Compiles just fine
    println(a)
  }
  println(a)

但是在 scala interpreter 中,看起來雖然像是同一個 scope,但實際上是不同的,因此在 interpreter 中,變數可重複定義。

  scala> val a = 1
  a: Int = 1

  scala> val a = 2
  a: Int = 2

  scala> println(a)
  2

Reference

Programming In Scala by Martin Odersky, Lex Spoon, and Bill Venners