Making a layout

Open app/res/layout/activity_main.xml. You’ll see the layout designer, with a “Hello World!” text at the center of a device screen. The text is an example of a so-called view. Technically, a view is just a class that extends View and draws something on the screen. A view whose purpose is to display other views is called a view group. The classic way of creating Android layouts is to use various ViewGroup classes, such as LinearLayout, FrameLayout and so on. Each of these perform some simple task, such as stacking views after each other horizontally or vertically. To create your layout, you just combine these in creative ways. A newer, fancier technique is the constraint-based layout. Here, you put everything you wish to display into a special ConstraintLayout view group, and then apply constraints telling the system how the views are supposed to be placed relative to each other. Think about them as springs holding the views in place. It's easier to understand if you just try it out: Click the “Hello World!” text. Notice how the four “springs” that show up, these are the view’s constraints. As you see, the constraints hold the view in the center of the screen. Let’s make the text bigger! On the right-hand side of Studio (the “Attributes” panel), find the textAppearance attribute, and select AppCompat.Display1 from the dropdown menu. You can also enter your own text size manually in the textSize attribute if you prefer. Find a Button in the left-hand overview of available views, and drag it onto the layout somewhere below the text. Create constraints: grab the handle at the top of the button and connect the spring to the bottom of the text. Connect the other sides to the edges of the screen. The button will end up centered horizontally, in the middle between the hello text and navigation bar(=the thing with back/home/recents buttons). Replace the text of the button to “Change deadline” by typing the new text into the text attribute in the attributes panel. (Yes, there are two attributes named text. Use the one without the wrench icon on it.) Now, run the app again to see your brand new layout in action! Protip: whenever the lightning bolt next to the play button is yellow, click the lightning bolt to see your changes “instantly”. Next: Interactivity (Java) or Interactivity (Kotlin) LF

Interactivity

Even though we have a fancy layout now, the button doesn’t do anything. How can we write code that updates the text when the button is clicked? Actually, setContentView(…); in MainActivity returns nothing at all, so how can we even access our views from the code? The solution is to give every view an ID, and then do a lookup to get hold of them. Let’s start with the text: In activity_main.xml, click the text view. Change the ID at the top of the Attributes panel to something like “hoursLeftTextView” or whatever you like. (If Studio asks to update references in XML code, answer Yes.) At the top of our class, declare a field TextView hoursLeftTextView;. In onCreate, just below setContentView, do the lookup: hoursLeftTextView = findViewById(R.id.hoursLeftTextView); Next, update the text to “X hours left”: hoursLeftTextView.setText("X hours left"); Note: in Kotlin, skip step 3 and 4. See Kotlin version for info. Run the app again. You should see that the text is now “X hours left”. Now, let’s change the time left every time we click the button! Write a method to handle button clicks in MainActivity, and move the setText line there: public void onChangeDeadlineClicked(View viewThatWasClicked) {    hoursLeftTextView.setText("X hours left"); } In activity_main.xml, click our button and select onChangeDeadlineClicked for the onClick attribute. Now, Android knows what to do when the button is clicked. Run the app and try it out! Use (int) (Math.random() * 100) to get a random number in [0, 100]. Display that number instead of X. Run our deadline app. It’s cool to have numbers changing all the time, but it’s not very useful. Can we count down toward a real deadline instead? Next: Setting and showing a deadline LF

Setting and showing a deadline

