Menerima pembaruan lokasi di Android dengan Kotlin

1. Sebelum memulai

Android 10 dan 11 memberi pengguna kontrol yang lebih besar atas aplikasi mereka akses ke lokasi perangkat mereka.

Saat aplikasi yang berjalan di Android 11 meminta akses lokasi, pengguna memiliki empat opsi:

  • Selalu izinkan
  • Izinkan hanya saat aplikasi digunakan (di Android 10)
  • Hanya satu kali (di Android 11)
  • Tolak

Android 10

6a1029175b467c77.pngS

Android 11

73d8cc88c5877c25.pngS

Dalam codelab ini, Anda akan mempelajari cara menerima pembaruan lokasi dan cara mendukung lokasi di versi Android apa pun, terutama Android 10 dan 11. Di akhir codelab, Anda akan memiliki aplikasi yang mengikuti praktik terbaik saat ini untuk mengambil pembaruan lokasi.

Prasyarat

Yang akan Anda lakukan

  • Ikuti praktik terbaik untuk lokasi di Android.
  • Menangani izin akses lokasi latar depan (saat pengguna meminta aplikasi Anda mengakses lokasi perangkat saat aplikasi sedang digunakan).
  • Mengubah aplikasi yang ada guna menambahkan dukungan untuk meminta akses lokasi dengan menambahkan kode untuk berlangganan dan berhenti berlangganan lokasi.
  • Tambahkan dukungan ke aplikasi untuk Android 10 dan 11 dengan menambahkan logika untuk mengakses lokasi di lokasi latar depan atau saat digunakan.

Yang Anda butuhkan

  • Android Studio 3.4 atau yang lebih baru untuk menjalankan kode
  • Perangkat/emulator yang menjalankan pratinjau developer Android 10 dan 11

2. Memulai

Meng-clone repositori project awal

Untuk membantu Anda memulai secepat mungkin, Anda dapat mengembangkan project awal ini. Jika sudah menginstal Git, Anda cukup menjalankan perintah berikut:

 git clone https://github.com/android/codelab-while-in-use-location

Jangan ragu untuk mengunjungi halaman GitHub secara langsung.

Jika tidak memiliki Git, Anda bisa mendapatkan project sebagai file ZIP:

Mengimpor project

Buka Android Studio, pilih "Open an existing Android Studio project" dari layar sambutan, dan buka direktori project.

Setelah project dimuat, Anda juga akan melihat notifikasi bahwa Git tidak melacak semua perubahan lokal. Anda dapat mengklik Abaikan. (Anda tidak akan mendorong perubahan apa pun kembali ke repositori Git.)

Di sudut kiri atas jendela project, Anda akan melihat gambar seperti gambar di bawah ini jika berada dalam tampilan Android. (Jika berada dalam tampilan Project, Anda harus memperluas project untuk melihat hal yang sama.)

fa825dae96c5dc18.png

Ada dua folder (base dan complete). Masing-masing dikenal sebagai "modul".

Perlu diketahui bahwa Android Studio mungkin memerlukan waktu beberapa detik untuk mengompilasi project di latar belakang untuk pertama kalinya. Selama peninjauan, Anda akan melihat pesan berikut di status bar di bagian bawah Android Studio:

c2273e7835c0841a.png

Tunggu hingga Android Studio selesai mengindeks dan membuat project sebelum mengubah kode. Ini akan memungkinkan Android Studio menarik semua komponen yang diperlukan.

Jika Anda mendapatkan perintah yang mengatakan Reload for language changes to take effect? atau yang serupa, pilih Yes.

Memahami project awal

Anda sudah siap untuk meminta lokasi di aplikasi. Gunakan modul base sebagai titik awal. Pada setiap langkah, tambahkan kode ke modul base. Setelah menyelesaikan codelab ini, kode dalam modul base seharusnya cocok dengan konten modul complete. Modul complete dapat digunakan untuk memeriksa pekerjaan Anda atau sebagai referensi jika Anda mengalami masalah apa pun.

