Save the date!

So do we put the deadline in outState and consider ourselves done? Well, we want to keep the deadline even if the entire phone is turned off, don’t we? Instead, we’ll use SharedPreferences. This is essentially a glorified way to save small amounts of data in key:value format on disk. SharedPreferences survive practically everything except a factory reset (and even then we can choose to have it backed up by Google). Let’s try it out: Declare a field SharedPreferences prefs; at the top of our activity class. In onCreate, load the file: prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); Note: If you know that the preferences are only going to be used in this particular Activity, you can use this.getPreferences(MODE_PRIVATE) instead. Preference manager's default shared preferences are shared across the entire app. In onDateSet, convert the deadline into millisecond format, put it into prefs and commit the transaction: prefs.edit() .putLong("deadline", deadline.getTime()) .apply(); Read the deadline in onCreate using prefs.getLong and update our deadline field. Tip: System.currentTimeMillis() returns the current time in millisecond format, and could be a good candidate for a default value. Fetching the deadline is nice and all, but shouldn't we display it too? While we're at it, what about making sure the time left is updated every time the app is presented onscreen, even if the activity is re-used? This is the famous "Android Activity Lifecycle", showing which methods of the Activity are called, and when it happens. In the lower left corner, we can see our onCreate() method, called once Android wants to create the activity. Whenever the app is about to become visible to the user, we can see that onStart() is called first. Override onStart(). Keep the call to super.onStart() Add a call updateUI(). Run our app again. Now the correct deadline is displayed! Let’s finish up our app by adding the animation! Next: Animation 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)

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

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

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

The anatomy of an Android project

In the project overview on the left-hand side, you’ll see quite a few folders and files. Here’s an overview: res/layout/activity_main.xml Here’s the layout for our main activity! We’ll get back to this in a moment. res/mipmap/ic_launcher This is our app’s icon, which will be displayed on the home screen (back in Android 1.x, this was called the “launcher”). It comes in many different sizes. Which size is used depends on the phone. If you create a high-resolution icon, Studio can generate scaled versions to make it automatically look good on any phone. res/values/styles.xml Controls how the app will look (colors, font sizes etc) Right now, we inherit our AppTheme from Theme.AppCompat.Light.DarkActionBar. Because dark apps look better, remove .Light.DarkActionBar to switch to the dark version. If you want, try to get rid of the awful default colors as well! java/no.hackerspace_ntnu.deadline/MainActivity This is where we write the Java/Kotlin code to “communicate” with the user. Long story short: we put views on the screen, and react when the user touches them. Right now, all we do is to tell Android where to find the activity_main.xml layout file. Notice: this class extends AppCompatActivity, which in turn extends Activity. These two classes contain lots of code to make our lives easier. The Activity class ships with every Android device out there, and is technically not part of our app. Google makes changes to this class in almost every new Android version, and after a few years it started getting really hard to keep track of what parts of Activity you could use and how it would behave on different devices. To make this easier, Google made AppCompat, a library (aka. pile of code) that makes almost everything work exactly the same way no matter what device our app is running on. AppCompatActivity is shipped with our app, and is the same no matter what device the app is running on. app/manifests/AndroidManifest.xml This file tells Android what our app is called, what it can do, which permissions it needs and so on. Notice how it uses our icon, app style and other resources from the `res` folder: @resource_type/resource_name The <activity android:name=”.MainActivity”> tag is very interesting. Here, we are telling Android how users can enter our app. In our case, we see that the app can be opened from the home screen (“launcher”). Gradle scripts Gradle is the build system that makes everything work together. Now that you know everything about how Android works (heh), let’s start coding our app! Next: Making a layout LF

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

Age of Freemium

This game is intended as a joke. Please don't take it too seriously. Have you downloaded a game from the top charts recently? Noticed how almost all of them are free to download, but you're constantly bombarded with ads and shops where you can buy stuff that makes the game much easier to win? Well, here's a game that takes all this waaay too far. You'll get: ☑ A hillarious gameplay! ☑ A shop where you can buy stuff! ☑ Ingame currency! (exchange from USD available) ☑ Global leaderboards! ☑ Ads! ☑ Stupid jokes! Think you've got what it takes to become a master in the Age of Freemium? Download now (for free) and find out! Created by Even Klemsdal and Simen Burud as a little side project. Don't expect support or updates, but you can still shoot us an email at aof@simen.codes ;) Warning: this game lets you buy ingame currency for real money, but remember that it's a one-way transfer. Please think before you spend money on freemium games. if (window.location.hash == "#go") window.location = "https://play.google.com/store/apps/details?id=codes.simen.ageoffreemium";

Presis

Presis er appen for deg som venter på bussen. Det er en uoffisiell erstatning for RuterReise, og leverer raskere og bedre sanntids enn Ruters offisielle app. Følg bussen "live" Lett å bruke – sanntid er aldri mer enn et trykk unna Snøstorm? Få vite om det når bussen er forsinket eller kansellert Alltid oppdatert info – aldri trykk refresh igjen! Alarm – mobilen ringer når det er på tide å gå ut døra Legg en rute eller holdeplass på hjemskjermen, så er den enda raskere tilgjengelig Funksjoner som kommer: Tidlig ute? Presis anbefaler steder rundt holdeplassen du bør sjekke ut Se hvor fullt det er på T-banen – før den kommer Stjernemerk en holdeplass eller rute, så ser du avganger sekundet etter du åpner appen Flere ideer? Send mail! OBS! Presis er fortsatt under utvikling, alt er ikke på plass helt ennå. Har du forslag, problemer eller ideer? Send en melding så skal du nok se at det kan ordnes :)