Android- Could not get shared preferences when attaching base context - android

i am working on adding international support to our app, each country has multiple languages and one of them is default, when the app is launched in the first launch the user choose a country then i save it in sharedPreferences with the default language, then he can change the language in the next screen,
in a BaseActivity i'm overriding attachBaseContext() function then i call a function in our LocalHelper that returns a context with the desired configuration,
the problem is i'm not able to fetch the country and the language from sharedPreferences in this level, it returns all time empty preferences, and in other places i get them without problem.
here us the function that returns the context with desired configuration:
fun setupLanguage(context: Context): Context {
val res = context.resources
val config = res.configuration
val json = Json { ignoreUnknownKeys = true }
val currentCountryPreferences = context.getSharedPreferences(
CURRENT_COUNTRY_PREFERENCES, Context.MODE_PRIVATE
)
val currentCountryLanguagePreferences = context.getSharedPreferences(
CURRENT_COUNTRY_LANGUAGE_PREFERENCES, Context.MODE_PRIVATE
)
val currentCountryString = currentCountryPreferences.getString(
CURRENT_COUNTRY_KEY, ""
)
val currentCountryLanguageString = currentCountryLanguagePreferences.getString(
CURRENT_COUNTRY_LANGUAGE_KEY, ""
)
val currentLanguage = try {
currentCountryLanguageString?.let {
json.decodeFromString(
Language.serializer(),
it
)
}
} catch (e: Exception) {
Timber.e(e)
null
}
val currentCountry = try {
currentCountryString?.let {
json.decodeFromString(
Country.serializer(),
it
)
}
} catch (e: Exception) {
Timber.e(e)
null
}
if (currentLanguage != null && currentCountry != null) {
if (!currentLanguage.code.isNullOrEmpty() && !currentCountry.code.isNullOrEmpty()) {
val language = currentLanguage.code
val countryCode = currentCountry.code?.uppercase()
val locale = Locale(language, countryCode)
Locale.setDefault(locale)
config.setLocale(locale)
when (language) {
AppLocal.ENGLISH.language -> {
config.setLayoutDirection(AppLocal.ENGLISH)
}
AppLocal.FRENCH.language -> {
config.setLayoutDirection(AppLocal.FRENCH)
}
AppLocal.ARABIC.language -> {
config.setLayoutDirection(AppLocal.ARABIC)
}
AppLocal.ARABIC_MOROCCO.language -> {
config.setLayoutDirection(AppLocal.ARABIC_MOROCCO)
}
}
}
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.createConfigurationContext(config)
} else {
val resources = context.resources
resources.updateConfiguration(config, resources.displayMetrics)
context
}
}

Related

How can I write to only one user with an existing WebSocket connection instead of to all users?