Komponen utama meliputi:

  • MainActivity—UI untuk pengguna yang mengizinkan aplikasi mengakses lokasi perangkat
  • LocationService—layanan yang berlangganan dan berhenti berlangganan perubahan lokasi, dan mempromosikan dirinya ke layanan latar depan (dengan notifikasi) jika pengguna keluar dari aktivitas aplikasi. Anda menambahkan kode lokasi di sini.
  • Util—Menambahkan fungsi ekstensi untuk class Location dan menyimpan lokasi di SharedPreferences (lapisan data yang disederhanakan).

Penyiapan emulator

Untuk mengetahui informasi tentang cara menyiapkan Android Emulator, lihat Menjalankan di emulator.

Menjalankan project awal

Jalankan aplikasi Anda.

  1. Hubungkan perangkat Android ke komputer atau mulai emulator. (Pastikan perangkat menjalankan Android 10 atau yang lebih tinggi.)
  2. Di toolbar, pilih konfigurasi base dari pemilih drop-down, lalu klik Run:

99600e9d44527ab.pngS

  1. Perhatikan aplikasi berikut yang muncul di perangkat Anda:

99bf1dae46f99af3.pngS

Anda mungkin melihat bahwa tidak ada informasi lokasi yang muncul di layar output. Hal itu karena Anda belum menambahkan kode lokasi.

3. Menambahkan lokasi

Konsep

Fokus codelab ini adalah menunjukkan cara menerima pembaruan lokasi, dan pada akhirnya mendukung Android 10 dan Android 11.

Namun, sebelum memulai coding, sebaiknya Anda mempelajari dasar-dasarnya.

Jenis akses lokasi

Anda mungkin ingat empat opsi berbeda untuk akses lokasi dari awal codelab. Lihat artinya:

  • Izinkan hanya saat aplikasi digunakan
  • Opsi ini adalah opsi yang direkomendasikan untuk sebagian besar aplikasi. Juga dikenal sebagai "saat digunakan" atau "hanya latar depan" , opsi ini ditambahkan di Android 10 dan memungkinkan developer mengambil lokasi hanya saat aplikasi secara aktif digunakan. Aplikasi dianggap aktif jika salah satu kondisi berikut terpenuhi:
  • Sebuah aktivitas terlihat.
  • Layanan latar depan berjalan dengan notifikasi berkelanjutan.
  • Hanya satu kali
  • Ditambahkan di Android 11, fungsi ini sama dengan Izinkan hanya saat aplikasi digunakan, tetapi untuk waktu terbatas. Untuk mengetahui informasi selengkapnya, lihat Izin satu kali.
  • Tolak
  • Opsi ini mencegah akses ke informasi lokasi.
  • Selalu izinkan
  • Opsi ini memungkinkan akses lokasi sepanjang waktu, tetapi memerlukan izin tambahan untuk Android 10 dan yang lebih baru. Anda juga harus memastikan bahwa Anda memiliki kasus penggunaan yang valid dan mematuhi kebijakan lokasi. Anda tidak akan membahas opsi ini dalam codelab ini, karena ini adalah kasus penggunaan yang lebih jarang. Namun, jika Anda memiliki kasus penggunaan yang valid dan ingin memahami cara menangani lokasi sepanjang waktu dengan benar, termasuk mengakses lokasi di latar belakang, tinjau contoh LocationUpdatesBackgroundKotlin.

Layanan, layanan latar depan, dan binding

Untuk mendukung sepenuhnya pembaruan lokasi Izinkan hanya saat aplikasi digunakan, Anda perlu memperhitungkan saat pengguna keluar dari aplikasi Anda. Jika ingin terus menerima update dalam situasi tersebut, Anda harus membuat Service latar depan dan mengaitkannya dengan Notification.

