Day displayed in Kotlin instead of variable - android-studio

I am new to kotlin and android studio and i am trying to have the day displayed in a text view. My problme is that only the number 1 to 7 is displayed acording to the current day but not the name of the day what do i have to change to fix this?
val day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fun dayOfWeek() {
val day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
println(
when (day) {
1 -> "Sunday"
2 -> "Monday"
3 -> "Tuesday"
4 -> "Wednesday"
5 -> "Thursday"
6 -> "Friday"
7 -> "Saturday"
else -> "Time has stopped"
}
)
}
tag = findViewById(R.id.Tag)
tag.text = day.toString()

java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API: There are a couple of ways to do it as shown below:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// If you want to deal with the system timezone, you can simply use
// LocalDate.now() instead of LocalDate.now(ZoneId.systemDefault()). In order to
// deal with a specific timezone, replace ZoneId.systemDefault() with the
// applicable ZoneId e.g. ZoneId.of("Asia/Kabul")
LocalDate date = LocalDate.now(ZoneId.systemDefault());
String dow = date.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH);
System.out.println(dow);
// Alternatively,
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH);
dow = dtf.format(date);
System.out.println(dow);
}
}
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.

In your code, day is an Int property.
You have created a function called dayOfWeek() that you never call so that code never runs.
When you do tag.text = day.toString(), it is looking at the day property, not the day variable inside your function. It is usually a bad idea to name a variable the same thing as some property in the same class because it makes the code confusing to read. But that doesn't matter either way in this case because both of them are Ints.
In your dayOfWeek() function, you are using a when expression to covert day to a String, but you are just printing it. You are not storing it in a variable such that it can be used for anything else, like setting it on a TextView.
You can make your function return a String, and use it that way. I would also move your function outside the onCreate() function. It is unusual to create a function inside another function for this kind of task.
val day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tag = findViewById(R.id.Tag)
tag.text = getDayOfWeekText()
}
fun getDayOfWeekText() {
return when (day) {
1 -> "Sunday"
2 -> "Monday"
3 -> "Tuesday"
4 -> "Wednesday"
5 -> "Thursday"
6 -> "Friday"
7 -> "Saturday"
else -> "Time has stopped"
}
}
Or to do this the easy way, and with proper language support:
tag.setText(DateTimeFormatter.ofPattern("EEEE").format(LocalDate.now()))

Related

Unable to display the age of an object that was passed to a kotlin function

