I cannot understand exactly where I made a stupid mistake. I would be very grateful for the advice
val f:File = File("drawable/paystack_mark.png")
// val path:Path = Paths.get("drawable/new_logo.jpg")
val attr = Files.readAttributes<BasicFileAttributes>(file.toPath(), BasicFileAttributes::class.java)
or
val path:Path = Paths.get("drawable/new_logo.jpg")
val attr = Files.readAttributes<BasicFileAttributes>(path, BasicFileAttributes::class.java)
I cannot understand exactly where I made a stupid mistake. I would be very grateful for the advice
Your second example is working fine, I have tried it on windows with a valid path to a .png file.
This is how I used it (just printed the attr.creationTime()):
fun main(args: Array<String>) {
val path: Path = Paths.get("M:\\y\\path\\to\\the\\image.png")
val attr = Files.readAttributes<BasicFileAttributes>(path, BasicFileAttributes::class.java)
println("creation time: " + attr.creationTime())
}
The output was just
creation time: 2018-03-16T13:11:57.40283Z
Keep in mind that you are coding for Android, so maybe the String-versions of the paths are different (see the backslashes for the path on a windows machine).
This is work for me and thank you
// read image from gallary or you can give direct path
getImage()// read image
val contentURI = data?.getData()
var imageFile = File(contentURI?.let { getRealPathFromURI(it) })
val lastModifiedDate: Date = Date(imageFile.lastModified())
println("creation time: " + attr.creationTime())
// get Native URI Function
private fun getRealPathFromURI(contentURI: Uri): String {
var getApplicationContext = getContext()?.getContentResolver();
var result: String
var cursor: Cursor? =
context?.getContentResolver()?.query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath().toString();
} else {
cursor.moveToFirst();
val idx: Int = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
Related
The question says it all, i have a function to which i pass a phone number and the function asigns a predownloaded ringtone to a contact with the passed phone number (if such a contact exists). The problem is that it only works on Android 9 and below, when i run it on an Android 10 it just does nothing, no exceptions, no crashes, nothing..
Heres the functions if anybody knows whats up:
private fun setCustomRingtone(targetContactPhone: String): String {
val lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, targetContactPhone)
val projection = arrayOf<String>(Contacts._ID, Contacts.LOOKUP_KEY)
val data = contentResolver.query(lookupUri, projection, null, null,null)!!
data.moveToFirst()
try {
// Get the contact lookup Uri
val contactID: Long = data.getLong(0)
val lookupKey = data.getString(1);
val contactUri = Contacts.getLookupUri(contactID, lookupKey)
if (contactUri == null){
Log.i(TAG_LABEL, "contactUri je null, invalid arguments?")
return "fail";
}
Log.i(TAG_LABEL, "contact uri: " + contactUri.toString()) // valid and expected Uri
// Get the path of the ringtone you'd like to use
val storage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
Log.i(TAG_LABEL, "out: $storage")
val file = File(storage, "ringtone.mp3")
Log.i(TAG_LABEL, file.exists().toString()) // reasures me that the file exists
val value = Uri.fromFile(file).toString()
Log.i(TAG_LABEL, "size: " + file.length().toString() + " name: " + file.name)
val values = ContentValues()
values.put(Contacts.CUSTOM_RINGTONE, value)
// TRIED A COMBINATION OF THESE, THEY DON'T WORK
// values.put(MediaStore.MediaColumns.TITLE, file.nameWithoutExtension);
// values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg");
// values.put(MediaStore.MediaColumns.SIZE, file.length())
// values.put(MediaStore.Audio.Media.IS_RINGTONE, true)
// values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false);
// values.put(MediaStore.Audio.Media.IS_ALARM, false);
// values.put(MediaStore.Audio.Media.IS_MUSIC, false);
// values.put(MediaStore.Audio.Media.ARTIST, "example")
val res = contentResolver.update(contactUri, values, null, null)
Log.i(TAG_LABEL, "content resolver res: " + res.toString())
}finally {
data.close();
}
return "succes"
}
Once again, it runs fine on Android 9 and below. Also, i can't seem to find any information in the official documentation that mentions changes in Contacts services or similar.
In dual-SIM card mobiles I manage to differentiate SIM cards in the calllog using the PHONE_ACCOUNT_ID property as shown in the code below. Now I need to know what SIM card actually was use (1 or 2) to make or receive the call. PHONE_ACCOUNT_ID shows something like this 8953011201104578086F for and one SIM card and similar, but no equal to the other. This was tested in a Samsung mobile:
fun readCallLog() {
val cursor = context.contentResolver.query(CallLog.Calls.CONTENT_URI,null, null, null, CallLog.Calls.DATE + " DESC")
val number = cursor?.getColumnIndex(CallLog.Calls.NUMBER)
val date = cursor?.getColumnIndex(CallLog.Calls.DATE)
val type = cursor?.getColumnIndex(CallLog.Calls.TYPE)
val account_id = cursor?.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ID)
val tmp : MutableList<List<String?>> = mutableListOf()
while (cursor?.moveToNext() == true ) {
val call_number = if (number != null) cursor.getString(number) else ""
val call_date = if(date != null) cursor.getString(date) else ""
val call_type = if(type != null) cursor.getInt(type).toString() else ""
val call_account_id = if(account_id != null) cursor.getString(account_id) else ""
tmp.add( listOf(call_number, call_date, call_type, call_account_id))
}
}
You can get information on SIM cards with SubscriptionManager.getActiveSubscriptionInfoList().
On some devices, Call.PHONE_ACCOUNT_ID equals subscriptionInfo.getSubscriptionId(), however on other devices (your case) subscriptionInfo.getIccId() is a substring of it, so you need to check both.
See also SubscriptionManager reference.
The official way is to check the account-id (documentation here), but on some devices it just returns the SIM card slot index, so here's the code with a workaround (from Android 6.0 (Marshmallow)):
fun getSimSlotIndexFromAccountId(context: Context, accountIdToFind: String): Int {
// This is actually the official data that should be found, as on the emulator, but sadly not all phones return here a proper value
val telecomManager = context.getSystemService<TelecomManager>()
telecomManager.callCapablePhoneAccounts.forEachIndexed { index, account: PhoneAccountHandle ->
val phoneAccount: PhoneAccount = telecomManager.getPhoneAccount(account)
val accountId: String = phoneAccount.accountHandle
.id
if (accountIdToFind == accountId) {
return index
}
}
accountIdToFind.toIntOrNull()?.let {
if (it >= 0)
return it
}
return -1
}
Usage:
val simIdColumnIndex = callLogCursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ID)
val accountId: String = callLogCursor.getString(simIdColumnIndex)
val simCardSlotIndex = getSimSlotIndexFromAccountId(applicationContext, accountId)
I've reported about this issue (that some devices don't follow official API) here:
Bug: on some devices, PHONE_ACCOUNT_ID just returns the SIM-card slot index
On this line in
val contactUri: Uri? = data.data
…
val cursor = requireActivity().contentResolver
.query(**contactUri**, queryFields, null, null, null)
Type mismatch required Uri got Uri
How do you fix this?
Code sample
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when {
resultCode != Activity.RESULT_OK -> return
requestCode == REQUEST_CONTACT && data != null -> {
val contactUri: Uri? = data.data
// Specify which fields query to return values for.
val queryFields = arrayOf(ContactsContract.Contacts.DISPLAY_NAME)
// Perform query
val cursor = requireActivity().contentResolver
.query(contactUri, queryFields, null, null, null)
cursor?.use {
// Double-check that you actually got results
if (it.count == 0) {
return
}
// Pull out the first column of the first row of data
it.moveToFirst()
val suspect = it.getString(0)
crime.suspect = suspect
crimeDetailViewModel.saveCrime(crime)
suspectButton.text = suspect
}
}
}
}
Either try removing the ? after your uri so:
val contactUri: Uri = data.data
instead of
val contactUri: Uri? = data.data
Or if that doesn't work, check this answer out
Background
We allow the user to create some text that will get converted to HTML, using a rich-text editor library (called Android-RTEditor).
The output HTML text is saved as is on the server and the device.
Because on some end cases, there is a need to show a lot of this content (multiple instances), we wish to also save a "preview" version of this content, meaning it will be much shorter in length (say 120 of normal characters, excluding the extra characters for the HTML tags, which are not counted).
What we want is a minimized version of the HTML. Some tags might optionally be removed, but we still want to see lists (numbered/bullets), no matter what we choose to do, because lists do show like text to the user (the bullet is a character, and so do the numbers with the dot).
The tag of going to next line should also be handled , as it's important to go to the next line.
The problem
As opposed to a normal string, where I can just call substring with the required number of characters, on HTML it might ruin the tags.
What I've tried
I've thought of 2 possible solutions for this:
Convert to plain text (while having some tags handled), and then truncate : Parse the HTML, and replacing some tags with Unicode alternatives, while removing the others. For example, instead of a bullet-list, put the bullet character (maybe this), and same for numbered list (put numbers instead). All the other tags would be removed. Same goes for the tag of going to the next line (""), which should be replaced with "\n". After that, I could safely truncate the normal text, because there are no more tags that could be ruined.
Truncate nicely inside the HTML : Parse the HTML, while identifying the text within it, and truncate it there and closing all tags when reaching the truncation position. This might even be harder.
I'm not sure which is easier, but I can think of possible disadvantages for each. It is just a preview though, so I don't think it matters much.
I've searched the Internet for such solutions, to see if others have made it.
I've found some links that talk about "cleaning" or "optimizing" HTML, but I don't see they can handle replacing them or truncating them. Not only that, but since it's HTML, most are not related to Android, and use PHP, C#, Angular and others as their language.
Here are some links that I've found:
Java Library to truncate html strings?
how to truncate HTML string without leaving it malformated?
The questions
Are those solutions that I've written possible? If so, is there maybe a known way to implement them? Or even a Java/Kotlin/Android library? How hard would it be to make such a solution?
Maybe other solution I haven't thought about?
EDIT:
I've also tried using an old code I've made in the past (here), which parses XML. Maybe it will work. I also try now to investigate some third party libraries for parsing HTML, such as Jsoup. I think it can help with the truncating, while supporting "faulty" HTML inputs.
OK, I think I got it, using my old code for converting XML string into an object . It would still be great to see more robust solutions, but I think what I got is good enough, at least for now.
Below code uses it (origininal XmlTag class available here) :
XmlTagTruncationHelper.kt
object XmlTagTruncationHelper {
/**#param maxLines max lines to permit. If <0, means there is no restriction
* #param maxTextCharacters max text characters to permit. If <0, means there is no restriction*/
class Restriction(val maxTextCharacters: Int, val maxLines: Int) {
var currentTextCharactersCount: Int = 0
var currentLinesCount: Int = 0
}
#JvmStatic
fun truncateXmlTag(xmlTag: XmlTag, restriction: Restriction): String {
if (restriction.maxLines == 0 || (restriction.maxTextCharacters >= 0 && restriction.currentTextCharactersCount >= restriction.maxTextCharacters))
return ""
val sb = StringBuilder()
sb.append("<").append(xmlTag.tagName)
val numberOfAttributes = if (xmlTag.tagAttributes != null) xmlTag.tagAttributes!!.size else 0
if (numberOfAttributes != 0)
for ((key, value) in xmlTag.tagAttributes!!)
sb.append(" ").append(key).append("=\"").append(value).append("\"")
val numberOfInnerContent = if (xmlTag.innerTagsAndContent != null) xmlTag.innerTagsAndContent!!.size else 0
if (numberOfInnerContent == 0)
sb.append("/>")
else {
sb.append(">")
for (innerItem in xmlTag.innerTagsAndContent!!) {
if (restriction.maxTextCharacters >= 0 && restriction.currentTextCharactersCount >= restriction.maxTextCharacters)
break
if (innerItem is XmlTag) {
if (restriction.maxLines < 0)
sb.append(truncateXmlTag(innerItem, restriction))
else {
// Log.d("AppLog", "xmlTag:" + innerItem.tagName + " " + innerItem.innerTagsAndContent?.size)
var needToBreak = false
when {
innerItem.tagName == "br" -> {
++restriction.currentLinesCount
needToBreak = restriction.currentLinesCount >= restriction.maxLines
}
innerItem.tagName == "li" -> {
++restriction.currentLinesCount
needToBreak = restriction.currentLinesCount >= restriction.maxLines
}
}
if (needToBreak)
break
sb.append(truncateXmlTag(innerItem, restriction))
}
} else if (innerItem is String) {
if (restriction.maxTextCharacters < 0)
sb.append(innerItem)
else
if (restriction.currentTextCharactersCount < restriction.maxTextCharacters) {
val str = innerItem
val extraCharactersAllowedToAdd = restriction.maxTextCharacters - restriction.currentTextCharactersCount
val strToAdd = str.substring(0, Math.min(str.length, extraCharactersAllowedToAdd))
if (strToAdd.isNotEmpty()) {
sb.append(strToAdd)
restriction.currentTextCharactersCount += strToAdd.length
}
}
}
}
sb.append("</").append(xmlTag.tagName).append(">")
}
return sb.toString()
}
}
XmlTag.kt
//based on https://stackoverflow.com/a/19115036/878126
/**
* an xml tag , includes its name, value and attributes
* #param tagName the name of the xml tag . for example : <a>b</a> . the name of the tag is "a"
*/
class XmlTag(val tagName: String) {
/** a hashmap of all of the tag attributes. example: <a c="d" e="f">b</a> . attributes: {{"c"="d"},{"e"="f"}} */
#JvmField
var tagAttributes: HashMap<String, String>? = null
/**list of inner text and xml tags*/
#JvmField
var innerTagsAndContent: ArrayList<Any>? = null
companion object {
#JvmStatic
fun getXmlFromString(input: String): XmlTag? {
val factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
val xpp = factory.newPullParser()
xpp.setInput(StringReader(input))
return getXmlRootTagOfXmlPullParser(xpp)
}
#JvmStatic
fun getXmlRootTagOfXmlPullParser(xmlParser: XmlPullParser): XmlTag? {
var currentTag: XmlTag? = null
var rootTag: XmlTag? = null
val tagsStack = Stack<XmlTag>()
xmlParser.next()
var eventType = xmlParser.eventType
var doneParsing = false
while (eventType != XmlPullParser.END_DOCUMENT && !doneParsing) {
when (eventType) {
XmlPullParser.START_DOCUMENT -> {
}
XmlPullParser.START_TAG -> {
val xmlTagName = xmlParser.name
currentTag = XmlTag(xmlTagName)
if (tagsStack.isEmpty())
rootTag = currentTag
tagsStack.push(currentTag)
val numberOfAttributes = xmlParser.attributeCount
if (numberOfAttributes > 0) {
val attributes = HashMap<String, String>(numberOfAttributes)
for (i in 0 until numberOfAttributes) {
val attrName = xmlParser.getAttributeName(i)
val attrValue = xmlParser.getAttributeValue(i)
attributes[attrName] = attrValue
}
currentTag.tagAttributes = attributes
}
}
XmlPullParser.END_TAG -> {
currentTag = tagsStack.pop()
if (!tagsStack.isEmpty()) {
val parentTag = tagsStack.peek()
parentTag.addInnerXmlTag(currentTag)
currentTag = parentTag
} else
doneParsing = true
}
XmlPullParser.TEXT -> {
val innerText = xmlParser.text
if (currentTag != null)
currentTag.addInnerText(innerText)
}
}
eventType = xmlParser.next()
}
return rootTag
}
/**returns the root xml tag of the given xml resourceId , or null if not succeeded . */
fun getXmlRootTagOfXmlFileResourceId(context: Context, xmlFileResourceId: Int): XmlTag? {
val res = context.resources
val xmlParser = res.getXml(xmlFileResourceId)
return getXmlRootTagOfXmlPullParser(xmlParser)
}
}
private fun addInnerXmlTag(tag: XmlTag) {
if (innerTagsAndContent == null)
innerTagsAndContent = ArrayList()
innerTagsAndContent!!.add(tag)
}
private fun addInnerText(str: String) {
if (innerTagsAndContent == null)
innerTagsAndContent = ArrayList()
innerTagsAndContent!!.add(str)
}
/**formats the xmlTag back to its string format,including its inner tags */
override fun toString(): String {
val sb = StringBuilder()
sb.append("<").append(tagName)
val numberOfAttributes = if (tagAttributes != null) tagAttributes!!.size else 0
if (numberOfAttributes != 0)
for ((key, value) in tagAttributes!!)
sb.append(" ").append(key).append("=\"").append(value).append("\"")
val numberOfInnerContent = if (innerTagsAndContent != null) innerTagsAndContent!!.size else 0
if (numberOfInnerContent == 0)
sb.append("/>")
else {
sb.append(">")
for (innerItem in innerTagsAndContent!!)
sb.append(innerItem.toString())
sb.append("</").append(tagName).append(">")
}
return sb.toString()
}
}
Sample usage:
build.grade
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
...
dependencies{
implementation 'com.1gravity:android-rteditor:1.6.7'
...
}
...
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// val inputXmlString = "<zz>Zhshs<br/>ABC</zz>"
val inputXmlString = "Aaa<br/><b>Bbb<br/></b>Ccc<br/><ul><li>Ddd</li><li>eee</li></ul>fff<br/><ol><li>ggg</li><li>hhh</li></ol>"
// XML must have a root tag
val xmlString = if (!inputXmlString.startsWith("<"))
"<html>$inputXmlString</html>" else inputXmlString
val rtApi = RTApi(this, RTProxyImpl(this), RTMediaFactoryImpl(this, true))
val mRTManager = RTManager(rtApi, savedInstanceState)
mRTManager.registerEditor(beforeTruncationTextView, true)
mRTManager.registerEditor(afterTruncationTextView, true)
beforeTruncationTextView.setRichTextEditing(true, inputXmlString)
val xmlTag = XmlTag.getXmlFromString(xmlString)
Log.d("AppLog", "xml parsed: " + xmlTag.toString())
val maxTextCharacters = 10
val maxLines = 20
val output = XmlTagTruncationHelper.truncateXmlTag(xmlTag!!, XmlTagTruncationHelper.Restriction(maxTextCharacters, maxLines))
afterTruncationTextView.setRichTextEditing(true, output)
Log.d("AppLog", "xml with truncation : maxTextCharacters: $maxTextCharacters , maxLines: $maxLines output: " + output)
}
}
activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"
tools:context=".MainActivity">
<com.onegravity.rteditor.RTEditText
android:id="#+id/beforeTruncationTextView" android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="#11ff0000" tools:text="beforeTruncationTextView"/>
<com.onegravity.rteditor.RTEditText
android:id="#+id/afterTruncationTextView" android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="#1100ff00" tools:text="afterTruncationTextView"/>
</LinearLayout>
And the result:
Below is the input file(csv) looks like:
Carrier_create_date,Message,REF_SHEET_CREATEDATE,7/1/2008
Carrier_create_time,Message,REF_SHEET_CREATETIME,8:53:57
Carrier_campaign,Analog,REF_SHEET_CAMPAIGN,25
Carrier_run_no,Analog,REF_SHEET_RUNNO,7
Below is the list of columns each rows has:
(Carrier_create_date, Carrier_create_time, Carrier_campaign, Carrier_run_no)
Desired output as dataframe:
7/1/2008,8:53:57,25,7
Basically the input file has column name and value on each rows.
What I have tried so far is:
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.hive.HiveContext
import org.apache.spark.{SparkContext, SparkConf}
object coater4CR {
// Define the application Name
val AppName: String = "coater4CR"
// Set the logging level.ERROR)
Logger.getLogger("org.apache").setLevel(Level.ERROR)
def main(args: Array[String]): Unit = {
// define the input parmeters
val input_file = "/Users/gangadharkadam/myapps/NlrPraxair/src/main/resources/NLR_Praxair›2008›3QTR2008›Coater_4›C025007.csv"
// Create the Spark configuration and the spark context
println("Initializing the Spark Context...")
val conf = new SparkConf().setAppName(AppName).setMaster("local")
// Define the Spark Context
val sc = new SparkContext(conf)
// Read the csv file
val inputRDD = sc.wholeTextFiles(input_file)
.flatMap(x => x._2.split(" "))
.map(x => {
val rowData = x.split("\n")
var Carrier_create_date: String = ""
var Carrier_create_time: String = ""
var Carrier_campaign: String = ""
var Carrier_run_no: String = ""
for (data <- rowData) {
if (data.trim().startsWith("Carrier_create_date")) {
Carrier_create_date = data.split(",")(3)
} else if (data.trim().startsWith("Carrier_create_time")) {
Carrier_create_time = data.split(",")(3)
} else if (data.trim().startsWith("Carrier_campaign")) {
Carrier_campaign = data.split(",")(3)
} else if (data.trim().startsWith("Carrier_run_no")) {
Carrier_run_no = data.split(",")(3)
}
}
(Carrier_create_date, Carrier_create_time, Carrier_campaign, Carrier_run_no)
}).foreach(println)
}
}
issues with the above code
when I run the above code I am getting an empty list as below
(,,,)
when I change
Carrier_campaign = data.split(",")(3)
to
Carrier_campaign = data.split(",")(2)
I am getting the below output which is somewhat closer
(REF_SHEET_CREATEDATE,REF_SHEET_CREATETIME,REF_SHEET_CAMPAIGN,REF_SHEET_RUNNO)
(,,,)
some how the above code is not able to pick the last column position from the data row but is working for column positions 0,1,2.
So my questions are-
whats wrong with the above code
whats the efficient approach to read this multiline input and load it in tabular format to database
Appreciate any help/pointers on this. Thanks.