Selain itu, jika Anda ingin menggunakan Service yang sama untuk meminta pembaruan lokasi saat aplikasi Anda terlihat dan saat pengguna keluar dari aplikasi Anda, Anda harus mengikat/melepaskan Service tersebut ke elemen UI.

Karena codelab ini hanya berfokus untuk mendapatkan pembaruan lokasi, Anda dapat menemukan semua kode yang diperlukan di class ForegroundOnlyLocationService.kt. Anda dapat menjelajahi class tersebut dan MainActivity.kt untuk melihat cara keduanya bekerja sama.

Untuk mengetahui informasi selengkapnya, lihat Ringkasan layanan dan Ringkasan layanan terikat.

Izin

Untuk menerima pembaruan lokasi dari NETWORK_PROVIDER atau GPS_PROVIDER, Anda harus meminta izin pengguna dengan menyatakan masing-masing izin ACCESS_COARSE_LOCATION atau ACCESS_FINE_LOCATION, di file manifes Android Anda. Tanpa izin ini, aplikasi Anda tidak akan dapat meminta akses ke lokasi saat runtime.

Izin tersebut mencakup kasus Satu kali saja dan Izinkan hanya saat aplikasi digunakan saat aplikasi Anda digunakan di perangkat yang menjalankan Android 10 atau yang lebih baru.

Lokasi

Aplikasi Anda dapat mengakses kumpulan layanan lokasi yang didukung melalui class dalam paket com.google.android.gms.location.

Lihat class utama:

  • FusedLocationProviderClient
  • Ini adalah komponen utama dari framework lokasi. Setelah dibuat, Anda akan menggunakannya untuk meminta pembaruan lokasi dan mendapatkan lokasi terakhir yang diketahui.
  • LocationRequest
  • Ini adalah objek data yang berisi parameter kualitas layanan untuk permintaan (interval untuk pembaruan, prioritas, dan akurasi). Lokasi ini akan diteruskan ke FusedLocationProviderClient saat Anda meminta pembaruan lokasi.
  • LocationCallback
  • Fitur ini digunakan untuk menerima notifikasi saat lokasi perangkat telah berubah atau tidak lagi dapat ditentukan. Tindakan ini akan meneruskan LocationResult tempat Anda bisa mendapatkan Location untuk disimpan di database Anda.

Setelah memiliki gambaran dasar tentang apa yang Anda lakukan, mulailah dengan membuat kode.

4. Tambahkan fitur lokasi

Codelab ini berfokus pada opsi lokasi yang paling umum: Izinkan hanya saat aplikasi digunakan.

Untuk menerima update lokasi, aplikasi Anda harus memiliki aktivitas yang terlihat atau layanan yang berjalan di latar depan (dengan notifikasi).

Izin

Tujuan codelab ini adalah menunjukkan cara menerima pembaruan lokasi, bukan cara meminta izin akses lokasi, sehingga kode berbasis izin sudah ditulis untuk Anda. Jangan ragu untuk melewatinya jika Anda sudah memahaminya.

Berikut adalah sorotan izin (tidak diperlukan tindakan untuk bagian ini):

  1. Deklarasikan izin yang Anda gunakan di AndroidManifest.xml.
  2. Sebelum mencoba mengakses informasi lokasi, periksa apakah pengguna telah memberikan izin kepada aplikasi Anda untuk melakukannya. Jika aplikasi Anda belum menerima izin, minta akses.
  3. Menangani pilihan izin pengguna. (Anda dapat melihat kode ini di MainActivity.kt.)

Jika Anda menelusuri TODO: Step 1.0, Review Permissions di AndroidManifest.xml atau MainActivity.kt, Anda akan melihat semua kode yang ditulis untuk izin.

Untuk mengetahui informasi selengkapnya, lihat Ringkasan izin.

Sekarang, mulailah menulis beberapa kode lokasi.

Meninjau variabel utama yang diperlukan untuk pembaruan lokasi

Dalam modul base, telusuri TODO: Step 1.1, Review variables di

