티스토리 뷰

결과화면

 

I Pdf파일 업로드

1. PdfAddActivity: FirebaseStorage(클라우드)에 저장 -> 실제 저장 

    private fun uploadPdfToStorage() {
        // upload pdf to firebase storage
        Log.d(TAG, "uploadPdfToStorage: uploading the attached file to firebase storage")
        progressDialog.setMessage("Uploading pdf file")
        progressDialog.show()

        val timestamp = System.currentTimeMillis()
        // pdf path in firebase storage
        val filePathAndName = "Books/$timestamp"
        // storage ref
        val storageReference = FirebaseStorage.getInstance().getReference(filePathAndName)
        storageReference.putFile(pdfUri!!)
            .addOnSuccessListener {taskSnapshot ->
                Log.d(TAG, "uploadPdfToStorage: The file attached has been successfully uploaded")
                // get uri of the file
                val uriTask: Task<Uri> = taskSnapshot.storage.downloadUrl
                while(!uriTask.isSuccessful);
                val uploadedPdfUrl = "${uriTask.result}"

                uploadPdfInfoToDb(uploadedPdfUrl, timestamp)
            }
            .addOnFailureListener{e ->
                Log.d(TAG, "uploadPdfToStorage: failed to upload. Error: ${e.message}")
                progressDialog.dismiss()
                Toast.makeText(this, "Failed to upload the attached file", Toast.LENGTH_SHORT).show()
            }


    }

2.PdfAddActivity - 업로드 목록 불러오기에 필요한 정보를 별도로 RealtimeDB에 저장

private fun uploadPdfInfoToDb(uploadedPdfUrl: String, timestamp: Long) {
        // upload pdf into to firebase db
        Log.d(TAG, "uploadPdfInfoToDb: uploading the file to db")
        progressDialog.setMessage("Uploading the pdf file")

        // get an uid of the current user
        val uid = firebaseAuth.uid
        // setup data to upload
        val hashMap: HashMap<String, Any> = HashMap()
        hashMap["uid"] = "$uid"
        hashMap["id"] = "$timestamp"
        hashMap["title"] = "$title"
        hashMap["description"] = "$description"
        hashMap["categoryId"] = "$selectedCategoryId"
        hashMap["url"] = "$uploadedPdfUrl"
        hashMap["timestamp"] = timestamp
        hashMap["viewCnt"] = 0
        hashMap["downloadCnt"] = 0
        // db ref
        val ref = FirebaseDatabase.getInstance().getReference("Books")
        ref.child("$timestamp")
            .setValue(hashMap)
            .addOnSuccessListener {
                Log.d(TAG, "uploadPdfInfoToDb: uploaded to firebase db")
                progressDialog.dismiss()
                Toast.makeText(this, "Successfully uploaded to firebase db", Toast.LENGTH_SHORT).show()
            }
            .addOnFailureListener {e ->
                Log.d(TAG, "uploadPdfInfoToDb: failed to upload into firebase. Error: ${e.message}")
                progressDialog.dismiss()
                Toast.makeText(this, "Failed to upload the attached file into firebase", Toast.LENGTH_SHORT).show()
            }



    }

3.PdfAddActivity - 선택된 카테고리값 추출

 private fun categoryPickDialog(){
        Log.d(TAG, "categoryPickDialog: Showing pdf category pic dialog")
        //get string array of categories from arrayList
        val categoriesArray = arrayOfNulls<String>(categoryArrayList.size)
        for(i in categoryArrayList.indices){
            categoriesArray[i] = categoryArrayList[i].category
        }
        // alert
        val builder = AlertDialog.Builder(this)
        builder.setTitle("Select category")
            .setItems(categoriesArray){dialog, which ->
                // item click event
                selectedCategoryTitle = categoryArrayList[which].category
                selectedCategoryId = categoryArrayList[which].id
                // set category to textview
                binding.categoryTv.text = selectedCategoryTitle
                Log.d(TAG, "categoryPickDialog: Selected Category Id: $selectedCategoryId, Title: $selectedCategoryTitle")
            }
            .show()
    }

 

II Pdf파일 정보 불러오기

1. ModelPdf 생성 (uid, id, title, description, categoryId, url, timestamp, viewCnt, downloadCnt)

2. MyApplication 