Here's what I have:
Here, incoming messages are sent to all users.
fun Route.chatSocket(roomController: RoomController) {
webSocket("/chat-socket" ) {
val session = call.sessions.get<ChatSession>()
if (session==null) {
close(CloseReason(CloseReason.Codes.VIOLATED_POLICY,"No session"))
return#webSocket
}
try {
roomController.onJoin(
username = session.username,
sessionId = session.sessionId,
socket = this
)
incoming.consumeEach { frame ->
if (frame is Frame.Text) {
roomController.sendMessage(
senderUsername = session.username,
message = frame.readText()
)
}
}
} catch (e: MemberAlredyExistExpetion) {
call.respond(HttpStatusCode.Conflict)
} finally {
roomController.tryDisconnect(session.username)
}
}
}
And here is the roomController:
class RoomController(
private val messageDataSource: MessageDataSource
) {
private val members = ConcurrentHashMap<String,Member>()
fun onJoin(
username:String,
sessionId:String,
socket: WebSocketSession
) {
if (members.containsKey(username)) {
throw MemberAlredyExistExpetion()
}
members[username] = Member(
username = username,
sessionId = sessionId,
socket = socket
)
}
suspend fun sendMessage(senderUsername: String, message: String) {
members.values.forEach { member ->
val messageEntity = Message(
text = message,
username = senderUsername,
timestamp = System.currentTimeMillis()
)
messageDataSource.insertMessage(messageEntity)
val parsedMessage = Json.encodeToString(messageEntity)
member.socket.send(Frame.Text(parsedMessage))
}
}
Here I have tried to implement a command with which I can write directly to a user. But it don`t work.
fun Route.chatToOne(roomController: RoomController) {
webSocket("/whisper") {
val session = call.sessions.get<ChatSessionToOne>()
if (session==null) {
close(CloseReason(CloseReason.Codes.VIOLATED_POLICY,"No Session"))
return#webSocket
}
try {
incoming.consumeEach { frame ->
if (frame is Frame.Text) {
roomController.sendMessageToOneUser(
senderUsername = session.username,
targetUsername = session.targetusername,
message = frame.readText()
)
}
}
} catch (e: MemberAlredyExistExpetion) {
call.respond(HttpStatusCode.Conflict)
}
}
}
And here the code from the roomController:
suspend fun sendMessageToOneUser(senderUsername: String, targetUsername :String, message: String) {
members.values.forEach { member ->
if (member.username == targetUsername) {
val messageEntity = Message(
text = message,
username = targetUsername,
timestamp = System.currentTimeMillis()
)
messageDataSource.insertMessage(messageEntity)
val parsedMessage = Json.encodeToString(messageEntity)
member.socket.send(Frame.Text(parsedMessage))
}
}
}
How can I write to only one user with an existing WebSocket connection instead of to all users?

How to auto get my country currency symbol with out mentioning the country in kotlin android studio

I am using this code but it return usd=$ instead of pkr=Rs I wanted to get Rs without mention my country or currency
val currency: Currency = Currency.getInstance(Locale.getDefault())
val symbol: String = currency.symbol
As I know, It will work using your device telephony manager.
Here is the working code, You gave to get the county code.
fun Context.getDeviceCountryCode(): String {
var countryCode: String?
val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
countryCode = tm.simCountryIso
if (countryCode != null && countryCode.length == 2) return countryCode.lowercase()
countryCode = if (tm.phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
getCDMACountryIso()
} else {
tm.networkCountryIso
}
if (countryCode != null && countryCode.length == 2) return countryCode.lowercase()
countryCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
resources.configuration.locales[0].country
} else {
resources.configuration.locale.country
}
return if (countryCode.length == 2) countryCode.lowercase() else "us"
}
After that call this method for getting the currency symbol
fun getCurrencySymbol(currencyCode: String?): String? {
val currencySymbol: String?
if (currencyCode == null || currencyCode.isEmpty()) {
currencySymbol = currencyCode
} else {
val currencyLocale: Locale?
val currencyLocaleMap: Map<Currency?, Locale>?
val currency: Currency?
try {
currency = Currency.getInstance(currencyCode)
currencyLocaleMap = getCurrencyLocaleMap()
currencyLocale = currencyLocaleMap[currency]
} catch (e: java.lang.Exception) {
return "$"
}
currencySymbol = if (currency != null && currencyLocale != null) {
currency.getSymbol(currencyLocaleMap[currency])
} else {
currencyCode
}
}
return currencySymbol
}
Here are the calling methods, you can call that method and the method will be return the currency code, According to your device.
countryCurrencySysmbol = getCurrencyIcon(getDeviceCountryCode())

How to fetch files inside hidden folder using Media Store API on Android 11

I need to fetch data inside WhatsApp folders on External Storage.
As i am targeting API Level 30 i am no longer able to access WhatsApp folders on External Storage. I have implemented Storage Access Framework and got Android/media folder Uri and Document File. And using listFiles() i am able to list files but with filter() and sortedByDescending() functions it becomes very slow.
What i have tried?
Used Cursor loader with Projection and Selection Arguments but it only
worked for non hidden folders like WhatsApp Images and WhatsApp Videos
It returns empty cursor for hidden folder .Statuses
Tried replacing MediaStore.Video.Media.EXTERNAL_CONTENT_URI with MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
What is required?
List images and videos from .Statuses folder same as i am listing WhatsApp Images using Media Store in HomeActivity.java
Below is my code
In this activity i get permision to Android/media and set all WhatsApp folders URIs for status fetching and other use, but fetched WhatsApp Images with projection and selection from WhatsApp Images folder
class HomeActivity : AppCompatActivity(), InternetListener, PurchasesUpdatedListener,
CoroutineScope {
private val exceptionHandler = CoroutineExceptionHandler { context, exception ->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
}
private val dataRepository: DataRepository by inject()
val tinyDB: TinyDB by inject()
val REQUEST_CODE = 12123
init {
newNativeAdSetUp = null
}
val sharedViewModel by viewModel<SharedViewModel>()
val viewModel by viewModel<HomeViewModel>()
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
val sdk30PermissionListener = object : PermissionListener {
override fun onPermissionGranted() {
openDocumentTree()
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
handlePermissionsByVersion()
}
private fun handlePermissionsByVersion() {
if (SDK_INT >= Build.VERSION_CODES.R) {
if ((ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED)
) {
//if granted load whatsapp images and some uris setup to viewmodel
loadWhatsAppImages()
if (arePermissionsGranted()) {
if (dataRepository.mrWhatsAppImages == null || dataRepository.mrWhatsAppBusinessImages == null) {
setUpWAURIs()
}
}
} else {
TedPermission.with(this)
.setPermissionListener(sdk30PermissionListener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
.check()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, #Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
if (data != null) {
//this is the uri user has provided us
val treeUri: Uri? = data.data
if (treeUri != null) {
sharedViewModel.treeUri = treeUri
val decoded = Uri.decode(treeUri.toString())
Log.i(LOGTAG, "got uri: ${treeUri.toString()}")
// here we should do some checks on the uri, we do not want root uri
// because it will not work on Android 11, or perhaps we have some specific
// folder name that we want, etc
if (Uri.decode(treeUri.toString()).endsWith(":")) {
showWrongFolderSelection()
return
}
if (!decoded.equals(Constants.WHATSAPP_MEDIA_URI_DECODED)) {
showWrongFolderSelection()
return
}
// here we ask the content resolver to persist the permission for us
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(
treeUri,
takeFlags
)
val treeUriAsString = treeUri.toString()
tinyDB.putString("FOLDER_URI", treeUriAsString)
if (SDK_INT >= Build.VERSION_CODES.R) {
setupPaths()
}
}
}
}
}
private fun setupPaths() {
setUpOverlay()
fetchWhatsAppRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
fetchWhatsAppBusinessRootURIs(
this,
sharedViewModel,
dataRepository,
tinyDB
) {
tinyDB.putBoolean("WARootPathsDone", true)
removeOverlay()
}
}
}
override fun onDestroy() {
dialogHandler.removeCallbacksAndMessages(null)
super.onDestroy()
}
val loadmanagerImages = object : LoaderManager.LoaderCallbacks<Cursor> {
val whatsAppImagesArrayList = arrayListOf<File>()
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
var location: File = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path
)
if (!location.exists()) {
location = File(
Environment.getExternalStorageDirectory()
.toString() + Constants.whatsapp_images_path11
)
}
if (location != null && location.exists()) {
whatsAppImagesArrayList.clear()
Timber.e("checkLoaded-onCreateLoader $id")
if (id == 0) {
var folder = location.absolutePath
val projection = arrayOf(
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.DATE_MODIFIED
)
val selection = MediaStore.Images.Media.DATA + " like ? "
val selectionArgs: String = "%$folder%"
return CursorLoader(
this#HomeActivity,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
arrayOf(selectionArgs),
"${MediaStore.Images.Media.DATE_MODIFIED} DESC"
)
}
}
return null!!
}
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
Timber.e("checkLoaded-onLoadFinished")
var absolutePathOfImage: String
if (loader.id == 0) {
cursor?.let {
val columnIndexData = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
GlobalScope.launch(Dispatchers.Main + exceptionHandler) {
async(Dispatchers.IO + exceptionHandler) {
while (!cursor.isClosed && cursor.moveToNext() == true) {
absolutePathOfImage = cursor.getString(columnIndexData!!)
whatsAppImagesArrayList.add(File(absolutePathOfImage))
}
}.await()
LoaderManager.getInstance(this#HomeActivity).destroyLoader(0)
Timber.e("checkLoaded-Completion")
galleryViewModel.whatsAppImagesList.postValue(whatsAppImagesArrayList)
}
}
}
}
override fun onLoaderReset(loader: Loader<Cursor>) {
}
}
fun loadWhatsAppImages() {
try {
tinyDB.putBoolean("whatsAppMediaLoadCalled", true)
LoaderManager.getInstance(this).initLoader(
0,
null,
loadmanagerImages
)
} catch (e: RuntimeException) {
Log.e("exVideos ", "ex : ${e.localizedMessage}")
}
}
companion object {
const val ANDROID_DOCID = "primary:Android/media/"
const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"
private val androidUri = DocumentsContract.buildDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
val androidTreeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
)
}
private fun openDocumentTree() {
val uriString = tinyDB.getString("FOLDER_URI", "")
when {
uriString == "" -> {
Log.w(LOGTAG, "uri not stored")
askPermission()
}
arePermissionsGranted() -> {
}
else -> {
Log.w(LOGTAG, "uri permission not stored")
askPermission()
}
}
}
// this will present the user with folder browser to select a folder for our data
private fun askPermission() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, androidUri)
startActivityForResult(intent, REQUEST_CODE)
}
private fun arePermissionsGranted(): Boolean {
var uriString = tinyDB.getString("FOLDER_URI", "")
val list = contentResolver.persistedUriPermissions
for (i in list.indices) {
val persistedUriString = list[i].uri.toString()
if (persistedUriString == uriString && list[i].isWritePermission && list[i].isReadPermission) {
return true
}
}
return false
}
private fun showWrongFolderSelection() {
val layoutInflaterAndroid = LayoutInflater.from(this)
val mView = layoutInflaterAndroid.inflate(R.layout.layout_dialog_wrong_folder, null)
val builder = AlertDialog.Builder(this, R.style.ThemePageSearchDialog)
builder.setView(mView)
val alertDialog = builder.show()
alertDialog.setCancelable(false)
val btnOk = mView.findViewById(R.id.tvExit) as TextView
val tvCancel = mView.findViewById(R.id.tvCancel) as TextView
btnOk.setOnClickListener {
alertDialog.dismiss()
openDocumentTree()
}
tvCancel.setOnClickListener {
alertDialog.dismiss()
}
}
private fun setUpWAURIs() {
dataRepository.mrWhatsAppImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppImages")
)
dataRepository.mrWhatsAppVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVN")
)
dataRepository.mrWhatsAppDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppDocs")
)
dataRepository.mrWhatsAppVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppVideo")
)
dataRepository.mrWhatsAppAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppAudio")
)
dataRepository.WhatsAppStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppStatuses")
)
dataRepository.mrWhatsAppBusinessImages =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessImages")
)
dataRepository.mrWhatsAppBusinessVN =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVN")
)
dataRepository.mrWhatsAppBusinessDocs =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessDocs")
)
dataRepository.mrWhatsAppBusinessVideo =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessVideo")
)
dataRepository.mrWhatsAppBusinessAudio =
getDocumentFileFromStringURI(
this,
tinyDB.getString("mrWhatsAppBusinessAudio")
)
dataRepository.WhatsAppBusinessStatuses =
getDocumentFileFromStringURIStatuses(
this,
tinyDB.getString("WhatsAppBusinessStatuses")
)
}
fun setUpOverlay() {
val dialogfragment = FullScreenLoadingDialog()
dialogfragment.isCancelable = false
dialogfragment.setisAdmobAd(true)
val ft: FragmentTransaction =
supportFragmentManager.beginTransaction()
ft.add(dialogfragment, "DialogFragment_FLAG")
ft.commitAllowingStateLoss()
}
fun removeOverlay() {
val fragment: Fragment? = supportFragmentManager.findFragmentByTag("DialogFragment_FLAG")
if (fragment != null && fragment is DialogFragment) {
fragment.dismissAllowingStateLoss()
}
}
fun fetchWhatsAppRootURIs(
context: Context,
sharedViewModel: SharedViewModel,
dataRepository: DataRepository,
tinyDB: TinyDB, completed: () -> Unit
) {
val selectedPackageName = Constants.WHATSAPP_PKG_NAME
val selectedRootName = Constants.WHATSAPP_ROOT_NAME
var waImages: DocumentFile? = null
var waVN: DocumentFile? = null
var waDocs: DocumentFile? = null
var waVideos: DocumentFile? = null
var waAudio: DocumentFile? = null
var waStatus: DocumentFile? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && sharedViewModel.treeUri != null) {
CoroutineScope(Dispatchers.Main).launch {
async(Dispatchers.IO) {
val dir = DocumentFile.fromTreeUri(
context,
sharedViewModel.treeUri!!
)
dir?.listFiles()?.forEach {
if (it.name.equals(selectedPackageName)) {
it.listFiles().forEach {
if (it.name.equals(selectedRootName)) {
it.listFiles().forEach {
if (it.name.equals(Constants.WHATSAPP_MEDIA_FOLDER_NAME)) {
it.listFiles().forEach {
if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_IMAGES)) {
waImages = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VN)) {
waVN = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_DOCUMENTS)) {
waDocs = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_VIDEO)) {
waVideos = it
} else if (it.name.equals(Constants.FOLDER_NAME_WHATSAPP_AUDIO)) {
waAudio = it
} else if (it.name.equals(Constants.FOLDER_NAME_STATUSES)) {
waStatus = it
}
}
}
}
}
}
}
}
}.await()
Timber.e("processStatusFetch:Done")
tinyDB.putString("mrWhatsAppImages", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVN", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppDocs", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppVideo", waImages?.uri.toString())
tinyDB.putString("mrWhatsAppAudio", waImages?.uri.toString())
tinyDB.putString("WhatsAppStatuses", waStatus?.uri.toString())
dataRepository.mrWhatsAppImages = waImages
dataRepository.mrWhatsAppVN = waVN
dataRepository.mrWhatsAppDocs = waDocs
dataRepository.mrWhatsAppVideo = waVideos
dataRepository.mrWhatsAppAudio = waAudio
dataRepository.WhatsAppStatuses = waStatus
completed()
}
}
}
Here i am using .Statuses folder URI to list DocumentFiles and display but this way it is slow
class StatusImageFragment : Fragment(), StatusListener, CoroutineScope {
companion object {
fun newInstance() = StatusImageFragment()
}
val handler = CoroutineExceptionHandler { _, exception ->
Log.d("CoroutineException", "$exception handled !")
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job + handler
private lateinit var job: Job
private var adapterSDK30 = StatusImageAdapterSDK30()
private var no_image: ImageView? = null
private var no_image_txt: TextView? = null
val tinyDB: TinyDB by inject()
val sharedViewModel by viewModel<SharedViewModel>()
private val dataRepository: DataRepository by inject()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
job = Job()
return inflater.inflate(R.layout.status_image_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
swipeRefresh(false, false)
}
public fun swipeRefresh(isReloadRequired: Boolean, isFromModeChanged: Boolean) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (isFromModeChanged) {
status_image_recycler.visibility = View.GONE
progressbar.visibility = View.VISIBLE
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
if (adapterSDK30.listImages == null || adapterSDK30.listImages.size == 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
progressbar.visibility = View.VISIBLE
}
}
if (isReloadRequired) {
processStatusFetchFromChild({
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
postFetchingExecutionSDK30()
})
})
} else {
sharedViewModel.statusImages.observe(viewLifecycleOwner, Observer {
val arrayList = it
adapterSDK30.listImages = arrayList
adapterSDK30.listImages = it
postFetchingExecutionSDK30()
})
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
private fun postFetchingExecutionSDK30() {
progressbar.visibility = View.GONE
status_image_recycler.visibility = View.VISIBLE
if (adapterSDK30!!.listImages != null && adapterSDK30!!.listImages.size > 0) {
no_image?.let {
it.visibility = View.GONE
}
no_image_txt?.let {
it.visibility = View.GONE
}
go_to_app?.let {
it.visibility = View.GONE
}
} else {
no_image?.let {
it.visibility = View.VISIBLE
}
no_image_txt?.let {
it.visibility = View.VISIBLE
}
go_to_app?.let {
it.visibility = View.VISIBLE
}
}
adapterSDK30!!.notifyDataSetChanged()
status_img_swipe.isRefreshing = false
}
override fun onDestroyView() {
job.cancel()
super.onDestroyView()
}
fun processStatusFetchFromChild(completed: () -> Unit) {
val statusSelection = tinyDB.getInt(Constants.status_accounts)
if (statusSelection == 0 || statusSelection == 1) {
if (dataRepository.WhatsAppStatuses == null) {
(activity as StatusActivity).setUpWAURIs()
}
var documentFileStatuses: DocumentFile? = dataRepository.WhatsAppStatuses
if (statusSelection == 1) {
documentFileStatuses = dataRepository.WhatsAppBusinessStatuses
}
if (documentFileStatuses != null) {
launch(Dispatchers.Main) {
val statusImages1 = arrayListOf<DocumentFile>()
async(Dispatchers.IO) {
//this takes time ; want to fetch this same as WhatsApp Gallery
statusImages1.addAll(documentFileStatuses!!.listFiles().filter {
it.mimeType.equals(Constants.MIME_TYPE_IMG_PNG) || it.mimeType.equals(
Constants.MIME_TYPE_IMG_JPG
) || it.mimeType.equals(Constants.MIME_TYPE_IMG_JPEG)
}.sortedByDescending { it.lastModified() })
}.await()
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(statusImages1)
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
} else {
Timber.e("processStatusFetch:Done")
sharedViewModel.statusImages.postValue(arrayListOf<DocumentFile>())
completed()
}
}
}
Please note WhatsApp folder path which i used is
val whatsapp_images_path11 = "/Android/media/“ +"com.whatsapp" +"/WhatsApp/Media/WhatsAppImages/"
How i can use MediaStore in this case so that i don't need to use sort and filter functions of list? Its not important to get java.io File only i can work with URIs as well.
What I have finally implemented, in android 10+ you need to ask the user for your specific directory access. Then you can use this functions to fetch statuses:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun readSDKFrom30(): ArrayList<String> {
val treeUri = DocumentsContract.buildTreeDocumentUri(
EXTERNAL_STORAGE_PROVIDER_AUTHORITY,
"primary:Android/media/com.whatsapp/WhatsApp/Media/.Statuses"
)
val tree = DocumentFile.fromTreeUri(context, treeUri)!!
val pathList = ArrayList<String>()
listFolderContent(tree).forEach { uri ->
val file = createFileFromContentUri(uri)
pathList.add(file.toString())
}
return pathList
}
private fun listFolderContent(folder: DocumentFile): List<Uri> {
return if (folder.isDirectory) {
val files = folder.listFiles().toMutableList()
files.sortByDescending { it.lastModified() }
files.mapNotNull { file ->
if (file.name != null) file.uri else null
}
} else {
emptyList()
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createFileFromContentUri(fileUri: Uri): File {
var fileName = ""
fileUri.let { returnUri ->
context.contentResolver.query(returnUri, null, null, null)
}?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
fileName = cursor.getString(nameIndex)
}
val iStream: InputStream =
context.contentResolver.openInputStream(fileUri)!!
val outputDir: File = context.cacheDir!!
val outputFile = File(outputDir, fileName)
copyStreamToFile(iStream, outputFile)
iStream.close()
return outputFile
}
private fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
Using DocumentFile to handle SAF uries is slow indeed.
Better use DocumentsContract to do so.
Its about twenty times as fast as DocumentFile and about as fast as classic File class stuff.
Using MediaStore for hidden folders should be possible. You cannot create hidden folders with the mediastore. But if you managed to make them not using mediastore you should be able to list files in them using mediastore. Well if they are scanned. And if they belong to your app.

Not able to change language locale in Kotlin

I have 2 string files "en" and "es". When ever I am changing the locale in locale manager class, it saves the new locale successfully but does not reflect changes on refreshing activity.
MyApplication.kt
open class MyApplication : Application() {
init {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(LocaleManagerMew.setLocale(base))
// MultiDex.install(base)
}
override fun onCreate() {
super.onCreate()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
LocaleManagerMew.setLocale(this)
Log.d("app", "onConfigurationChanged: " + newConfig.locale.getLanguage())
}
}
LocaleManagerNew.kt
object LocaleManagerMew {
val SELECTED_LANGUAGE = "MEW_CURRENT_-- USER_LANGUAGE"
var mSharedPreference: SharedPref? = null
var mEnglishFlag = "en"
var mSpanishFlag = "es"
fun setLocale(context: Context?): Context {
return updateResources(context!!, getCurrentLanguage(context)!!)
}
inline fun setNewLocale(context: Context, language: String) {
persistLanguagePreference(context, language)
updateResources(context, language)
}
inline fun getCurrentLanguage(context: Context?): String? {
var mCurrentLanguage: String?
if (mSharedPreference == null)
mSharedPreference = SharedPref(context!!)
mCurrentLanguage = mSharedPreference!!.getSavedLang()
return mCurrentLanguage
}
fun persistLanguagePreference(context: Context, language: String) {
if (mSharedPreference == null)
mSharedPreference = SharedPref(context!!)
mSharedPreference!!.setSavedLang(language)
}
fun updateResources(context: Context, language: String): Context {
var contextFun = context
var locale = Locale(language)
Locale.setDefault(locale)
var resources = context.resources
var configuration = Configuration(resources.configuration)
if (Build.VERSION.SDK_INT >= 17) {
configuration.setLocale(locale)
contextFun = context.createConfigurationContext(configuration)
} else {
configuration.locale = locale
resources.updateConfiguration(configuration, resources.getDisplayMetrics())
}
return contextFun
}
}
BaseActivity.kt
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(LocaleManagerMew.setLocale(base))
}
MainActivity.kt
R.id.spanishCL -> {
sp.setSavedLang("es")
var mCurrentLanguage = LocaleManagerMew.getCurrentLanguage(this#MainActivity.applicationContext)
LocaleManagerMew.setNewLocale(this#MainActivity, LocaleManagerMew.mSpanishFlag)
mContext?.recreate()
}
This is the onClick method call to change english locale to spanish. After recreating acitivity new locale changes does not reflect
Try the below code for updating language from attachBaseContext of every Activity
fun setLanguage(context: Context, language: String): ContextWrapper {
var mContext = context
val localeLang = language.split("_".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val locale: Locale
if (localeLang.size > 1)
locale = Locale(localeLang[0], localeLang[1])
else
locale = Locale(localeLang[0])
val res = mContext.resources
val configuration = res.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(locale)
LocaleList.setDefault(localeList)
configuration.locales = localeList
mContext = mContext.createConfigurationContext(configuration)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale)
mContext = mContext.createConfigurationContext(configuration)
} else {
configuration.locale = locale
res.updateConfiguration(configuration, res.getDisplayMetrics())
}
return ContextWrapper(mContext)
}
And implementation in attachBaseContext of Activity class
// For Language Changing
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase?.let {
Common.setLanguage(
it,
PreferenceManager.getPref<String>(Constants.LANGUAGE_PREFERENCE).toString()
)
})
}
And this is how I restart the activity from fragment
Handler().post {
val intent = activity?.intent
intent?.flags =
Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NO_ANIMATION
activity?.overridePendingTransition(0, 0)
activity?.finish()
activity?.overridePendingTransition(0, 0)
startActivity(intent)
}

WorkManager beginUniqueWork queue not getting called after first work

I'm using WorkManager 1.0.0-alpha02
I've created my worker class like:
class PrintWorker: Worker(){
override fun doWork(): WorkerResult {
try {
val label: String = inputData.getString(LABEL_ARG_CONTENIDO, null)
val isLastLabel: Boolean = inputData.getBoolean(LABEL_ARG_IS_LAST,false)
var result = Printer(applicationContext).print(label)
var outPut: Data = Data.Builder().putString("PrinterResponse",result.printerResponse).putBoolean(LABEL_ARG_IS_LAST,isLastLabel).build()
outputData = outPut
return result.workResult
}catch (e: Exception){
return WorkerResult.FAILURE
}
}
companion object {
const val LABEL_ARG_CONTENIDO = "Label"
const val LABEL_ARG_IS_LAST = "Position"
}
}
and then in my viewmodel I've schedulled the work chain like:
var myQueue: WorkContinuation? = null
for (label in labelEntities){
val newPrintWork = OneTimeWorkRequest.Builder(PrintWorker::class.java)
val builder = Data.Builder()
var data: Data
builder.putString(PrintWorker.LABEL_ARG_CONTENIDO, label.contenido)
if(myQueue == null){
data = builder.build()
newPrintWork.setInputData(data)
myQueue = WorkManager.getInstance().beginUniqueWork(printWorkId,ExistingWorkPolicy.REPLACE,newPrintWork.build())
}
else{
if(labelEntities.indexOf(label) == labelEntities.size - 1)
builder.putBoolean(PrintWorker.LABEL_ARG_IS_LAST, true)
data = builder.build()
newPrintWork.setInputData(data)
myQueue.then(newPrintWork.build())
}
}
myQueue?.enqueue()
finally in another piece of code I'm observing it with:
viewmodel.printStatus.observe(this, Observer { works ->
if(works!= null){
if(works.filter { it.state == State.FAILED}.isNotEmpty()){
MyWorkManager().cancelPrinting()
context?.let { showDialog(MyAlertDialogManager(it).noMacAddressErrorDialog()) }
}
if(works.filter { it.state == State.SUCCEEDED }.size == works.size &&
works.isNotEmpty() &&
works.filter { it.outputData.getBoolean(LABEL_ARG_IS_LAST,false) }.isNotEmpty()){
context?.let { showDialog(MyAlertDialogManager(it).confirmPrintedCorrectly(factura,this)) }
}
}
})
The first work gets done right after enqueueing and returns Worker.WorkerResult.SUCCESS but the rest of the chain doesnt gets called

Categories

Resources