Let’s be honest here: dealing with date arithmetic is a pain, and working with Java or Kotlin does not exactly make things easier. In real apps, use JodaTime or JSR-310 to deal with dates. To keep things simple, we’ll use the old java.util.Date class you know from TDT4100 (“the Java course”) and a very dirty hack to calculate the number of hours left. Android Studio will complain, if you find it annoying just add @SuppressWarnings("deprecation") at the top of our class. To ask for a date, the easiest solution is to display a DatePickerDialog. So, when the user taps “Change deadline”, display a date picker dialog. When the user taps OK, the date picker dialog tells us what the user selected (which means we need to listen to the date picker), and we update the deadline. Create a date field called deadline, and initialize it using new Date(). This field will hold the date selected by the user. When our button is clicked, create and display a date picker dialog. There are many ways to create the date picker, we’ll use the variant that expects the following: DatePickerDialog(Context context, OnDateSetListener listener, int year, int month, int dayOfMonth) A few hints: An Activity is a Context. OnDateSetListener is an interface you need to implement somehow. Use an anonymous class or make MainActivity implement it. The people who made Date liked two-digit notation for year, so you need to add 1900 to the output of date.getYear(), and subtract 1900 from the argument you pass to date.setYear(). Don't confuse date.getDay()("day of week") and date.getDate() ("day of month") Remember to show the dialog. Test that it works before moving on. Hint: datePicker.show(); By now, you should have an empty onDateSet method somewhere – either in MainActivity or inside an anonymous class in the DatePickerDialog constructor call. Use it to update the deadline values. Important detail: remember to set time of day to 00:00 or 23:59 or whenever your deadlines are. Use a TimePickerDialog if you want to be fancy, it works just like the date picker dialog. Now we have the correct deadline, but we need to display it somehow. Let's apply our dirty hack for calculate the difference: To keep things (somewhat) tidy, create a method to update the screen: void updateUI() {} and call it at last line of onDateSet. Create a local Date object called now inside our new updateUI method. Get the time in milliseconds from both dates, and subtract. Convert to hours: long hoursLeft = TimeUnit.HOURS.convert(diff, TimeUnit.MILLISECONDS); // Use TimeUnit from java.util.concurrent, not from android.whatever. (If you get this wrong, delete the import line at the top of the file and try again.) Update the text to display the actual number of hours left. Optional: Display the deadline in the title at the top of the screen. setTitle(String title); and SimpleDateFormat.getDateTimeInstance().format(Date date); are your friends. Run our app again. Now you can set the deadline, and the number of days left are updated! All done? Deadline works and everything? Turn your phone sideways, and see what happens… Next: When things go sideways – understanding lifecycle LF

When things go sideways – understanding lifecycle

Open the app, set a deadline, and turn the phone sideways so it switches to landscape mode. What happened?? Just turning the phone sideways made the app forget the deadline! Clearly this must be a bug in Android!? To understand what happens, we need to understand that mobile apps work differently from other apps. As Apple puts it: “Your app can be interrupted at any time. When an interruption occurs, your app should save the current state quickly and precisely so people can seamlessly continue where they left off when they return. “ An “interruption” usually happens when the user doesn’t use your app for a while, or when there’s little battery left and a phone call comes in. Both iOS and Android has plenty of tools to make it as easy as possible to save and restore the current state. You might have noticed already that our onCreate method has a parameter Bundle savedInstanceState. This bundle is used to restore our state. When it is time to save the state, Android will call onSaveInstanceState(Bundle outState). Whatever you put in outState will come back in savedInstanceState when the activity is recreated! So, back to our original question: why does the app forget the deadline when we rotate the phone?? Well… the people who made Android in the first place figured that since every activity should be able to save and restore their state automatically anyway: When the phone is rotated, they decided to simply save the state, throw away the activity and create a new one! This turned out to be a super-easy way to handle practically all configuration changes, including the tricky ones: resizing windows on Chromebooks split screen on tablets moving the activity to a TV screen (HDMI or Chromecast) changing font, scaling factor switching language Next: Save the date! (Kotlin version)

Hello Android Studio!

Android Studio is where all Android development happens. It contains everything you need to develop, run, test and deploy Android apps. If you have worked with IntelliJ before, you’ll probably want to know that Android Studio actually is a fork of IntelliJ IDEA – so almost all keyboard shortcuts and menus are the same. If you’re new to IntelliJ and Studio, you need to learn two shortcuts right now: Ctrl + Shift + A: search for anything in any menu. Alt + Enter: Show suggestions to fix or improve whatever code you have highlighted. If you haven’t done so already, install Android Studio from d.android.com/studio. The installer asks some questions – if you’re not sure what to answer, just stick to the default options. Eventually, you’ll reach the screen titled “Welcome to Android Studio”. Optional: From the welcome screen, go to Configure ➡ Settings ➡ Editor ➡ General ➡ Auto import. Then, turn on “Add unambiguous imports on the fly” and “Optimize imports on the fly”. Now you can focus on writing code, and don’t worry about importing things. To get started: “Start a new Android Studio project”. Let’s call our app “Deadline”, and use “hackerspace-ntnu.no” for Company Domain. Decide if you want to use Kotlin or Java. To use Kotlin, check the box labeled "Include Kotlin support". On the next page, ensure that only “Phone and Tablet” is checked, and choose the oldest version of Android you want to support. For our project, the default is just fine. Then pick an "Empty activity". Stick to the defaults on the "Configure Activity" page. Eventually, your new app project will open in Studio. Click the green Play button near the top to run your app. If you have a real Android phone with you, turn on developer mode and USB debugging. Otherwise, click “Create a virtual device” and follow the instructions. Congratulations, you now have your brand new app on your phone! Next: The anatomy of an Android project