본문으로 바로가기

custom ContentProvider 만들기-3 (다른 앱을 통한 제어)

category Android_app 2021. 2. 2. 15:05

 Custom ContentPrivider 만들기 -2 편에 연속됩니다.

  기존 동일한  앱에  contentProvider , SQLiteOpenHelper ,MainActivity 를 모두 가지고 테스트 했습니다.


  이제 다른앱을 통한 제어를 해보겠습니다. (MainaActivity 부분만 다른 앱으로 옮기도록 하겠습니다.)

 

 

1> 다른앱에서 Uri 및 table name , column name 를  쉽게 접근하기 위한 Contract class를 한개 만들겠습니다.

   코틀린에서는 object 로 추가해야 합니다.

 

object MyContract {
    const val AUTHORITY ="com.leevisual.mykotlin.PROVIDER"
    val BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY)

    object Entry : BaseColumns {
        val TABLE_NAME = "ENGLISH"
        val COLUMN_NAME = "NAME"
        val COLUMN_SCORE = "SCORE"
    }
}

 

 

"Custom ContentPrivider 만들기 -2 " 에 적용한 MainActivity 를 그대로 위의 MyContract 버전으로 수정.

class MainActivity : AppCompatActivity() {
    val TAG = MainActivity::class.java.simpleName

    object MyContract {
        const val AUTHORITY ="com.leevisual.mykotlin.PROVIDER"
        val BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY)

        object Entry : BaseColumns {
            val TABLE_NAME = "ENGLISH"
            val COLUMN_NAME = "NAME"
            val COLUMN_SCORE = "SCORE"
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnQuery = findViewById<Button>(R.id.btn_query)
        val btnInsert = findViewById<Button>(R.id.btn_insert)
        val btnShowAll = findViewById<Button>(R.id.show_all)
        val btnDelete = findViewById<Button>(R.id.delete_column)
        val btnDeleteAll = findViewById<Button>(R.id.delete_all)
        val editName = findViewById<EditText>(R.id.edit_name)
        val editScore = findViewById<EditText>(R.id.edit_score)
        val logMsg = findViewById<TextView>(R.id.log_text)

        btnShowAll.setOnClickListener {
            Log.d(TAG,"Show all columns ")
            val cursor = contentResolver.query(MyContract.BASE_CONTENT_URI,null, null,null,null)

            // show all data.
            with(cursor!!) {
                if (count > 0){
                    logMsg.append("Show data all. \n")
                    while (moveToNext()) {
                        val id = getInt(getColumnIndexOrThrow(BaseColumns._ID))
                        val score = getInt(getColumnIndexOrThrow(MyContract.Entry.COLUMN_SCORE))
                        val name = getString(getColumnIndexOrThrow(MyContract.Entry.COLUMN_NAME))
                        Log.d(TAG, "query name = $name, score = $score")
                        logMsg.append("query id =$id name = $name, score = $score \n")
                    }
                }else{
                    Log.d(TAG, "Data is not existed.")
                }
            }

            Log.d(TAG,"contentProvider - ")
        }

        btnQuery.setOnClickListener {
            Log.d(TAG,"test contentProvider + ")
            // check name
            if(editName.text.isEmpty()){

            }else {
                // show data which  is matched by editName.
                val selection = "${MyContract.Entry.COLUMN_NAME} = ?"
                val selectionArgs = arrayOf("${editName.text.toString()}")

                val cursor = contentResolver.query(MyContract.BASE_CONTENT_URI, null, selection, selectionArgs, null)

                with(cursor!!) {
                    if (count > 0) {
                        logMsg.append("Show data about ${editName.text}. \n")
                        while (moveToNext()) {
                            val id = cursor.getInt(getColumnIndexOrThrow(BaseColumns._ID))
                            val score = this.getInt(getColumnIndexOrThrow(MyContract.Entry.COLUMN_SCORE))
                            val name = getString(getColumnIndexOrThrow(MyContract.Entry.COLUMN_NAME))
                            editScore.setText(score.toString())
                            logMsg.append("query id =$id name = $name, score = $score \n")

                        }
                    } else {
                        logMsg.append("Data is not existed. \n")
                    }
                }
            }

            Log.d(TAG,"contentProvider - ")
        }