File ForegroundOnlyLocationService.kt.

Anda tidak perlu melakukan tindakan apa pun dalam langkah ini. Anda hanya perlu meninjau blok kode berikut, beserta komentar, untuk memahami class dan variabel utama yang Anda gunakan untuk menerima update lokasi.

// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest

// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback

// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null

Meninjau inisialisasi FusedLocationProviderClient

Dalam modul base, telusuri TODO: Step 1.2, Review the FusedLocationProviderClient dalam file ForegroundOnlyLocationService.kt. Kode akan terlihat seperti ini:

// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

Seperti yang disebutkan dalam komentar sebelumnya, ini adalah class utama untuk mendapatkan pembaruan lokasi. Variabel sudah diinisialisasi untuk Anda, tetapi penting untuk meninjau kodenya guna memahami cara inisialisasinya. Anda akan menambahkan beberapa kode di sini nanti untuk meminta pembaruan lokasi.

Melakukan inisialisasi LocationRequest

  1. Dalam modul base, telusuri TODO: Step 1.3, Create a LocationRequest dalam file ForegroundOnlyLocationService.kt.
  2. Tambahkan kode berikut setelah komentar.

Kode inisialisasi LocationRequest menambahkan kualitas parameter layanan ekstra yang Anda perlukan untuk permintaan (interval, waktu tunggu maks, dan prioritas).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().apply {
   // Sets the desired interval for active location updates. This interval is inexact. You
   // may not receive updates at all if no location sources are available, or you may
   // receive them less frequently than requested. You may also receive updates more
   // frequently than requested if other applications are requesting location at a more
   // frequent interval.
   //
   // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
   // targetSdkVersion) may receive updates less frequently than this interval when the app
   // is no longer in the foreground.
   interval = TimeUnit.SECONDS.toMillis(60)

   // Sets the fastest rate for active location updates. This interval is exact, and your
   // application will never receive updates more frequently than this value.
   fastestInterval = TimeUnit.SECONDS.toMillis(30)

   // Sets the maximum time when batched location updates are delivered. Updates may be
   // delivered sooner than this interval.
   maxWaitTime = TimeUnit.MINUTES.toMillis(2)

   priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
  1. Baca komentar untuk memahami cara kerjanya.

Melakukan inisialisasi LocationCallback

  1. Dalam modul base, telusuri TODO: Step 1.4, Initialize the LocationCallback dalam file ForegroundOnlyLocationService.kt.
  2. Tambahkan kode berikut setelah komentar.
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult) {
        super.onLocationResult(locationResult)

        // Normally, you want to save a new location to a database. We are simplifying
        // things a bit and just saving it as a local variable, as we only need it again
        // if a Notification is created (when the user navigates away from app).
        currentLocation = locationResult.lastLocation

        // Notify our Activity that a new location was added. Again, if this was a
        // production app, the Activity would be listening for changes to a database
        // with new locations, but we are simplifying things a bit to focus on just
        // learning the location side of things.
        val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
        intent.putExtra(EXTRA_LOCATION, currentLocation)
        LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)

        // Updates notification content if this service is running as a foreground
        // service.
        if (serviceRunningInForeground) {
            notificationManager.notify(
                NOTIFICATION_ID,
                generateNotification(currentLocation))
        }
    }
}

LocationCallback yang Anda buat di sini adalah callback yang akan dipanggil FusedLocationProviderClient saat pembaruan lokasi baru tersedia.

Dalam callback, pertama-tama Anda akan mendapatkan lokasi terbaru menggunakan objek LocationResult. Setelah itu, beri tahu Activity tentang lokasi baru tersebut menggunakan siaran lokal (jika aktif) atau perbarui Notification jika layanan ini berjalan sebagai Service latar depan.

  1. Baca komentar untuk memahami fungsi setiap bagian.

Berlangganan perubahan lokasi

