diff --git a/app/build.gradle b/app/build.gradle index 51dd3c295d8f677781cf066465b568c11e449030..6ba7fe0afe3bf9d7ee8791a91bdc17228c2d608c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "jp.atled.agileworks" minSdkVersion 24 targetSdk 34 - versionCode 3 - versionName "1.1.0" + versionCode 4 + versionName "1.1.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1f6cbec0fd5d735842a5ad99ed3427a7254a30ec..3c450c434ed58604aed9218429dcced4d64cb0ae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,9 @@ + + + Unit) { + fun getSession(onResult: (isSuccess: Boolean, response: SessionResponse?, headers: Map>?) -> Unit) { val instance = ApiClient.instance(null) - instance.getSession().enqueue(ApiCallbackCommon(activity, onResult)) + instance.getSession().enqueue(SessionApiCallback(activity, onResult)) } companion object { fun getInstance(activity: Activity?) = SessionRepository(activity) + + fun setCookie(setCookieHeaders: List, bodySessionId: String, url: String){ + CookieManager.getInstance().apply { + val preferences = "cookie" + ServerRepository().loadServer().toString() + var cookie = "" + for (header in setCookieHeaders) { + cookie += header + ";" + } + // ヘッダからJSESSIONIDを取得 + val headerSessionId = extractJSessionId(cookie) + + // レスポンスヘッダとボディのJSESSIONIDが同じとき、そのユーザの最新のセッションになる + if(bodySessionId == headerSessionId) { + for (header in setCookieHeaders) { + // Cookieをセット + setCookie(url, header) + } + flush() + // Cookieを保存 + AwApp.instance.applicationContext.getSharedPreferences( + preferences, + Context.MODE_PRIVATE + ) + .edit() + .putString("cookie", cookie) + .apply() + } + } + } + + fun extractJSessionId(cookie: String): String? { + val startIndex = cookie.indexOf("JSESSIONID=") + if (startIndex != -1) { + val jsessionIdPart = cookie.substring(startIndex) + val parts = jsessionIdPart.split(";") + if (parts.isNotEmpty()) { + val jsessionIdValuePair = parts[0] + val valueParts = jsessionIdValuePair.split("=",) + if (valueParts.size == 2) { + return valueParts[1] + } + } + } + return null + } } } \ No newline at end of file diff --git a/app/src/main/java/jp/atled/agileworks/model/api/ApiClient.kt b/app/src/main/java/jp/atled/agileworks/model/api/ApiClient.kt index 068b008114c4caf13d925d023b40642eab5dacf3..e7247c0e94a3708cba36d643c12f60e8377dc30a 100644 --- a/app/src/main/java/jp/atled/agileworks/model/api/ApiClient.kt +++ b/app/src/main/java/jp/atled/agileworks/model/api/ApiClient.kt @@ -153,12 +153,12 @@ object ApiClient { // 保存しているセッションIDを取得 var preferences = "cookie" + ServerRepository().loadServer().toString() val cookiePref = AwApp.instance.applicationContext.getSharedPreferences(preferences, Context.MODE_PRIVATE) - val sessionId = cookiePref.getString("sessionId", "") ?: "" + val cookie = cookiePref.getString("cookie", "") ?: "" interceptor = Interceptor { chain -> val original = chain.request() val requestBuilder = original.newBuilder() val request = requestBuilder - .addHeader("Cookie", "JSESSIONID=$sessionId") + .addHeader("Cookie", cookie) .build() chain.proceed(request) } diff --git a/app/src/main/java/jp/atled/agileworks/model/api/SessionApiCallback.kt b/app/src/main/java/jp/atled/agileworks/model/api/SessionApiCallback.kt new file mode 100644 index 0000000000000000000000000000000000000000..d8a54bbf95ed966a3d32ce4ffb38c50ca1dc5661 --- /dev/null +++ b/app/src/main/java/jp/atled/agileworks/model/api/SessionApiCallback.kt @@ -0,0 +1,50 @@ +package jp.atled.agileworks.model.api + +import android.app.Activity +import android.app.AlertDialog +import android.util.Log +import jp.atled.agileworks.R +import jp.atled.agileworks.view.ui.login.LoginUtil +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class SessionApiCallback( + private val activity: Activity?, + private val onResult: (isSuccess: Boolean, response: T?, headers: Map>?) -> Unit +) : Callback { + + companion object { + private const val TAG = "SessionApiCallback" + } + + override fun onResponse(call: Call, response: Response) { + try { + if (response != null && response.isSuccessful) { + val headers = response.headers().toMultimap() + onResult(true, response.body(), headers) + } else { + onResult(false, null, null) + } + } catch (e: IllegalStateException) { + // 非同期処理が画面遷移後に完了したときに発生する。握りつぶす。 + Log.w(TAG, "IllegalStateException", e) + } + } + + override fun onFailure(call: Call, t: Throwable) { + if ((activity != null) && (t is BadRefreshTokenException)) { + AlertDialog.Builder(activity) + .setTitle(R.string.refresh_token_failure_dialog_title) + .setMessage(R.string.refresh_token_failure_dialog_message) + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(R.string.refresh_token_failure_dialog_button_label) { _, _ -> + LoginUtil.reauth(activity) + } + .setCancelable(false) + .show() + } else { + onResult(false, null, null) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/jp/atled/agileworks/view/ui/DrawerMenu.kt b/app/src/main/java/jp/atled/agileworks/view/ui/DrawerMenu.kt index 60c53b5c987a4ca5580aef7a41530c348b551451..166955a317c648283cd559197d017fc89b23de4a 100644 --- a/app/src/main/java/jp/atled/agileworks/view/ui/DrawerMenu.kt +++ b/app/src/main/java/jp/atled/agileworks/view/ui/DrawerMenu.kt @@ -62,7 +62,7 @@ class DrawerMenu { } private fun setupUserName(activity: Activity, drawer: NavigationView) { - SessionRepository.getInstance(activity).getSession { _, response -> + SessionRepository.getInstance(activity).getSession { _, response, _ -> val userName = response?.user?.name.orEmpty() var systemName = response?.system_name.orEmpty() val header = drawer.getHeaderView(0) @@ -75,7 +75,7 @@ class DrawerMenu { private fun showProfile(activity: AppCompatActivity) { // ログイン中に変化があるかもしれないのでメニューが呼ばれる度に取得し直す。 - SessionRepository.getInstance(activity).getSession { _, response -> + SessionRepository.getInstance(activity).getSession { _, response, _ -> val userCode = response?.user?.code.orEmpty() val userName = response?.user?.name.orEmpty() val loginId = response?.user?.loginId.orEmpty() diff --git a/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebDownload.kt b/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebDownload.kt index 9f6d385bac18a07a0b1a1a6e6ca5d34e3008b85b..f133e2030937f63a598dea22a4a52bf839bbe515 100644 --- a/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebDownload.kt +++ b/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebDownload.kt @@ -151,7 +151,7 @@ class DocumentWebDownloader(private val handlingFragment: Fragment, private val val openUriIntent = Intent(Intent.ACTION_VIEW, destinationUri).apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } - val pendingIntent = PendingIntent.getActivity(AwApp.instance.applicationContext, 0, openUriIntent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT) + val pendingIntent = PendingIntent.getActivity(AwApp.instance.applicationContext, 0, openUriIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT) progressNotificationBuilder .setContentText(context.resources.getText(R.string.download_notification_successful_message)) .setSmallIcon(android.R.drawable.stat_sys_download_done) diff --git a/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebFragment.kt b/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebFragment.kt index 9e27c64515eb36d85f44ea6884a262355f225107..f516c95814edccd9866785bdd55f841a124d223e 100644 --- a/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebFragment.kt +++ b/app/src/main/java/jp/atled/agileworks/view/ui/documentweb/DocumentWebFragment.kt @@ -566,27 +566,15 @@ class DocumentWebFragment : Fragment(), DocumentWebPresenter { activity = null } - SessionRepository.getInstance(activity).getSession() { isSuccess, response -> + SessionRepository.getInstance(activity).getSession() { isSuccess, response, headers -> if (isSuccess) { url?.let { url -> val uri = Uri.parse(url) response!!.session_id?.apply { sessionId = response!!.session_id - setCookie( - "https://${uri.host}", - "JSESSIONID=${sessionId}; Path=/${LoginRepository().loadServerContext()}; HttpOnly;Secure;SameSite=None" - ) - flush() - - // セッションID保存 - var preferences = "cookie" + ServerRepository().loadServer().toString() - AwApp.instance.applicationContext.getSharedPreferences( - preferences, - Context.MODE_PRIVATE - ) - .edit() - .putString("sessionId", sessionId) - .apply() + headers?.get("set-cookie")?.also { setCookieHeaders -> + SessionRepository.setCookie(setCookieHeaders, sessionId!!, "https://${uri.host}") + } } if (updateLanguage) { // 多言語対応 diff --git a/app/src/main/java/jp/atled/agileworks/view/ui/login/LoginFragment.kt b/app/src/main/java/jp/atled/agileworks/view/ui/login/LoginFragment.kt index 35e82d774fe755a71bbcfa962ddc329b5b37caba..9c28d6b0b16b8f37416e081b56972705afecfb9d 100644 --- a/app/src/main/java/jp/atled/agileworks/view/ui/login/LoginFragment.kt +++ b/app/src/main/java/jp/atled/agileworks/view/ui/login/LoginFragment.kt @@ -51,6 +51,7 @@ class LoginFragment: Fragment() { private var shouldClearTaskOnOAuthFailure = false private var disableButtonFlg = false private var tempServerNumber: Int? = 0 + private var addServerFlg = false override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { Log.d(TAG, "call onCreateView()") @@ -124,6 +125,7 @@ class LoginFragment: Fragment() { } else if (requireActivity().intent.action == LoginActivity.ACTION_ADD_SERVER) { //サーバー追加のログイン画面 Log.d(TAG, "intent.action == LoginActivity.ACTION_ADD_SERVER") + addServerFlg = true } else { // 過去に認証したときの authState を取得 if (mOAuthService.getAuthState(savedInstanceState).isAuthorized) { @@ -178,7 +180,11 @@ class LoginFragment: Fragment() { binding.viewmodel?.setServerContext() if (checkServer()) { - whenAuthorizationSucceeds() + if(addServerFlg) { + loginAlert(R.string.login_server_check_alert) + } else { + startAuthorization() + } } else { try { startAuthorization() @@ -253,8 +259,6 @@ class LoginFragment: Fragment() { for (server in serverList) { val serverName = LoginRepository(server).loadServerUrl() if (serverName == serverHost) { - ServerRepository().setServer(server) - ServerRepository().changeServerList(server) return true } } @@ -477,7 +481,7 @@ class LoginFragment: Fragment() { if (deviceId != null) { // レスポンスが返ってきたらMainActivity起動 ServerRepository().addServerList() - whenAuthorizationSucceeds() + setCookie() } else { LoginUtil.clearAuthStateAndNotificationToken{ requireActivity().runOnUiThread { @@ -550,15 +554,9 @@ class LoginFragment: Fragment() { val response = ApiClient.instance.getSession().execute() if (response.isSuccessful) { response.body()!!.session_id?.apply { + val setCookieHeaders = response.headers().values("Set-Cookie") val sessionId = response.body()!!.session_id - setCookie("https://${LoginRepository().loadServerUrl()}", "JSESSIONID=${sessionId}; Path=/${LoginRepository().loadServerContext()}; HttpOnly;Secure;SameSite=None") - flush() - // セッションID保存 - var preferences = "cookie" + ServerRepository().loadServer().toString() - AwApp.instance.applicationContext.getSharedPreferences(preferences, Context.MODE_PRIVATE) - .edit() - .putString("sessionId", sessionId) - .apply() + SessionRepository.setCookie(setCookieHeaders, sessionId, "https://${LoginRepository().loadServerUrl()}") } //トークン更新に成功するとメイン画面へ whenAuthorizationSucceeds()