diff --git a/android/app/src/main/java/com/compassconnections/app/MainActivity.java b/android/app/src/main/java/com/compassconnections/app/MainActivity.java index ae04999e..f00979fa 100644 --- a/android/app/src/main/java/com/compassconnections/app/MainActivity.java +++ b/android/app/src/main/java/com/compassconnections/app/MainActivity.java @@ -19,6 +19,12 @@ import com.getcapacitor.BridgeActivity; import com.getcapacitor.BridgeWebViewClient; import com.getcapacitor.Plugin; import com.getcapacitor.PluginHandle; +import com.google.android.play.core.appupdate.AppUpdateInfo; +import com.google.android.play.core.appupdate.AppUpdateManager; +import com.google.android.play.core.appupdate.AppUpdateManagerFactory; +import com.google.android.play.core.appupdate.AppUpdateOptions; +import com.google.android.play.core.install.model.AppUpdateType; +import com.google.android.play.core.install.model.UpdateAvailability; import org.json.JSONException; import org.json.JSONObject; @@ -27,46 +33,6 @@ import ee.forgr.capacitor.social.login.GoogleProvider; import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin; import ee.forgr.capacitor.social.login.SocialLoginPlugin; - -//import android.app.NotificationChannel; -//import android.app.NotificationManager; -//import android.os.Build; -//import com.google.firebase.messaging.RemoteMessage; -//import com.capacitorjs.plugins.pushnotifications.MessagingService; - -//public class MyMessagingService extends MessagingService { -// -// @Override -// public void onMessageReceived(RemoteMessage remoteMessage) { -// // TODO(developer): Handle FCM messages here. -// // Not getting messages here? See why this may be: https://goo.gl/39bRNJ -// Log.d(TAG, "From: " + remoteMessage.getFrom()); -// -// // Check if message contains a data payload. -// if (remoteMessage.getData().size() > 0) { -// Log.d(TAG, "Message data payload: " + remoteMessage.getData()); -// -// if (/* Check if data needs to be processed by long running job */ true) { -// // For long-running tasks (10 seconds or more) use WorkManager. -// scheduleJob(); -// } else { -// // Handle message within 10 seconds -// handleNow(); -// } -// -// } -// -// // Check if message contains a notification payload. -// if (remoteMessage.getNotification() != null) { -// Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); -// } -// -// // Also if you intend on generating your own notifications as a result of a received FCM -// // message, here is where that should be initiated. See sendNotification method below. -// } -//} - - public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin { // Declare this at class level @@ -143,6 +109,9 @@ public class MainActivity extends BridgeActivity implements ModifiedMainActivity // }}); askNotificationPermission(); + + appUpdateManager = AppUpdateManagerFactory.create(this); + checkForUpdates(); } @Override @@ -169,5 +138,69 @@ public class MainActivity extends BridgeActivity implements ModifiedMainActivity @Override public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() { } + + private static final int UPDATE_REQUEST_CODE = 500; + private AppUpdateManager appUpdateManager; + private static final String TAG = "MainActivity"; + + private void checkForUpdates() { + appUpdateManager.getAppUpdateInfo() + .addOnSuccessListener(appUpdateInfo -> { + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) { + if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { + startImmediateUpdate(appUpdateInfo); + } else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { + startFlexibleUpdate(appUpdateInfo); + } + } + }) + .addOnFailureListener(exception -> { + // Handle error - log it + Log.e(TAG, "Failed to check For Updates", exception); + }); + } + + private void startImmediateUpdate(AppUpdateInfo appUpdateInfo) { + AppUpdateOptions options = AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(); + + appUpdateManager.startUpdateFlow(appUpdateInfo, this, options) + .addOnSuccessListener(result -> { + Log.i(TAG, "Immediate update started successfully"); + }) + .addOnFailureListener(exception -> { + Log.e(TAG, "Failed to start immediate update", exception); + }); + } + + private void startFlexibleUpdate(AppUpdateInfo appUpdateInfo) { + AppUpdateOptions options = AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build(); + + appUpdateManager.startUpdateFlow(appUpdateInfo, this, options) + .addOnSuccessListener(result -> { + Log.i(TAG, "Flexible update started successfully"); + }) + .addOnFailureListener(exception -> { + Log.e(TAG, "Failed to start flexible update", exception); + }); + } + + + @Override + public void onResume() { + super.onResume(); + + // Check if an immediate update was interrupted + appUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> { + if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { + startImmediateUpdate(appUpdateInfo); + } + }); + } + + @Override + public void onDestroy() { + super.onDestroy(); + appUpdateManager = null; + } } diff --git a/web/components/about-settings.tsx b/web/components/about-settings.tsx index 85d4e1d9..f023d5c2 100644 --- a/web/components/about-settings.tsx +++ b/web/components/about-settings.tsx @@ -3,14 +3,12 @@ import {PrivateUser} from "common/user" import {Col} from "web/components/layout/col" import {HOSTING_ENV, IS_VERCEL} from "common/hosting/constants" import {Capacitor} from "@capacitor/core" -import {LiveUpdate} from "@capawesome/capacitor-live-update" import {useEffect, useState} from "react" import {App} from "@capacitor/app" import {api} from "web/lib/api" import {githubRepo} from "common/constants" import {CustomLink} from "web/components/links" import {Button} from "web/components/buttons/button" -import {getLiveUpdateInfo} from "web/lib/live-update"; import {useT} from 'web/lib/locale' export type WebBuild = { @@ -77,17 +75,19 @@ function useDiagnostics() { if (Capacitor.isNativePlatform()) { const appInfo = await App.getInfo() - const bundle = await LiveUpdate.getCurrentBundle().catch(() => {return {bundleId: null}}) - const buildInfo = await getLiveUpdateInfo().catch(() => null) + // const bundle = await LiveUpdate.getCurrentBundle().catch(() => { + // return {bundleId: null} + // }) + // const buildInfo = await getLiveUpdateInfo().catch(() => null) diagnostics.android = { appVersion: appInfo.version, buildNumber: appInfo.build, - liveUpdate: { - bundleId: bundle?.bundleId, - commitSha: buildInfo?.commitSha, - commitMessage: buildInfo?.commitMessage, - commitDate: buildInfo?.commitDate - } + // liveUpdate: { + // bundleId: bundle?.bundleId, + // commitSha: buildInfo?.commitSha, + // commitMessage: buildInfo?.commitMessage, + // commitDate: buildInfo?.commitDate + // } } } @@ -166,7 +166,7 @@ const LoadedAboutSettings = (props: { } -const WebBuildInfo = (props: {info?: WebBuild}) => { +const WebBuildInfo = (props: { info?: WebBuild }) => { const {info} = props if (!info) return const env = info.environment @@ -183,7 +183,7 @@ const WebBuildInfo = (props: {info?: WebBuild}) => { } -const AndroidInfo = (props: {info?: Android}) => { +const AndroidInfo = (props: { info?: Android }) => { const {info} = props if (!info) return const sha = info.liveUpdate?.commitSha @@ -192,14 +192,18 @@ const AndroidInfo = (props: {info?: Android}) => {