Setelah melakukan inisialisasi semuanya, Anda harus memberi tahu FusedLocationProviderClient bahwa Anda ingin menerima update.

  1. Dalam modul base, telusuri Step 1.5, Subscribe to location changes dalam file ForegroundOnlyLocationService.kt.
  2. Tambahkan kode berikut setelah komentar.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())

Panggilan requestLocationUpdates() memberi tahu FusedLocationProviderClient bahwa Anda ingin menerima pembaruan lokasi.

Anda mungkin mengenali LocationRequest dan LocationCallback yang Anda tentukan sebelumnya. Parameter tersebut memberi tahu FusedLocationProviderClient parameter kualitas layanan untuk permintaan Anda dan apa yang harus dipanggil saat memiliki update. Terakhir, objek Looper menentukan thread untuk callback.

Anda mungkin juga melihat bahwa kode ini berada dalam pernyataan try/catch. Metode ini memerlukan pemblokiran tersebut karena SecurityException terjadi saat aplikasi Anda tidak memiliki izin untuk mengakses informasi lokasi.

Berhenti berlangganan perubahan lokasi

Jika aplikasi tidak lagi memerlukan akses ke informasi lokasi, Anda harus berhenti berlangganan pembaruan lokasi.

  1. Dalam modul base, telusuri TODO: Step 1.6, Unsubscribe to location changes dalam file ForegroundOnlyLocationService.kt.
  2. Tambahkan kode berikut setelah komentar.
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
   if (task.isSuccessful) {
       Log.d(TAG, "Location Callback removed.")
       stopSelf()
   } else {
       Log.d(TAG, "Failed to remove Location Callback.")
   }
}

Metode removeLocationUpdates() menyiapkan tugas untuk memberi tahu FusedLocationProviderClient bahwa Anda tidak ingin lagi menerima pembaruan lokasi untuk LocationCallback. addOnCompleteListener() memberikan callback untuk diselesaikan dan mengeksekusi Task.

Seperti pada langkah sebelumnya, Anda mungkin telah melihat bahwa kode ini berada dalam pernyataan try/catch. Metode ini memerlukan pemblokiran tersebut karena SecurityException terjadi saat aplikasi Anda tidak memiliki izin untuk mengakses informasi lokasi

Anda mungkin bertanya-tanya kapan metode yang berisi kode berlangganan/berhenti berlangganan dipanggil. Tombol dipicu di class utama saat pengguna mengetuk tombol. Jika Anda ingin melihatnya, lihat class MainActivity.kt.

Jalankan aplikasi

Jalankan aplikasi Anda dari Android Studio dan coba tombol lokasi.

Anda akan melihat informasi lokasi di layar output. Ini adalah aplikasi yang berfungsi penuh untuk Android 9.

2ae45c4e297e3681.png

d66089bfb532e993.png

5. Mendukung Android 10

Di bagian ini, Anda akan menambahkan dukungan untuk Android 10.

Aplikasi Anda sudah berlangganan perubahan lokasi, sehingga tidak banyak pekerjaan yang harus dilakukan.

Bahkan, yang perlu Anda lakukan adalah menentukan bahwa layanan latar depan digunakan untuk tujuan lokasi.

SDK Target 29

  1. Dalam modul base, telusuri TODO: Step 2.1, Target Android 10 and then Android 11. dalam file build.gradle.
  2. Buat perubahan berikut:
  3. Tetapkan targetSdkVersion ke 29.

Kode akan terlihat seperti ini:

android {
   // TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 29
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 29
       versionCode 1
       versionName "1.0"
   }
...
}

Setelah melakukannya, Anda akan diminta untuk menyinkronkan project. Klik Sync Now.

153f70847e0ec320.pngS

Setelah itu, aplikasi Anda hampir siap untuk Android 10.

Menambahkan Jenis Layanan Latar Depan

Di Android 10, Anda harus menyertakan jenis layanan latar depan jika memerlukan akses lokasi saat digunakan. Dalam kasus Anda, info ini digunakan untuk mendapatkan informasi lokasi.

