Tuesday, June 25, 2019

Grid UI Launcher by ConstraintLayout

When we are trying to build a custom launcher, we could think about some layout options like grid, scroll view... In this post, we are implementing the grid layout by android default constraint layout.

1. Draw GuideLines

constraintlayout with grid guideline

ConstraintLayout containing 5x4 grid with 6x5 guideline

guideline attribute percent

Set constraint like the above picture(percent 0.2, 0.4, etc...) to each of vertical, horizontal lines.

2. Grid Element View

Make Custom Element View composed of upper-side Icon image, lower-side app name.

class AppIconView(context:Context) : View(context) {
 private val paint = Paint()
 private val iconDrawRegion = Rect()
 private lateinit var label: String
 lateinit var icon: Bitmap
 lateinit var packageName: String

 init{
  paint.setTypeface(Typeface.create(Typeface.DEFAULT,Typeface.NORMAL))
  paint.setAntiAlias(true)
 }

 fun setAppInfo(packageName:String, label:String ,icon:BitmapDrawable){
  this.packageName = packageName
  this.label = label
  this.icon = icon.bitmap
 }

 override fun onDraw(canvas: Canvas) {
  paint.textSize = width/7f
  val paddingW = width/5
  iconDrawRegion.set(paddingW,paddingW,width-paddingW,width-paddingW)
  canvas.drawBitmap(icon,null,iconDrawRegion,paint)

  val textDrawSize = paint.measureText(label)
  canvas.drawText(label,(width-textDrawSize)/2f,height-(2*paint.textSize),paint)
 }

Above code would make a view seems like simple and conventional launcher. For launcher's other behaviors such as drag and drop, We need some additional code that would also be covered by later post.

3. Compose Simple Activity

 guideLineV = arrayOf(R.id.guide_v0,R.id.guide_v1,R.id.guide_v2,R.id.guide_v3,R.id.guide_v4)  guideLineH = arrayOf(R.id.guide_h0,R.id.guide_h1,R.id.guide_h2,R.id.guide_h3,R.id.guide_h4,R.id.guide_h5)  layout = findViewById(R.id.my_constraint_layout)
 var app_count =0
 val filter = Intent(Intent.ACTION_MAIN)
 filter.addCategory(Intent.CATEGORY_LAUNCHER)
 val allApps = packageManager.queryIntentActivities(filter, 0)
 val cs = ConstraintSet()
 for (info in allApps) {
  val da = AppIconView(this).apply {
   id = View.generateViewId()
   setAppInfo(info.activityInfo.packageName, info.loadLabel(packageManager).toString() ,info.activityInfo.loadIcon(packageManager) as BitmapDrawable)
   
  val col = app_count % 4
  val row = app_count / 4
  if(row>4){ // Because we didn't implement tab for multiple appPage, exceeded apps would be just skipped
   break
  }
  apps[row][col] = da
  da.col = col
  da.row = row
  layout.addView(da)
  cs.connect(da.id,ConstraintSet.START,guideLineV[col],ConstraintSet.START)
  cs.connect(da.id,ConstraintSet.END,guideLineV[col+1],ConstraintSet.START)
  cs.connect(da.id,ConstraintSet.TOP,guideLineH[row],ConstraintSet.TOP)
  cs.connect(da.id,ConstraintSet.BOTTOM,guideLineH[row+1],ConstraintSet.TOP)
  app_count++
 }
 cs.applyTo(layout)

I referenced this article for getting installed apps for custom launcher. We can insert above code to oncreate method, and it's done.

4. Result

Now, Our view would be seems like below.

android launcher grid layout

Friday, June 14, 2019

When you delete project in android studio by mistake

Few minutes ago, I had almost been dying after deleting my project by mistake, I tried some third-party recovery softwares. Though it had restored some files(.gitignore, .class, etc..), really most-helpful files(in my case, kotlin files) were not recovered.

Luckily, I found a solution before shutdown the android studio then it saved me to recover the complete project with all files.

If you delete a project in android studio and are still remaining the studio open, you can recover your project.

On Menubar, VCS -> Local History -> Show History.

Then window like below picture would be shown.

how to recover misdelete by android studio history

(Because I had deleted the project after creating new-project, In history it was recorded as EXTERNAL CHANGE)

Right-click the log, and click revert. it's done.

I hope it would save you.

Thursday, June 13, 2019

Sc create, devcon install Difference

In Window Driver, There are the pnp(plug-and-play) type drivers, and the non-pnp type drivers. When non-pnp type drivers have once installed, they would be supposed to be used continuously without uninstall. On the other hand, pnp drivers are namely for easy plug-in(out). For two different types of driver, two-ways of installing exists.

1. PNP Driver

sc create [service name] binpath=[c:\...] type=[kernel]

sc start [service name]

sc stop [service name]

Because sc command is used for creating wide-range window-services, this is Not used only for the device driver. But this is also useful for the device driver.

2. Non PNP Driver

devcon install [filename.inf] [hardware id]

Saturday, June 8, 2019

When Devcon install command failed on VM

I was missing two basic points for installing driver on virtualbox because I never experienced developing driver before.

1. Test-certificate driver could only be installed after recovery boot. ( on 32bit window it could be simple, see this )

Settings -> Update & Security -> Recovery tab -> Restart now -> Trouble Shoot ->
Advanced Options -> Startup Settings -> Restart -> F7

hardware id location in inf file

Red-marked place should be replaced with real hardware id. it can be checked in device manager.

Way to call Default Launcher by Custom Launcher

It would not be guaranteed to work always properly, though it works well in my case

1. manifest example

<activity ... android:label="@string/app_name" android:name=".activities.LauncherActivity" android:theme="@style/FullscreenTheme">
 <intent-filter>
  <action android:name="android.intent.action.MAIN"/>
  <category android:name="android.intent.category.HOME"/>
  <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>
</activity>

2. custom launcher code example

var launcher_list = packageManager.queryIntentActivities(Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_HOME) },0)
val name = ComponentName(
 //expecting first element of launchers be the default
 launcher_list[0].activityInfo.packageName,
 launcher_list[0].activityInfo.name
)

val i = Intent(Intent.ACTION_MAIN)
i.addCategory(Intent.CATEGORY_LAUNCHER)
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
i.setComponent(name)

startActivity(i)
finish()

[osx command] file or folder monitoring

1. command fswatch [file or folder to monitor] | xargs -I {} echo $(date) {} >> [log file name] ...