Android (Capacitor / Capawesome)

App version (Android): {info.appVersion}

Native build number (Android): {info.buildNumber}

-

Live update build ID (Capawesome): {info.liveUpdate?.bundleId}

-

Live update commit SHA (Capawesome): {sha}

-

Live update commit message (Capawesome): {info.liveUpdate?.commitMessage}

-

Live update commit date (Capawesome): {info.liveUpdate?.commitDate}

+ {info.liveUpdate && + <> +

Live update build ID (Capawesome): {info.liveUpdate?.bundleId}

+

Live update commit SHA (Capawesome): {sha}

+

Live update commit message (Capawesome): {info.liveUpdate?.commitMessage}

+

Live update commit date (Capawesome): {info.liveUpdate?.commitDate}

+ + } } -const BackendInfo = (props: {info?: Backend}) => { +const BackendInfo = (props: { info?: Backend }) => { const {info} = props if (!info) return const sha = info.gitSha @@ -215,7 +219,7 @@ const BackendInfo = (props: {info?: Backend}) => { } -const RuntimeInfo = (props: {info?: Runtime}) => { +const RuntimeInfo = (props: { info?: Runtime }) => { const {info} = props if (!info) return return diff --git a/web/lib/app-update.ts b/web/lib/app-update.ts new file mode 100644 index 00000000..4435fdab --- /dev/null +++ b/web/lib/app-update.ts @@ -0,0 +1,16 @@ +import {registerPlugin} from '@capacitor/core' + +interface AppUpdatePlugin { + checkForUpdate(): Promise<{ + updateAvailable: boolean + immediateAllowed: boolean + flexibleAllowed: boolean + availableVersionCode: number + }> + + startUpdate(options: { type: 'immediate' | 'flexible' }): Promise +} + +const AppUpdate = registerPlugin('AppUpdate') + +export default AppUpdate \ No newline at end of file diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index d6e930d2..06c9b6a5 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -18,14 +18,13 @@ import {StatusBar} from '@capacitor/status-bar' import {App} from '@capacitor/app' import {useRouter} from "next/navigation" import {Keyboard} from "@capacitor/keyboard" -import {LiveUpdate} from "@capawesome/capacitor-live-update" import {IS_VERCEL} from "common/hosting/constants" -import {getLocale, resetCachedLocale} from "web/lib/locale-cookie"; +import {getLocale, resetCachedLocale} from "web/lib/locale-cookie" import {I18nContext} from "web/lib/locale" if (Capacitor.isNativePlatform()) { // Only runs on iOS/Android native - // Note sure it's doing anything, though, need to check + // Note sure it's doing anything, though. Need to check StatusBar.setOverlaysWebView({overlay: false}).catch(console.warn) // StatusBar.setStyle({style: Style.Light}).catch(console.warn) @@ -33,23 +32,24 @@ if (Capacitor.isNativePlatform()) { window.dispatchEvent(new CustomEvent('appBackButton')) }) - App.addListener("resume", async () => { - const newChannelName = 'default' - try { - await LiveUpdate.setChannel({channel: newChannelName}) - console.log(`Device channel set to: ${newChannelName}`) - } catch (error) { - console.error('Failed to set channel', error) - } - const {nextBundleId} = await LiveUpdate.sync() - if (nextBundleId) { - // Ask the user if they want to apply the update immediately - const shouldReload = confirm("A new update is available. Would you like to install it?") - if (shouldReload) { - await LiveUpdate.reload() - } - } - }) + // Remove live update as the free plan is very limited (100 live updates per year). Do releases on Play Store instead. + // App.addListener("resume", async () => { + // const newChannelName = 'default' + // try { + // await LiveUpdate.setChannel({channel: newChannelName}) + // console.log(`Device channel set to: ${newChannelName}`) + // } catch (error) { + // console.error('Failed to set channel', error) + // } + // const {nextBundleId} = await LiveUpdate.sync() + // if (nextBundleId) { + // // Ask the user if they want to apply the update immediately + // const shouldReload = confirm("A new update is available. Would you like to install it?") + // if (shouldReload) { + // await LiveUpdate.reload() + // } + // } + // }) } @@ -93,7 +93,7 @@ function MyApp(props: AppProps) { useHasLoaded() const router = useRouter() - const [locale, setLocaleState] = useState(getLocale()); + const [locale, setLocaleState] = useState(getLocale()) const setLocale = (newLocale: string) => { document.cookie = `lang=${newLocale}; path=/; max-age=31536000` setLocaleState(newLocale)