001package com.gigya.android.sdk.ui; 002 003import android.app.Activity; 004import android.content.Context; 005import android.content.Intent; 006import android.net.Uri; 007import android.os.Bundle; 008import android.os.CountDownTimer; 009 010import com.gigya.android.sdk.GigyaLogger; 011import com.gigya.android.sdk.utils.UrlUtils; 012 013import java.util.HashMap; 014import java.util.Map; 015import java.util.concurrent.TimeUnit; 016 017 018public class WebLoginActivity extends Activity { 019 020 private static final String LOG_TAG = "WebLoginActivity"; 021 022 private static final String EXTRA_LIFECYCLE_CALLBACK_ID = "web_login_lifecycle_callback"; 023 private static final String EXTRA_URI = "web_login_uri"; 024 025 private static final int REQUEST_CODE = 4040; 026 027 /* 028 Result handling and cancel dismissal timer. 029 */ 030 private boolean _handledResult = false; 031 private CountDownTimer _cancelResultTimer; 032 033 public interface WebLoginActivityCallback { 034 void onResult(Activity activity, Map<String, Object> parsed); 035 036 void onCancelled(); 037 } 038 039 private WebLoginActivityCallback _webLoginLifecycleCallbacks; 040 private int _webLoginLifecycleCallbacksId = -1; 041 private String _uri; 042 043 public static void present(Context context, String uri, WebLoginActivityCallback lifecycleCallback) { 044 Intent intent = new Intent(context, WebLoginActivity.class); 045 intent.putExtra(EXTRA_LIFECYCLE_CALLBACK_ID, Presenter.addWebLoginLifecycleCallback(lifecycleCallback)); 046 intent.putExtra(EXTRA_URI, uri); 047 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); 048 context.startActivity(intent); 049 } 050 051 @Override 052 protected void onCreate(Bundle savedInstanceState) { 053 super.onCreate(savedInstanceState); 054 055 if (getIntent() != null && getIntent().getExtras() != null) { 056 _webLoginLifecycleCallbacksId = getIntent().getIntExtra(EXTRA_LIFECYCLE_CALLBACK_ID, -1); 057 _uri = getIntent().getStringExtra(EXTRA_URI); 058 if (_webLoginLifecycleCallbacksId == -1) { 059 finish(); 060 return; 061 } 062 if (_uri == null) { 063 finish(); 064 return; 065 } 066 // Reference the callback using static getter from the Presenter. Same as the HostActivity. 067 _webLoginLifecycleCallbacks = Presenter.getWebLoginCallback(_webLoginLifecycleCallbacksId); 068 } 069 070 // Now that we have the callback. 071 072 final Uri uri = Uri.parse(_uri); 073 Intent browserIntent = new Intent(Intent.ACTION_VIEW, uri); 074 browserIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 075 startActivityForResult(browserIntent, REQUEST_CODE); 076 } 077 078 079 @Override 080 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 081 if (requestCode == REQUEST_CODE) { 082 // Start short timer to see if result is being handled in the onNewIntent. 083 // Explanation: When using startActivityForResult with an ACTION_VIEW intent we will not receive the correct 084 // resultCode (Will always be 0 == Activity.RESULT_CANCELED). Therefore in order to determine when the user 085 // actually dismissed the browser we will use this short countdown timer as the onNewIntentCall should trigger 086 // immediately. 087 _cancelResultTimer = new CountDownTimer(TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(1)) { 088 089 @Override 090 public void onTick(long millisUntilFinished) { 091 GigyaLogger.debug(LOG_TAG, "Result countdown tick:"); 092 } 093 094 @Override 095 public void onFinish() { 096 if (!_handledResult && !isFinishing()) { 097 _webLoginLifecycleCallbacks.onCancelled(); 098 finish(); 099 } 100 } 101 }.start(); 102 } else { 103 super.onActivityResult(requestCode, resultCode, data); 104 } 105 } 106 107 @Override 108 protected void onNewIntent(Intent intent) { 109 super.onNewIntent(intent); 110 111 112 invalidateCancelTimer(); 113 114 GigyaLogger.debug(LOG_TAG, "onNewIntent: " + intent.getAction()); 115 final Uri data = intent.getData(); 116 if (data == null) { 117 finish(); 118 return; 119 } 120 121 // Reference identifiers to verify intent deep link. 122 final String packageName = getPackageName(); 123 final String scheme = data.getScheme(); 124 final String host = data.getHost(); 125 final String pathPrefix = data.getPath(); 126 if (scheme == null || host == null || pathPrefix == null) { 127 finish(); 128 return; 129 } 130 // Evaluate intent-filter params, 131 if (scheme.equals("gigya") && host.equals("gsapi") && pathPrefix.equalsIgnoreCase("/" + packageName + "/login_result")) { 132 133 _handledResult = true; 134 135 final String encodedFragment = data.getEncodedFragment(); 136 final Map<String, Object> parsed = new HashMap<>(); 137 UrlUtils.parseUrlParameters(parsed, encodedFragment); 138 if (_webLoginLifecycleCallbacks != null && !isFinishing()) { 139 _webLoginLifecycleCallbacks.onResult(this, parsed); 140 } 141 finish(); 142 } else { 143 finish(); 144 } 145 } 146 147 private void invalidateCancelTimer() { 148 if (_cancelResultTimer != null) { 149 _cancelResultTimer.cancel(); 150 } 151 _cancelResultTimer = null; 152 } 153 154 @Override 155 public void finish() { 156 157 _handledResult = false; 158 159 invalidateCancelTimer(); 160 161 Presenter.flushWebLoginLifecycleCallback(_webLoginLifecycleCallbacksId); 162 super.finish(); 163 /* 164 Disable exit animation. 165 */ 166 overridePendingTransition(0, 0); 167 } 168}