class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
    }
    companion object{
        // create a static method to convert timestamp to the proper date format
        fun formatTimeStamp(timestamp: Long): String{
            val cal = Calendar.getInstance(Locale.ENGLISH)
            cal.timeInMillis = timestamp
            return DateFormat.format("dd/MM/yyyy", cal).toString()
        }
        // get the uploaded file size
        fun loadPdfSize(pdfUrl: String, pdfTitle: String, sizeTv: TextView){
            val TAG = "PDF_SIZE_TAG"

            val ref = FirebaseStorage.getInstance().getReferenceFromUrl(pdfUrl)
            ref.metadata
                .addOnSuccessListener{
                    Log.d(TAG, "loadPdfSize: get metadata")
                    val bytes = StorageMetadata().sizeBytes.toDouble()
                    Log.d(TAG, "loadPdfSize: Size Bytes $bytes")
                    val kb = bytes/1024
                    val mb = kb/1024
                    if(mb>=1){
                        sizeTv.text = "${String.format("%.2f", mb)} MB"
                    }else if(kb>=1){
                        sizeTv.text = "${String.format("%.2f", kb)} KB"
                    }else {
                        sizeTv.text = "${String.format("%.2f", bytes)} bytes"
                    }
                }
                .addOnFailureListener{ e ->
                    Log.d(TAG, "loadPdfSize: Failed to get metadata, ERROR: ${e.message}")
                }
        }
        // get the file and its metadata from firebase storage with uri
        fun loadPdfFromUrlSinglePage(
            pdfUrl: String,
            pdfTitle: String,
            pdfView: PDFView,
            progressBar: ProgressBar,
            pagesTv: TextView?
        ){

            val TAG = "PDF_THUMBNAIL_TAG"

            val ref = FirebaseStorage.getInstance().getReferenceFromUrl(pdfUrl)
            ref.getBytes(Constants.MAX_BYTES_PDF)
                .addOnSuccessListener{ bytes ->
                    Log.d(TAG, "loadPdfSize: Size Bytes $bytes")
                    // Set pdfView
                    pdfView.fromBytes(bytes)
                        .pages(0)
                        .spacing(0)
                        .swipeHorizontal(false)
                        .enableSwipe(false)
                        .onError { t->
                            progressBar.visibility = View.INVISIBLE
                            Log.d(TAG, "loadPdfFromUrlSinglePage: ${t.message}")
                        }
                        .onPageError{ page, t ->
                            progressBar.visibility = View.INVISIBLE
                            Log.d(TAG, "loadPdfFromUrlSinglePage: ${t.message}")
                        }
                        .onLoad { nbPages ->
                            Log.d(TAG, "loadPdfFromUrlSinglePage: pages - $nbPages")
                            progressBar.visibility = View.INVISIBLE
                            // if pagesTv param is not null
                            if(pagesTv != null){
                                pagesTv.text = "$nbPages"
                            }
                        }
                        .load()
                }
                .addOnFailureListener{ e ->
                    Log.d(TAG, "loadPdfSize: Failed to get metadata, ERROR: ${e.message}")
                }
        }
        // get category title with categoryId
        fun loadCategory(categoryId: String, categoryTv: TextView){
            val ref = FirebaseDatabase.getInstance().getReference("Categories")
            ref.child(categoryId)
                .addListenerForSingleValueEvent(object: ValueEventListener{
                    override fun onDataChange(snapshot: DataSnapshot) {
                        val category: String = "${snapshot.child("category").value}"
                        categoryTv.text = category
                    }

                    override fun onCancelled(error: DatabaseError) {
                        TODO("Not yet implemented")
                    }
                }
            )

        }
    }

}

3.AdapterPdfAdmin 데이터 핸들링

    override fun onBindViewHolder(holder: HolderPdfAdmin, position: Int) {
        // get data
        val model = pdfArrayList[position]
        val pdfId = model.id
        val categoryId = model.categoryId
        val title = model.title
        val description = model.description
        val pdfUrl = model.url
        val timestamp = model.timestamp
        //convert timestamp to the selected format
        val formattedDate = MyApplication.formatTimeStamp(timestamp)
        //set data
        holder.titleTv.text = title
        holder.descriptionTv.text = description
        holder.dateTv.text = formattedDate
        //get further details
        MyApplication.loadCategory(categoryId, holder.categoryTv)
        MyApplication.loadPdfFromUrlSinglePage(pdfUrl, title, holder.pdfView, holder.progressBar, null)
        MyApplication.loadPdfSize(pdfUrl, title, holder.sizeTv)
    }

4.PdfListAdminActivity - 업로드한 pdf 목록 카테고리별 불러오기

 private fun loadPdfList() {
        // init arrayList
        pdfArrayList = ArrayList()
        val ref = FirebaseDatabase.getInstance().getReference("Books")
        ref.orderByChild("categoryId").equalTo(categoryId)
            .addListenerForSingleValueEvent(object: ValueEventListener{
                override fun onDataChange(snapshot: DataSnapshot) {
                    pdfArrayList.clear()
                    for(ds in snapshot.children){
                        // get data
                        val model = ds.getValue(ModelPdf::class.java)
                        // add it to list
                        if (model != null) {
                            pdfArrayList.add(model)
                            Log.d(TAG, "onDataChange: ${model.title} ${model.categoryId}")
                        }
                    }
                    // setup adapter
                    adapterPdfAdmin = AdapterPdfAdmin(this@PdfListAdminActivity, pdfArrayList)
                    binding.booksRv.adapter = adapterPdfAdmin
                }
                override fun onCancelled(error: DatabaseError) {
                }
            })
    }

 

느낀점: 중간에 geolocation하다가 emulator를 바꾸면서 이것저것 지연이 되었으며 그동안 pdf 업로드 관련 그나마 존재하던 지식조차 증발했다. 그나마 불러오기하고 잘못된 마크 사용으로 디버깅만 하다 보니 조금 다시 기억이 살아나는 거 같기도 하지만 반복을 무수하게 해야지 체득 가능할 듯하다😅

 

* emulator에 pdf파일 업로드하는 방법 (android 11, api 30 기준)

- 우측 하단 Device File Explorer -> 실행중인 device 선택 -> storage -> emulated -> 0 -> Download - 마우스 우측 클릭 -> Upload

- Emulator -> Files -> Android SDK built for x86 -> Downloads로 들어가면 업로드한 pdf파일 확인 가능

Ref: https://www.youtube.com/channel/UCT132T980-1lhm0hcZFy4ZA