From 44f7f958edc1d756af5c95d4cd5a946623c152f9 Mon Sep 17 00:00:00 2001 From: sagar-bad Date: Fri, 12 Dec 2025 00:46:51 +0530 Subject: [PATCH] Airtel Attendance App with basic structure Added AndroidManifest.xml, SplashActivity, MainActivity, layout, menu, styles, colors, and CI workflow for the Airtel Attendance App. --- Airtel | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 Airtel diff --git a/Airtel b/Airtel new file mode 100644 index 00000000..51f82233 --- /dev/null +++ b/Airtel @@ -0,0 +1,319 @@ +app/src/main/AndroidManifest.xml + + + + + + + + + + + + + + + + + +app/src/main/java/com/gn/airtelattendance/SplashActivity.kt +package com.gn.airtelattendance + +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.activity.ComponentActivity + +class SplashActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // theme-based splash (see styles) + Handler(Looper.getMainLooper()).postDelayed({ + startActivity(Intent(this, MainActivity::class.java)) + finish() + }, 1200) // 1.2s + } +} +app/src/main/java/com/gn/airtelattendance/MainActivity.kt +package com.gn.airtelattendance + +import android.annotation.SuppressLint +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Bundle +import android.view.KeyEvent +import android.view.Menu +import android.view.MenuItem +import android.webkit.WebChromeClient +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.Toolbar +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import java.lang.Exception + +class MainActivity : ComponentActivity() { + + private lateinit var webView: WebView + private lateinit var swipe: SwipeRefreshLayout + private val url = "https://forms.gle/3zbwmyRZ7V2TrSy47" + + @SuppressLint("SetJavaScriptEnabled") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + val toolbar = findViewById(R.id.toolbar) + toolbar.title = "Airtel Attendance App" + toolbar.setNavigationOnClickListener { finish() } + + swipe = findViewById(R.id.swipe) + webView = findViewById(R.id.webview) + webView.settings.javaScriptEnabled = true + webView.settings.domStorageEnabled = true + webView.webViewClient = object : WebViewClient() { + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { + return false + } + override fun onReceivedError(view: WebView, errorCode: Int, description: String?, failingUrl: String?) { + showOfflineDialog() + } + } + webView.webChromeClient = WebChromeClient() + + swipe.setOnRefreshListener { + if (isOnline()) { + webView.reload() + } else { + swipe.isRefreshing = false + showOfflineDialog() + } + } + + // load + if (isOnline()) webView.loadUrl(url) else showOfflineDialog() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.main_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when(item.itemId) { + R.id.action_refresh -> { + if (isOnline()) { + webView.reload() + Toast.makeText(this, "Refreshing...", Toast.LENGTH_SHORT).show() + } else showOfflineDialog() + } + R.id.action_share -> { + try { + val i = android.content.Intent().apply { + action = android.content.Intent.ACTION_SEND + putExtra(android.content.Intent.EXTRA_TEXT, url) + type = "text/plain" + } + startActivity(android.content.Intent.createChooser(i, "Share link")) + } catch (e: Exception) { /* ignore */ } + } + } + return super.onOptionsItemSelected(item) + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (::webView.isInitialized && keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) { + webView.goBack() + return true + } + return super.onKeyDown(keyCode, event) + } + + private fun showOfflineDialog() { + swipe.isRefreshing = false + AlertDialog.Builder(this) + .setTitle("No Internet") + .setMessage("Internet connection not available. Please connect and try again.") + .setPositiveButton("Retry") { _, _ -> + if (isOnline()) webView.loadUrl(url) else Toast.makeText(this, "Still offline", Toast.LENGTH_SHORT).show() + } + .setNegativeButton("Open Settings") { _, _ -> + startActivity(android.provider.Settings.ACTION_WIFI_SETTINGS.let { android.content.Intent(it) }) + } + .show() + } + + private fun isOnline(): Boolean { + val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val n = cm.activeNetwork ?: return false + val caps = cm.getNetworkCapabilities(n) ?: return false + return caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + } + + override fun onDestroy() { + if (::webView.isInitialized) { + webView.loadUrl("about:blank") + } + super.onDestroy() + } +} +app/src/main/res/layout/activity_main.xml + + + + + + + + + + + +app/src/main/res/menu/main_menu.xml + + + + +app/src/main/res/values/styles.xml + + + + + +app/src/main/res/drawable/splash_background.xml + + + + + + +app/src/main/res/values/colors.xml + + #E40000 + +adaptive icon XML (mipmap-anydpi-v26/ic_launcher.xml): + + + + +ic_foreground_gn.png +app/build.gradle +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.gn.airtelattendance' + compileSdk 34 + + defaultConfig { + applicationId "com.gn.airtelattendance" + minSdk 21 + targetSdk 34 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + // produce unsigned AAR/APK (you can add signing in CI if you want) + signingConfig null + minifyEnabled false + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { jvmTarget = '17' } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10" + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.1' +} +Create file: .github/workflows/android-build.yml +name: Android CI - Build APK + +on: + push: + branches: [ main, master ] + workflow_dispatch: + +jobs: + build: + name: Build debug APK + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build Debug APK + run: ./gradlew assembleDebug --no-daemon + + - name: Upload artifact (APK) + uses: actions/upload-artifact@v4 + with: + name: airtel-attendance-apk + path: app/build/outputs/apk/debug/*.apk