I'm just starting to learn kotlin and ran into a problem:
I have a Person class that has two fields
-age (Int data type)
-name (data type String)
there is also a oldUp function where I pass a Person object and increment the object's age field by 10.
Before the end of the program ** I want to display the age of the object that was passed to oldUp **
However, age is not shown.
my code:
class Person(var name: String, var age: Int){
}
fun growOld(human: Person){
human.age+=10
}
fun main(args: Array<String>) {
var human = Person("Kitty",6)
growOld(human)
println(human)
}
If you want to print the age, you can just write: println(human.age).
In your example it might be cleaner to add the growOld method to your class so you can call it on the object. For example:
class Person(var name: String, var age: Int){
fun growOld() {
this.age += 10
}
}
fun main() {
var human = Person("Kitty", 6)
println(human.age) // prints 6
human.growOld()
println(human.age) // prints 16
println(human.name) // prints Kitty
}
The problem is you're trying to print the human object itself. Under the hood, this calls its toString() method - every class has one of these, because it's defined on the type all classes derive from. If you don't override it and provide a nice way to "pretty print" your object, it'll use the default implementation, which is basically a reference to the object in memory.
A lot of classes you use have a nice toString() implementation, e.g. if you print a List you get ["something", "that", "looks", "like", "this"]. But that behaviour needed to be coded in - and you need to do that for your Person class too!
So you can override the default implementation like this:
override fun toString(): String {
// return a String here
}
override means you're taking an existing function and writing your own version of it to use instead - if this doesn't match an existing function you can override, you'll get an error. You'll also get an error if you don't use the override keyword for a function that looks exactly like an existing one in a supertype - it's just to make sure you don't accidentally do the wrong thing. In IntelliJ you can do Ctrl+O to override existing functions if you like.
So you could do something like this:
// inside your Person class
override fun toString(): String {
return "Name: $name, age: $age"
}
and then when you use it in a print statement, or in a string (like "Details: $person" or val details = "Details: " + person) it will call that toString() method and get the string you produced.
Another way to approach this is to use a data class:
data class Person(var name: String, var age: Int)
A data class is a special kind of class where all your "data" goes in the constructor (as properties, either val or var), and then you get some boilerplate stuff for free which uses those properties (and only those properties). Things like an equals() and hashCode() implementation that uses that data - and the relevant thing here, it gives you a toString() implementation that pretty prints name and age. Try it out!
Data classes can be really handy for simple data objects like you have here - but in normal classes, overriding toString() yourself is the general way of doing things. And you can still override a data class's toString if you want - sometimes you might want a more complex representation, or nice formatting, or you might want to only include some properties and ignore others. You're in control of how it prints itself!
And if you just want to print the age property, or print anything at all using the data in your object, then you just need to do what Robin's answer says. You don't need a toString() implementation at all for that (and since this is how you usually use objects, often you won't need to write a toString for your own classes at all)

Dice roller app, formatting attribute id calls using Kotlin

I'm taking a kotlin basics course from the android devs website and we have to create an app that allows you to roll a dice and display an image with the number that rolled.
I can make a condition for each possible roll number, but i feel like that's dumb and with formatted reference id calls it should be done way better but I don't know how to accomplish that in kotlin using Android Studio. Any helpers?
package com.example.diceroller
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
/**
* Calls the onCreate() method to initiate the app in its MainActivity.
* setOnClickListener to look for activity in the window (button presses/taps).
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
/* Toast.makeText(this,"Dice Rolled!", Toast.LENGTH_SHORT).show() | Shows alert (toast) at bottom of the screen. */
}
/*
* Initiates a 'Dice' instance with 6 sides.
* calls the roll() method to call rng between 1 and numSides.
*
*/
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val diceImage: ImageView = findViewById(R.id.imageView)
if (diceRoll == 1){ diceImage.setImageResource(R.drawable.dice_1) }
else if (diceRoll == 2){ diceImage.setImageResource(R.drawable.dice_2) }
//Instead of doing it the "dumb way" I want to call the id depending on diceRoll value.
}
}
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
If you're asking how to add 1 or whatever to "R.drawable.dice_" and then turn that string into a resource ID, then you can follow the accepted answer here: https://stackoverflow.com/a/2414165/13598222
Personally I wouldn't recommend that - it's brittle since the compiler can't connect what you're doing to a specific resource. So it can't warn you if you change the resource name (so your code doesn't match anymore), and things like Proguard can end up stripping out "unused" resources (because it can't tell you're using them) and you have to manually fix that.
Probably the most standard way to do what you're doing is to use a when clause, and just return the drawable ID from that:
val drawableId = when(diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
diceImage.setImageResource(drawableId)
Sure it's a little repetitive, but it's a standard pattern, it's very easy to understand and work with, and you're explicitly handling all the cases by adding the (required) else clause - you could also handle 6 explicitly and make the else throw an out-of-range message, in case your roll function somehow sends an invalid value (which it could, since you're hardcoding six values here, but your Dice class can have an arbitrary number!)
Another way you could do it is to have a lookup, like a Map:
private val diceDrawables = mapOf {
1 to R.drawable.dice_1,
2 to R.drawable.dice_2,
3 to R.drawable.dice_3,
4 to R.drawable.dice_4,
5 to R.drawable.dice_5,
6 to R.drawable.dice_6
}
and then your image setting code can be nice and short:
diceImage.setImageResource(diceDrawables[diceRoll] ?: throw Exception("No image for roll: $diceRoll))
This isn't the "dumb" way to do things, it's neat and tidy and avoids repetition (e.g. repeating the setImageResource call). Getting clever with your code logic can cut down a few lines, but it can also make things much more complex and fragile, and just harder to work with. Sometimes that kind of thing is the right call, but in this situation I think this is probably the best compromise. Your call though!
Create a list using those resource ids.
private fun rollDice() {
val diceImages = listOf(R.drawable.dice_1, R.drawable.dice_2, ...)
val diceImage: ImageView = findViewById(R.id.imageView)
discImage.setImageResource(discImages.random())
}

How to make time slots using kotlin

"StartingTime":"11:00",
"EndingTime":"5:00"
Hello,i have a JSON response in which i have these two strings.What i want to do is I want to make time slots using these startingTime and EndingTime.BTW,these two can change for different responses.I want to make time slots with 2 hrs difference between them.Also I want to add an extra 2 hour after the EndingTime.
Example:
Startime = 11:00
EndingTime = 5:00
Time Slots I need = 11:00-1:00 , 1:00-3:00 , 3:00-5:00 , 5:00-7:00
Also once I get this time slots I want to store and add them in a spinner.
How can I achieve it.Thanks.
You can make a simple data class to represent a time slot.
data class TimeSlot(val startTime: LocalTime, val endTime: LocalTime)
And then write a function that splits it up into as many slots that will fit:
fun TimeSlot.divide(lengthHours: Long): List<TimeSlot> {
require(lengthHours > 0) { "lengthHours was $lengthHours. Must specify positive amount of hours."}
val timeSlots = mutableListOf<TimeSlot>()
var nextStartTime = startTime
while (true) {
val nextEndTime = nextStartTime.plusHours(lengthHours)
if (nextEndTime > endTime) {
break
}
timeSlots.add(TimeSlot(nextStartTime, nextEndTime))
nextStartTime = nextEndTime
}
return timeSlots
}
Note, this simple comparison nextEndTime > endTime won't handle a time range that crosses midnight. You'd have to make this a little more complicated if you want to handle that.
You can look up in other existing questions how to parse the JSON values into LocalTimes and how to populate a Spinner from a List.

NPOI DateCell is 1 second off from time in Excel

I have an excel document with times listed as "02:30" ... however, when iterating through them using NPOI, the DateCellValue flips to "02:29:59" ... This first happens on 1/1/2019 (which correctly stores as "02:30") but then on 1/2/2019 it flips to "02:29:59" ... Does anyone know how to have it simply get the value of the cell without trying to do any voodoo to it? It's obviously taking into account perhaps a leap second, or something? It is, however, clear as day in Excel as "02:30", and at my breakpoint with:
[Model].DepartureDttm = row.GetCell(j).DateCellValue [1/2/2019 2:29:59 AM]
You're not the only one that have faced this problem. Here's a good answer.
You can use the DateUtil.GetJavaDate coming with NPOI to resolve this issue . You can create this basic extension method:
public static class Extensions
{
public static DateTime DateCellValueRounded(this ICell cell)
{
return DateUtil.GetJavaDate(cell.NumericCellValue, false, TimeZone.CurrentTimeZone, true);
}
}
Then use it like this:
DateTime date = row.GetCell(index).DateCellValueRounded;
GetJavaDate signature (the last parameter set to true does the job):
public static DateTime GetJavaDate(double date, bool use1904windowing, TimeZone tz, bool roundSeconds);

Entry element for mm:ss with MonoTouch.Dialog?

I am giving a try to MonoTouch and MonoTouch.Dialog and was wondering what would be the best way to create a Time entry element for minutes and seconds only. The TimeElement only seems to support hours and minutes.
I am very new to the whole framework, so I am wondering if there a way to create a text entry element with sort of a "##:##" mask and use a numeric keypad to populate the minutes and seconds?
A cooler option would be to use a section that would take the user to a view with two "Picker Views" (rotating wheels) for minutes and seconds, but I am not there yet.
Thanks in advance
MonoTouch.Dialog's source code is available on Github (https://github.com/migueldeicaza/MonoTouch.Dialog/tree/master/MonoTouch.Dialog).
There, have a look at the implementation of the DateElement (https://github.com/migueldeicaza/MonoTouch.Dialog/blob/master/MonoTouch.Dialog/Elements.cs#L1827)
To get you a TimeElementMinutesSeconds, all you have to do is (untested) abuse the count down mode. It will offer hours and minutes, but you can just interpret them as minutes and seconds:
public class TimeElementMinutesSeconds : DateTimeElement {
public TimeElementMinutesSeconds (string caption, DateTime date) : base (caption, date)
{
}
public override string FormatDate (DateTime dt)
{
DateTime dtLocal = GetDateWithKind (dt).ToLocalTime ();
return string.Format("{0:##}:{1:##}"dtLocal.Hours, dtLocal.Minutes);
}
public override UIDatePicker CreatePicker ()
{
var picker = base.CreatePicker ();
picker.Mode = UIDatePickerMode.CountDownTimer;
return picker;
}
}
If that doesn't get you near enough, you can create your own picker and return that instead.

Resources