Dalam modul base, telusuri TODO: 2.2, Add foreground service type di AndroidManifest.xml dan tambahkan kode berikut ke elemen <service>:

android:foregroundServiceType="location"

Kode akan terlihat seperti ini:

<application>
   ...

   <!-- Foreground services in Android 10+ require type. -->
   <!-- TODO: 2.2, Add foreground service type. -->
   <service
       android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
       android:enabled="true"
       android:exported="false"
       android:foregroundServiceType="location" />
</application>

Selesai. Aplikasi Anda mendukung lokasi Android 10 untuk "saat digunakan" dengan mengikuti praktik terbaik untuk lokasi di Android.

Jalankan aplikasi

Jalankan aplikasi Anda dari Android Studio dan coba tombol lokasi.

Semuanya seharusnya berfungsi seperti sebelumnya, tetapi sekarang sudah berfungsi di Android 10. Jika sebelumnya Anda tidak menerima izin untuk lokasi, sekarang Anda akan melihat layar izin.

6a1029175b467c77.pngS

c7c1d226e49a121.png

39a262b66a275f66.pngS

6. Mendukung Android 11

Di bagian ini, Anda menargetkan Android 11.

Kabar baik, Anda tidak perlu melakukan perubahan pada file apa pun kecuali file build.gradle.

SDK 11 Target

  1. Dalam modul base, telusuri TODO: Step 2.1, Target SDK dalam file build.gradle.
  2. Buat perubahan berikut:
  3. compileSdkVersion hingga 30
  4. targetSdkVersion hingga 30

Kode akan terlihat seperti ini:

android {
   TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 30
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
   }
...
}

Setelah melakukannya, Anda akan diminta untuk menyinkronkan project. Klik Sync Now.

153f70847e0ec320.pngS

Setelah itu, aplikasi Anda siap untuk Android 11.

Jalankan aplikasi

Jalankan aplikasi Anda dari Android Studio dan coba klik tombol.

Semuanya seharusnya berfungsi seperti sebelumnya, tetapi sekarang sudah berfungsi di Android 11. Jika sebelumnya Anda tidak menerima izin untuk lokasi, sekarang Anda akan melihat layar izin.

73d8cc88c5877c25.pngS

cc98fac6e089bc4.png

7. Strategi lokasi untuk Android

Dengan memeriksa dan meminta izin akses lokasi dengan cara yang ditampilkan dalam codelab ini, aplikasi Anda dapat berhasil melacak tingkat aksesnya terkait lokasi perangkat.

Halaman ini mencantumkan beberapa praktik terbaik utama yang terkait dengan izin akses lokasi. Untuk informasi selengkapnya tentang cara menjaga keamanan data, lihat Praktik terbaik izin aplikasi.

Hanya meminta izin yang Anda perlukan

Minta izin hanya jika diperlukan. Contoh:

  • Jangan meminta izin akses lokasi saat aplikasi dimulai kecuali jika benar-benar diperlukan.
  • Jika aplikasi menargetkan Android 10 atau yang lebih baru dan Anda memiliki layanan latar depan, deklarasikan foregroundServiceType dari "location" dalam manifes.
  • Jangan meminta izin akses lokasi latar belakang kecuali Anda memiliki kasus penggunaan yang valid seperti yang dijelaskan dalam Akses yang Lebih Aman dan Transparan ke Lokasi Pengguna.

Mendukung degradasi halus jika izin tidak diberikan

Untuk mempertahankan pengalaman pengguna yang baik, desain aplikasi Anda agar dapat menangani situasi berikut dengan baik:

  • Aplikasi Anda tidak memiliki akses ke informasi lokasi.
  • Aplikasi Anda tidak memiliki akses ke informasi lokasi saat berjalan di latar belakang.

8. Selamat

Anda telah mempelajari cara menerima pembaruan lokasi di Android, dengan selalu mengingat praktik terbaik.

Pelajari lebih lanjut