android 檢查play store是否安裝

java

public static boolean isPlayStoreInstalled(Context context){
    try {
        context.getPackageManager()
                .getPackageInfo(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, 0);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}

kotlin

fun isPlayStoreInstalled(context: Context): Boolean {
    return try {
        context.packageManager
            .getPackageInfo(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, 0)
        true
    } catch (e: PackageManager.NameNotFoundException) {
        false
    }
}

build.gradle

    implementation 'com.google.android.gms:play-services-base:18.0.1'

android app關於新版本更新的想法

android app 新版本更新,除了google play store之外, 有些不含play store的, 該怎麼做?

簡單的方式就是使用自動更新套件, 搭配自己手動維護版本訊息, app就能自行更新, 但是該套件需要”安裝權限”, 有很大機率會被拒絕, 當然現在也不能上架play store , 除非說明原因.

app也可以自行檢查有新版本(自己手動維護版本訊息), 若有,也可以導到play store或是自己的網頁下載apk程式自行安裝.


val appPackageName =  getPackageName()
val intent = Intent(Intent.ACTION_VIEW).apply {
  data = Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName")
  setPackage("com.android.vending")
}
startActivity(intent)

另外若app可架play store成功, 可以額外下載play store簽名的apk, 使用該apk, 就可以避開簽名者不明的警告訊息呢.

android app字型不隨系統字型大小起舞

重點有二

  1. 在activity set content之前寫死字型大小
  2. 字型大小: resource的fontscale數值
  val resources: Resources = myactivity.getResources()
  var fontScale:Float = 1.0f
  if (resources != null && resources.getConfiguration().fontScale !== fontScale) {
    val configuration: Configuration = resources.getConfiguration()
    configuration.fontScale = fontScale
    resources.updateConfiguration(configuration, resources.getDisplayMetrics())
  }
 // 在set content之前
 setContent {
 }

android kotlin 呼叫(發起)email app

好多種版本, 終於找到可以用的

  • 第一種寫法(比較適合寫慣java的老師傅)
val email_intent =  Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:"));
    email_intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    email_intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("your@email.com"))
    email_intent.putExtra(Intent.EXTRA_SUBJECT, "問題回報");
    email_intent.putExtra(Intent.EXTRA_TEXT,"");
    try {
      startActivity(Intent.createChooser(email_intent, "請選擇郵件軟體"))
    }catch (e:ActivityNotFoundException){
      Toast.makeText(myactivity, "請確認並設定好郵件收發軟體", Toast.LENGTH_LONG).show()
    }
  • 第二種寫法
val email_intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply {
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    putExtra(Intent.EXTRA_EMAIL, arrayOf("your@email.com"))
    putExtra(Intent.EXTRA_SUBJECT, "問題回報")
    putExtra(Intent.EXTRA_TEXT, "")
  }
  try {
    startActivity(Intent.createChooser(email_intent, "請選擇郵件軟體"))
  } catch ( ex: ActivityNotFoundException) {
    Toast.makeText(myactivity, "請確認並設定好郵件收發軟體", Toast.LENGTH_LONG).show()
  }

android kotlin coroutines

import kotlinx.coroutines.*
fun main() {
   repeat(3) {
        GlobalScope.launch {
            println("Hi from ${Thread.currentThread()}")
        }
    }
}
  • 非同步
import kotlinx.coroutines.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

val formatter = DateTimeFormatter.ISO_LOCAL_TIME
val time = { formatter.format(LocalDateTime.now()) }

suspend fun getValue(): Double {
    println("entering getValue() at ${time()}")
    delay(3000)
    println("leaving getValue() at ${time()}")
    return Math.random()
}

fun main() {
    runBlocking {
        val num1 = getValue()
        val num2 = getValue()
        println("result of num1 + num2 is ${num1 + num2}")
    }
}

同步

import kotlinx.coroutines.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

val formatter = DateTimeFormatter.ISO_LOCAL_TIME
val time = { formatter.format(LocalDateTime.now()) }

suspend fun getValue(): Double {
    println("entering getValue() at ${time()}")
    delay(3000)
    println("leaving getValue() at ${time()}")
    return Math.random()
}

fun main() {
    runBlocking {
        val num1 = async { getValue() }
        val num2 = async { getValue() }
        println("result of num1 + num2 is ${num1.await() + num2.await()}")
    }
}

job

val job: Job = GlobalScope.launch(Dispatchers.Main) {
    // launch coroutine in the main thread
    for (i in 10 downTo 1) { // countdown from 10 to 1
        textView.text = "count down $i ..." // update text
        delay(1000) // wait half a second
    }
    textView.text = "Done!"
}
job.cancel()

android kotlin viewBinding 新做法

寫android總是寫不好, 之前取得view要使用findViewByI的方式, 過了一段時間回頭看, 現在又改了改成使用viewBinding 說是比較快…

  • gradle(module)
buildFeatures {
    viewBinding = true
}
  • main activity
class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}
  • 使用方式變簡單了
// 舊版
val myButton: Button = findViewById(R.id.my_button)
myButton.text = "A button"

// 新版
val myButton: Button = binding.myButton
myButton.text = "A button"

// Best way with view binding and no extra variable
binding.myButton.text = "A button"

android取得藍芽裝置連線狀態

Java

public static boolean isConnected(BluetoothDevice device) {
    try {
        Method m = device.getClass().getMethod("isConnected", (Class[]) null);
        boolean connected = (boolean) m.invoke(device, (Object[]) null);
        return connected;
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

Kotlin

fun isConnected(device: BluetoothDevice): Boolean {
  return try {
    val m: Method = device.javaClass.getMethod("isConnected")
    m.invoke(device) as Boolean
  } catch (e: Exception) {
    throw IllegalStateException(e)
  }
}