        btnInsert.setOnClickListener {
            Log.d(TAG,"insert database")
            val name =  editName.text
            val score = editScore.text

            if(name.isEmpty() || score.isEmpty()){
                Log.d(TAG,"name's or score's value is not defined.")
                Toast.makeText(applicationContext,"name's or score's value is not defined.",Toast.LENGTH_SHORT).show()
            }else {
                // Create a new map of values , where column names are the keys
                val values = ContentValues().apply {
                    put(MyContract.Entry.COLUMN_NAME, name.toString())
                    put(MyContract.Entry.COLUMN_SCORE, score.toString().toInt())
                }

                val uri =contentResolver.insert(MyContract.BASE_CONTENT_URI,values)
                Log.d(TAG,"uri = $uri")
//                logMsg.append("Insert id =$newRowId " + name.toString() + " " + score.toString() + "\n")
            }
        }

        btnDelete.setOnClickListener {
            var strName =  editName.text
            val projection = arrayOf(BaseColumns._ID, MyContract.Entry.COLUMN_NAME, MyContract.Entry.COLUMN_SCORE )

            val selection = "${MyContract.Entry.COLUMN_NAME} = ?"

            val selectionArgs = arrayOf("${strName.toString()}")

            // find id from COLUMN_NAME_TITLE
            val cursor = contentResolver.query(MyContract.BASE_CONTENT_URI,projection,selection,selectionArgs,null)

            with(cursor!!) {
                if(cursor.count > 0) {
                    while (moveToNext()) {
                        // delete column to use _id
                        val id = getInt(getColumnIndexOrThrow(android.provider.BaseColumns._ID))
                        Log.d(TAG, "id = $id")
                        contentResolver.delete( MyContract.BASE_CONTENT_URI,"_id=$id",null)
                        logMsg.append("Delete column of id =$id \n")
                    }
                }else {
                    Log.d(TAG, "$strName is not existed.")
                    logMsg.append("Failed to find " + strName.toString() +". \n")
                }
            }
        }

        btnDeleteAll.setOnClickListener {
            contentResolver.delete(MyContract.BASE_CONTENT_URI, null,null)
            logMsg.append("Delete all items.  \n")
        }
    }
}

 

 

 위코드 설명은 "custom ContentProvider  만들기-2" 참고하세요.

 

하여간 이게 동작을 하네요. Permission 수정도 안했는데..

기존 myKotlin 의 AndroidManifest.xml  provider tag 에 다음이 있어서 동작했네요.

 

<provider
    android:name=".MyContentProvider"
    android:authorities="com.leevisual.mykotlin.PROVIDER"
    android:enabled="true"
    android:exported="true"></provider>

==> exported = "false" 를 넣으니 역시나 동작을 안하네요. 물론 동일 앱에서는 동작합니다.

   myKotlin with custom provider  , myKotlin2 라는 두개의 앱이 있다고 할때.. myKotlin 앱만 

 contentResolver 를 사용해 access 가능합니다.

  exported = true 면 myKotlin2 앱도 access 가능합니다.

 

 

1> 2가지 앱 소스의 AndroidManifest.xml 에  수정사항이 발생하네요.

 1-1> custom provider 포함앱 수정사항

<permission android:name="com.leevisual.mykotlin.PROVIDER.permission.READ"
    android:label="com.xxx.mykotlin custom Provider read"
    android:protectionLevel="normal"/>

<permission android:name="com.leevisual.mykotlin.PROVIDER.permission.WRITE"
    android:label="com.xxx.mykotlin custom Provider write"
    android:protectionLevel="normal"/>
    ........중략

<application
    ........중략
    <provider
        android:name=".MyContentProvider"
        android:authorities="com.leevisual.mykotlin.PROVIDER"
        android:enabled="true"
        android:readPermission="com.leevisual.mykotlin.PROVIDER.permission.READ"
        android:writePermission="com.leevisual.mykotlin.PROVIDER.permission.WRITE"
        android:exported="true"></provider>
</application>

 

permission tag 부분과 provider tag의 readPermission, writePermission 추가했습니다.

 

 1-2> contentResolver를 통한  custom provider  사용앱 수정사항

<uses-permission android:name="com.leevisual.mykotlin.PROVIDER.permission.READ"/>
<uses-permission android:name="com.leevisual.mykotlin.PROVIDER.permission.WRITE"/>

이후 정상 동작하네요.

 

< 정리> 

 custom provider 쪽에서 read/write permission 추가하기
 other app 에서 uses-permission tag를 통한 read/write 추가하기

반응형