check at app start, if access_token is not null and valid. if not, ask for username and password.

This commit is contained in:
Peter Siegmund
2016-03-09 12:17:42 +01:00
parent 2bae2dc265
commit 22191d4cc5
36 changed files with 949 additions and 429 deletions
@@ -7,7 +7,7 @@
<bool name="preference_isSendingActive_default">true</bool>
<string name="preference_server_url_key">webserviceUrlPref</string>
<string name="preference_server_url_default">http://dev.sapsailing.com/</string>
<string name="preference_server_url_default">https://dev.sapsailing.com</string>
<string name="preference_enableLifecycleLogging_key">enableLifecycleLogging</string>
<bool name="preference_enableLifecycleLogging_default">false</bool>
@@ -0,0 +1,18 @@
package com.sap.sailing.android.shared.data;
import com.sap.sse.common.Base64Utils;
public class LoginData {
private String username;
private String password;
public LoginData(String username, String password) {
this.username = username;
this.password = password;
}
public String getCredentials() {
return Base64Utils.toBase64((username + ":" + password).getBytes());
}
}
@@ -44,7 +44,7 @@ public abstract class HttpRequest {
private final URL url;
private final Context context;
private boolean isCancelled;
private SharedPreferences pref;
protected SharedPreferences pref;
public HttpRequest(URL url, Context context) {
this(url, null, context);
@@ -95,7 +95,7 @@ public abstract class HttpRequest {
responseInputStream = doRequest(connection);
} catch (FileNotFoundException fnfe) {
if (HttpURLConnection.HTTP_UNAUTHORIZED == connection.getResponseCode()) {
throw new UnauthorizedException();
throw new UnauthorizedException(connection.getHeaderField("WWW-Authenticate"));
}
// 404 errors...
throw new FileNotFoundException(context.getString(R.string.http_request_exception, this.hashCode(), fnfe.getMessage(), connection.getResponseCode(), connection.getResponseMessage()));
@@ -0,0 +1,33 @@
package com.sap.sailing.android.shared.data.http;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import android.content.Context;
import com.sap.sailing.android.shared.data.LoginData;
public class LoginGetRequest extends HttpRequest {
public final static String ContentType = "application/json;charset=UTF-8";
private final LoginData loginData;
public LoginGetRequest(URL url, Context context, LoginData login) {
super(url, context);
loginData = login;
}
@Override
protected BufferedInputStream doRequest(HttpURLConnection connection) throws IOException {
connection.setRequestMethod("GET");
connection.setChunkedStreamingMode(0);
connection.setRequestProperty("Content-Type", ContentType);
connection.setRequestProperty("Accept", ContentType);
connection.setRequestProperty("Authorization", "Basic " + loginData.getCredentials());
return new BufferedInputStream(connection.getInputStream());
}
}
@@ -7,4 +7,8 @@ import java.io.IOException;
*/
public class UnauthorizedException extends IOException {
private static final long serialVersionUID = 5913076970322227942L;
public UnauthorizedException(String detailMessage) {
super(detailMessage);
}
}
@@ -0,0 +1,75 @@
package com.sap.sailing.android.shared.util;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import android.content.Context;
import android.os.AsyncTask;
import com.sap.sailing.android.shared.data.http.HttpJsonGetRequest;
import com.sap.sailing.android.shared.data.http.HttpRequest;
import com.sap.sailing.android.shared.logging.ExLog;
public class AuthCheckTask extends AsyncTask<Void, Void, JSONObject> {
private static final String TAG = AuthCheckTask.class.getName();
private final static String HELLO_REQUEST = "/security/api/restsecurity/hello";
private Context context;
private URL url;
private AuthCheckTaskListener listener;
private Exception exception;
public AuthCheckTask(Context ctx, String baseUrl, AuthCheckTaskListener taskListener) {
try {
context = ctx;
url = new URL(baseUrl + HELLO_REQUEST);
listener = taskListener;
} catch (MalformedURLException e) {
ExLog.e(context, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + e.getMessage());
}
}
@Override
protected JSONObject doInBackground(Void... params) {
JSONObject result = null;
if (url != null) {
try {
HttpRequest request = new HttpJsonGetRequest(url, context);
InputStream responseStream = request.execute();
JSONParser parser = new JSONParser();
result = (JSONObject) parser.parse(new InputStreamReader(responseStream));
} catch (Exception e) {
exception = e;
}
} else {
exception = new IllegalArgumentException();
}
return result;
}
@Override
protected void onPostExecute(JSONObject json) {
super.onPostExecute(json);
if (listener != null) {
if (exception != null) {
listener.onException(exception);
} else {
listener.onRequestReceived(json);
}
}
}
public interface AuthCheckTaskListener {
void onRequestReceived(JSONObject json);
void onException(Exception exception);
}
}
@@ -0,0 +1,77 @@
package com.sap.sailing.android.shared.util;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import android.content.Context;
import android.os.AsyncTask;
import com.sap.sailing.android.shared.data.LoginData;
import com.sap.sailing.android.shared.data.http.HttpRequest;
import com.sap.sailing.android.shared.data.http.LoginGetRequest;
import com.sap.sailing.android.shared.logging.ExLog;
public class LoginTask extends AsyncTask<LoginData, Void, String> {
private static final String TAG = LoginTask.class.getName();
private final static String TOKEN_REQUEST = "/security/api/restsecurity/access_token";
private URL url = null;
private Context context;
private LoginTaskListener listener;
private Exception exception;
public LoginTask(Context context, String baseUrl, LoginTaskListener listener) {
try {
this.context = context.getApplicationContext();
this.url = new URL(baseUrl + TOKEN_REQUEST);
this.listener = listener;
} catch (MalformedURLException e) {
ExLog.e(context, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + e.getMessage());
}
}
@Override
protected String doInBackground(LoginData... params) {
String access_token = null;
if (url != null && params != null && params.length > 0) {
try {
HttpRequest request = new LoginGetRequest(url, context, params[0]);
InputStream responseStream = request.execute();
JSONParser parser = new JSONParser();
JSONObject result = (JSONObject) parser.parse(new InputStreamReader(responseStream));
access_token = (String) result.get("access_token");
} catch (Exception e) {
exception = e;
}
} else {
exception = new IllegalArgumentException();
}
return access_token;
}
@Override
protected void onPostExecute(String accessToken) {
super.onPostExecute(accessToken);
if (listener != null) {
if (exception != null) {
listener.onException(exception);
} else {
listener.onTokenReceived(accessToken);
}
}
}
public interface LoginTaskListener {
void onTokenReceived(String accessToken);
void onException(Exception exception);
}
}
@@ -0,0 +1,44 @@
package com.sap.sailing.android.shared.util;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import android.content.Context;
import android.os.AsyncTask;
import com.sap.sailing.android.shared.data.http.HttpJsonGetRequest;
import com.sap.sailing.android.shared.data.http.HttpRequest;
import com.sap.sailing.android.shared.logging.ExLog;
public class LogoutTask extends AsyncTask<String, Void, Void> {
private static final String TAG = LogoutTask.class.getName();
private static final String LOGOUT_REQUEST = "/security/api/restsecurity/remove_access_token";
private Context context;
private URL url;
public LogoutTask(Context ctx, String baseUrl) {
context = ctx;
try {
url = new URL(baseUrl + LOGOUT_REQUEST);
} catch (MalformedURLException e) {
ExLog.e(context, TAG, "Error: Failed to perform checking due to a MalformedURLException: " + e.getMessage());
}
}
@Override
protected Void doInBackground(String... params) {
if (url != null && params != null && params.length > 0) {
HttpRequest request = new HttpJsonGetRequest(url, context);
try {
request.execute();
} catch (IOException e) {
ExLog.e(context, TAG, "Logout not possible: " + e.getMessage());
}
}
return null;
}
}
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"/>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="@drawable/round_button_pressed" android:state_pressed="true" />
<!-- focused -->
<item android:drawable="@drawable/round_button_no_fill" android:state_focused="true" />
<!-- disabled -->
<item android:drawable="@drawable/round_button_no_fill" android:state_enabled="false" />
<!-- default -->
<item android:drawable="@drawable/round_button_no_fill" />
</selector>
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:showIn="@layout/login_backdrop_normal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/change_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/side_padding"
android:src="@drawable/ic_create_light"/>
<TextView
android:id="@+id/server_url_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/change_server"
android:text="@string/server_url"
android:textColor="@color/constant_black"/>
<TextView
android:id="@+id/server_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/server_url_label"
android:layout_toLeftOf="@id/change_server"
android:textColor="@color/constant_black"
tools:text="https://dev.sapsailing.com"/>
</RelativeLayout>
<com.sap.sailing.racecommittee.app.ui.views.FloatLabelLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/side_padding"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
android:id="@+id/user_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"
android:inputType="text"
android:textColor="@color/constant_black"
android:textColorHint="@color/constant_black_light"/>
</com.sap.sailing.racecommittee.app.ui.views.FloatLabelLayout>
<com.sap.sailing.racecommittee.app.ui.views.FloatLabelLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/side_padding"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
android:id="@+id/user_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password"
android:inputType="textPassword"
android:textColor="@color/constant_black"
android:textColorHint="@color/constant_black_light"/>
</com.sap.sailing.racecommittee.app.ui.views.FloatLabelLayout>
<Button
android:id="@+id/login_request"
style="?attr/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/side_padding"
android:text="@string/login"
android:textSize="@dimen/textSize_18"/>
</merge>
@@ -1,25 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/backdrop_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/backdrop" />
android:src="@drawable/backdrop"/>
<ImageView
android:id="@+id/imageView"
android:id="@+id/sap_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/default_margin_half"
android:layout_marginTop="@dimen/default_margin_twice"
android:src="@drawable/sap_logo" />
android:src="@drawable/sap_logo"/>
<LinearLayout
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:background="@color/white_less_transparent"
android:orientation="vertical"
android:padding="@dimen/side_padding"
android:visibility="gone"
tools:visibility="visible">
<include layout="@layout/login_backdrop_login"/>
</LinearLayout>
<LinearLayout
android:id="@+id/login_onboarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:background="@color/white_less_transparent"
android:orientation="vertical"
android:padding="@dimen/side_padding"
android:visibility="gone">
<include layout="@layout/login_backdrop_onboarding"/>
</LinearLayout>
<TextView
android:id="@+id/backdrop_title"
@@ -33,34 +62,32 @@
android:paddingTop="@dimen/default_padding"
android:text="@string/login_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
android:textSize="@dimen/textSize_40"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:background="@color/constant_black_transparent"
android:id="@+id/gradient"
android:alpha="0">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:alpha="0"
android:background="@color/constant_black_transparent">
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:orientation="horizontal"
tools:alpha="1"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true">
android:orientation="horizontal">
<ImageView
android:id="@+id/more"
style="?attr/round"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="@dimen/default_margin_half"
android:layout_marginRight="@dimen/default_margin_half"
android:src="@drawable/ic_more_vert_white_48dp"
android:layout_marginBottom="@dimen/default_margin_half" />
android:src="@drawable/ic_more_vert_white_48dp"/>
</LinearLayout>
<TextView
@@ -69,7 +96,6 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/button_bar"
android:alpha="0"
android:drawableLeft="@drawable/ic_assignment_white_48dp"
android:drawablePadding="@dimen/default_padding_half"
android:gravity="center_vertical"
@@ -79,25 +105,7 @@
android:paddingTop="@dimen/default_padding"
android:text="@string/login_sub_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
<TextView
android:id="@+id/backdrop_onboarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/button_bar"
android:alpha="0"
android:drawableLeft="@drawable/ic_assignment_white_48dp"
android:drawablePadding="@dimen/default_padding_half"
android:gravity="center_vertical"
android:paddingBottom="@dimen/default_padding_half"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:paddingTop="@dimen/default_padding"
android:text="@string/login_onboarding_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
android:textSize="@dimen/textSize_40"/>
</RelativeLayout>
<View
@@ -105,5 +113,5 @@
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_alignParentBottom="true"
android:background="?attr/sap_yellow_1" />
android:background="?attr/sap_yellow_1"/>
</RelativeLayout>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:showIn="@layout/login_backdrop_normal">
<TextView
android:id="@+id/get_started"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/side_padding"
android:text="@string/get_started"
android:textAppearance="?attr/textSmall"/>
<Button
android:id="@+id/scanQr"
style="?attr/button"
android:layout_width="match_parent"
android:layout_height="@dimen/default_button_height"
android:layout_marginBottom="@dimen/side_padding"
android:text="@string/onboarding_scan"
android:textSize="@dimen/textSize_18"/>
<Button
android:id="@+id/manual_input"
style="?attr/button"
android:layout_width="match_parent"
android:layout_height="@dimen/default_button_height"
android:text="@string/onboarding_input"
android:textSize="@dimen/textSize_18"/>
</merge>
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/backdrop_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/backdrop" />
android:src="@drawable/backdrop"/>
<ImageView
android:id="@+id/imageView"
@@ -19,7 +19,37 @@
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:src="@drawable/sap_logo" />
android:src="@drawable/sap_logo"/>
<LinearLayout
android:id="@+id/login_form"
android:layout_width="400dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:background="@color/white_less_transparent"
android:orientation="vertical"
android:padding="@dimen/side_padding"
android:visibility="gone"
tools:visibility="gone">
<include layout="@layout/login_backdrop_login"/>
</LinearLayout>
<LinearLayout
android:id="@+id/login_onboarding"
android:layout_width="400dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:background="@color/white_less_transparent"
android:orientation="vertical"
android:padding="@dimen/side_padding"
android:visibility="gone"
tools:visibility="visible">
<include layout="@layout/login_backdrop_onboarding"/>
</LinearLayout>
<TextView
android:id="@+id/backdrop_title"
@@ -30,34 +60,32 @@
android:padding="@dimen/default_padding"
android:text="@string/login_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
android:textSize="@dimen/textSize_40"/>
<RelativeLayout
android:id="@+id/gradient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/constant_black_transparent"
android:layout_alignParentBottom="true"
android:alpha="0">
android:alpha="0"
android:background="@color/constant_black_transparent">
<LinearLayout
android:id="@id/button_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:orientation="horizontal"
tools:alpha="1"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true">
android:orientation="horizontal">
<ImageView
android:id="@+id/settings_button"
style="?attr/round"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin_half"
android:src="@drawable/ic_settings_white_48dp"
android:layout_marginBottom="@dimen/default_margin" />
android:src="@drawable/ic_settings_white_48dp"/>
<ImageView
android:id="@+id/technical_info"
@@ -65,7 +93,7 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="@dimen/default_margin_half"
android:src="@drawable/ic_info_outline_white_48dp" />
android:src="@drawable/ic_info_outline_white_48dp"/>
<ImageView
android:id="@+id/refresh_data"
@@ -73,7 +101,7 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="@dimen/default_margin_half"
android:src="@drawable/ic_refresh_white_48dp" />
android:src="@drawable/ic_refresh_white_48dp"/>
</LinearLayout>
<TextView
@@ -82,29 +110,13 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/button_bar"
android:alpha="0"
android:drawableLeft="@drawable/ic_assignment_white_48dp"
android:drawablePadding="@dimen/default_padding_half"
android:gravity="center_vertical"
android:padding="@dimen/default_padding"
android:text="@string/login_sub_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
<TextView
android:id="@+id/backdrop_onboarding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/button_bar"
android:alpha="0"
android:drawableLeft="@drawable/ic_assignment_white_48dp"
android:drawablePadding="@dimen/default_padding_half"
android:gravity="center_vertical"
android:padding="@dimen/default_padding"
android:text="@string/login_onboarding_text"
android:textColor="@color/constant_white"
android:textSize="@dimen/textSize_40" />
android:textSize="@dimen/textSize_40"/>
</RelativeLayout>
<View
@@ -112,5 +124,5 @@
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_alignParentBottom="true"
android:background="?attr/sap_yellow_1" />
android:background="?attr/sap_yellow_1"/>
</RelativeLayout>
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/sap_gray">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/get_started"
android:layout_width="wrap_content"
android:text="@string/get_started"
android:layout_height="wrap_content"
android:textAppearance="?attr/textNormal"
android:layout_marginBottom="@dimen/default_margin"
android:layout_gravity="center_horizontal"/>
<include
android:id="@+id/scanQr"
layout="@layout/login_onboarding_scan" />
<include
android:id="@+id/manual_input"
layout="@layout/login_onboarding_input" />
</LinearLayout>
</FrameLayout>
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/button"
android:layout_width="@dimen/default_button_width"
android:layout_height="@dimen/default_button_height"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/onboarding_input"
android:textSize="@dimen/textSize_18"
tools:showIn="@layout/login_listviews" />
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/onboarding_input"
android:textSize="@dimen/textSize_26"
tools:showIn="@layout/login_listviews" />
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/button"
android:layout_width="@dimen/default_button_width"
android:layout_height="@dimen/default_button_height"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/onboarding_scan"
android:textSize="@dimen/textSize_18"
tools:showIn="@layout/login_listviews" />
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/onboarding_scan"
android:textSize="@dimen/textSize_26"
tools:showIn="@layout/login_listviews" />
@@ -19,13 +19,6 @@
android:layout_gravity="center_horizontal|bottom"
tools:layout="@layout/login_listviews"/>
<FrameLayout
android:id="@+id/login_onboarding"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_horizontal|bottom"
tools:layout="@layout/login_onboarding"/>
<RelativeLayout
android:id="@+id/progress_spinner"
android:layout_width="match_parent"
@@ -84,7 +84,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:floatLabelPaddingLeft="9dp"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
@@ -103,7 +103,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:floatLabelPaddingLeft="9dp"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
@@ -79,7 +79,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:floatLabelPaddingLeft="9dp"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
@@ -98,7 +98,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:floatLabelPaddingLeft="9dp"
app:floatLabelPaddingLeft="@dimen/float_label_padding"
app:floatLabelTextAppearance="?attr/floatLabel">
<EditText
@@ -254,7 +254,7 @@
<string name="preference_allow_demo_summary">Wenn aktiv kann man sich im Demo-Modus anmelden. Im Demo-Modus werden keine Rennänderungen an den Server geschickt.</string>
<string name="loading_events_succeded">Events geladen.</string>
<string name="available_races">Verfügbare Rennen</string>
<string name="change">ÄNDERN</string>
<string name="change">Ändern</string>
<string name="race_started">Gestartet %s</string>
<string name="race_finished">Beendet %s</string>
<string name="wind">Wind</string>
@@ -464,7 +464,12 @@
<string name="race_group">Group Race</string>
<string name="wind_select_race">Select Race</string>
<string name="get_started">Getting Started</string>
<string name="get_started_url">http://www.sapsailing.com/gwt/Home.html?locale=de#SolutionsPlace:navigationTab=RaceCommitteeApp</string>
<string name="get_started_url">https://www.sapsailing.com/gwt/Home.html?locale=de#SolutionsPlace:navigationTab=RaceCommitteeApp</string>
<string name="user_unauthorized">Request fehlgeschlagen. User ist nicht autorisiert.</string>
<string name="server_url">Server</string>
<string name="username">Nutzername</string>
<string name="password">Passwort</string>
<string name="wrong_credentials">Nutzername oder Passwort ungültig</string>
<string name="unexpected_error">Ein unerwarteter Fehler ist aufgetreten</string>
</resources>
@@ -7,8 +7,6 @@
<item name="flag_list_item" type="layout">@layout/flag_list_item_xlarge</item>
<item name="login_backdrop" type="layout">@layout/login_backdrop_xlarge</item>
<item name="login_listviews_button" type="layout">@layout/login_listviews_button_xlarge</item>
<item name="login_onboarding_scan" type="layout">@layout/login_onboarding_scan_xlarge</item>
<item name="login_onboarding_input" type="layout">@layout/login_onboarding_input_xlarge</item>
<item name="photo_list_button" type="layout">@layout/photo_list_button_xlarge</item>
<item name="photo_list_item" type="layout">@layout/photo_list_item_xlarge</item>
<item name="race_finishing_button" type="layout">@layout/race_finishing_button_xlarge</item>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="float_label_padding">3dp</dimen>
</resources>
@@ -22,6 +22,7 @@
<attr name="buttonSolidSmall" format="reference"/>
<attr name="buttonSolidNormal" format="reference"/>
<attr name="round" format="reference"/>
<attr name="round_transparent" format="reference"/>
<attr name="text" format="reference"/>
<attr name="textXXSmall" format="reference"/>
<attr name="textXSmall" format="reference"/>
@@ -22,6 +22,8 @@
<dimen name="thick_line">2dp</dimen>
<dimen name="obesity_line">3dp</dimen>
<dimen name="float_label_padding">9dp</dimen>
<dimen name="textSize_14">12sp</dimen> <!-- XXSmall -->
<dimen name="textSize_18">14sp</dimen> <!-- XSmall -->
<dimen name="textSize_20">16sp</dimen>
@@ -8,8 +8,6 @@
<item name="flag_list_item" type="layout">@layout/flag_list_item_normal</item>
<item name="login_backdrop" type="layout">@layout/login_backdrop_normal</item>
<item name="login_listviews_button" type="layout">@layout/login_listviews_button_normal</item>
<item name="login_onboarding_scan" type="layout">@layout/login_onboarding_scan_normal</item>
<item name="login_onboarding_input" type="layout">@layout/login_onboarding_input_normal</item>
<item name="photo_list_button" type="layout">@layout/photo_list_button_normal</item>
<item name="photo_list_item" type="layout">@layout/photo_list_item_normal</item>
<item name="race_finished" type="layout">@layout/race_finished_normal</item>
@@ -286,7 +286,7 @@
<string name="races_all">ALL RACES</string>
<string name="welcome">WELCOME</string>
<string name="change">CHANGE</string>
<string name="change">Change</string>
<string name="start_procedure">Procedure</string>
<string name="start_procedure_more">More</string>
@@ -491,7 +491,12 @@
<string name="race_group">Group Racing</string>
<string name="wind_select_race">Select Race</string>
<string name="get_started">Getting Started</string>
<string name="get_started_url">http://www.sapsailing.com/gwt/Home.html?locale=en#SolutionsPlace:navigationTab=RaceCommitteeApp</string>
<string name="get_started_url">https://www.sapsailing.com/gwt/Home.html?locale=en#SolutionsPlace:navigationTab=RaceCommitteeApp</string>
<string name="user_unauthorized">Request failed. User is unauthorized.</string>
<string name="server_url">Server</string>
<string name="username">Username</string>
<string name="password">Password</string>
<string name="wrong_credentials">Username or password invalid</string>
<string name="unexpected_error">An unexpected error occurred</string>
</resources>
@@ -37,6 +37,7 @@
<item name="buttonSolidSmall">@style/AppTheme.Dark.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Dark.Button.Normal</item>
<item name="round">@style/AppTheme.Dark.Round</item>
<item name="round_transparent">@style/AppTheme.Dark.RoundTransparent</item>
<item name="text">@style/AppTheme.Dark.Text</item>
<item name="textXXSmall">@style/AppTheme.Dark.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Dark.Text.XSmall</item>
@@ -123,6 +124,11 @@
<item name="android:padding">@dimen/logout_padding</item>
</style>
<style name="AppTheme.Dark.RoundTransparent">
<item name="android:background">@drawable/round_button_transparent</item>
<item name="android:padding">@dimen/logout_padding</item>
</style>
<!-- custom styles -->
<style name="AppTheme.Dark.EditText" parent="android:Widget.EditText">
<item name="android:background">@drawable/edit_text_dark</item>
@@ -46,6 +46,7 @@
<item name="buttonSolidSmall">@style/AppTheme.Light.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Light.Button.Normal</item>
<item name="round">@style/AppTheme.Light.Round</item>
<item name="round_transparent">@style/AppTheme.Light.RoundTransparent</item>
<item name="text">@style/AppTheme.Light.Text</item>
<item name="textXXSmall">@style/AppTheme.Light.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Light.Text.XSmall</item>
@@ -144,6 +145,11 @@
<item name="android:padding">@dimen/logout_padding</item>
</style>
<style name="AppTheme.Light.RoundTransparent">
<item name="android:background">@drawable/round_button_transparent</item>
<item name="android:padding">@dimen/logout_padding</item>
</style>
<!-- custom styles -->
<style name="AppTheme.Light.EditText" parent="android:Widget.EditText">
<item name="android:background">@drawable/edit_text_light</item>
@@ -38,7 +38,6 @@ public class AppConstants {
public final static String INTENT_ACTION_EXTRA = "extra";
public final static String INTENT_ACTION_TOGGLE_PROCEDURE = "procedure";
public final static String INTENT_ACTION_TOGGLE_PROCEDURE_MORE = "procedure_more";
public final static String INTENT_ACTION_TOGGLE_PROCEDURE_MORE_MODE = "more_mode";
public final static String INTENT_ACTION_TOGGLE_PROCEDURE_MORE_PATHFINDER = "more_pathfinder";
public final static String INTENT_ACTION_TOGGLE_PROCEDURE_MORE_TIMING = "more_timing";
@@ -62,6 +61,11 @@ public class AppConstants {
public final static String INTENT_ACTION_TIME_HIDE = INTENT_ACTION_TIME + ".hide";
public final static String INTENT_ACTION_TIME_SHOW = INTENT_ACTION_TIME + ".show";
public final static String INTENT_ACTION_CHECK_LOGIN = "show_empty_screen";
public final static String INTENT_ACTION_SHOW_ONBOARDING = "show_onboarding";
public final static String INTENT_ACTION_SHOW_LOGIN = "show_login";
public final static String INTENT_ACTION_VALID_DATA = "valid_data";
// clears all toggle buttons
public final static String INTENT_ACTION_CLEAR_TOGGLE = PACKAGE_NAME + ".action.toggle.clear";
@@ -0,0 +1,7 @@
package com.sap.sailing.racecommittee.app.domain;
public interface BackPressListener {
boolean handleBackPress();
}
@@ -21,7 +21,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -49,11 +48,11 @@ import com.sap.sailing.racecommittee.app.data.DataManager;
import com.sap.sailing.racecommittee.app.data.DataStore;
import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager;
import com.sap.sailing.racecommittee.app.data.clients.LoadClient;
import com.sap.sailing.racecommittee.app.domain.BackPressListener;
import com.sap.sailing.racecommittee.app.domain.LoginType;
import com.sap.sailing.racecommittee.app.domain.configuration.impl.PreferencesDeviceConfigurationLoader;
import com.sap.sailing.racecommittee.app.logging.LogEvent;
import com.sap.sailing.racecommittee.app.ui.fragments.LoginListViews;
import com.sap.sailing.racecommittee.app.ui.fragments.LoginOnboarding;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.AttachedDialogFragment;
import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.DialogListenerHost;
import com.sap.sailing.racecommittee.app.ui.fragments.lists.CourseAreaListFragment;
@@ -92,6 +91,8 @@ public class LoginActivity extends BaseActivity
private ReadonlyDataManager dataManager;
private View progressSpinner;
private BackPressListener mBackPressListener;
private ItemSelectedListener<EventBase> eventSelectionListener = new ItemSelectedListener<EventBase>() {
public void itemSelected(Fragment sender, EventBase event) {
@@ -310,16 +311,11 @@ public class LoginActivity extends BaseActivity
loginListViews = new LoginListViews();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.login_listview, loginListViews);
transaction.replace(R.id.login_onboarding, new LoginOnboarding());
transaction.commitAllowingStateLoss();
new AutoUpdater(this).notifyAfterUpdate();
//setup the backdrop click listener
backdrop = findViewById(R.id.login_view_backdrop);
if (backdrop != null) {
backdrop.setOnClickListener(new BackdropClick());
}
if (!EulaHelper.with(this).isEulaAccepted()) {
EulaHelper.with(this).showEulaDialog(R.style.AppTheme_AlertDialog);
@@ -352,13 +348,15 @@ public class LoginActivity extends BaseActivity
IntentFilter filter = new IntentFilter();
filter.addAction(AppConstants.INTENT_ACTION_RESET);
filter.addAction(AppConstants.INTENT_ACTION_VALID_DATA);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
if (!TextUtils.isEmpty(AppPreferences.on(this).getServerBaseURL())) {
resetData();
} else {
slideUpBackdropDelayed();
}
BroadcastManager.getInstance(this).addIntent(new Intent(AppConstants.INTENT_ACTION_CHECK_LOGIN));
// if (!TextUtils.isEmpty(AppPreferences.on(this).getServerBaseURL())) {
// resetData();
// } else {
// BroadcastManager.getInstance(this).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_LOGIN));
// }
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
@@ -382,6 +380,17 @@ public class LoginActivity extends BaseActivity
dismissProgressSpinner();
}
@Override
public void onBackPressed() {
if (mBackPressListener != null) {
if (!mBackPressListener.handleBackPress()) {
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
private void setupDataManager() {
showProgressSpinner();
@@ -401,8 +410,6 @@ public class LoginActivity extends BaseActivity
Toast.makeText(getApplicationContext(), getString(R.string.loading_configuration_failed), Toast.LENGTH_LONG).show();
ExLog.ex(LoginActivity.this, TAG, reason);
}
slideUpBackdropDelayed();
}
@Override
@@ -450,6 +457,10 @@ public class LoginActivity extends BaseActivity
}
public void setBackPressListener(BackPressListener listener) {
mBackPressListener = listener;
}
private void slideUpBackdropDelayed() {
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@@ -463,21 +474,6 @@ public class LoginActivity extends BaseActivity
private void slideUpBackdrop() {
final View loginView = findViewById(R.id.login_listview);
loginView.setVisibility(View.GONE);
final View onboardingView = findViewById(R.id.login_onboarding);
onboardingView.setVisibility(View.GONE);
ViewHelper.get(backdrop, R.id.backdrop_login).setVisibility(View.GONE);
ViewHelper.get(backdrop, R.id.backdrop_onboarding).setVisibility(View.GONE);
if (TextUtils.isEmpty(AppPreferences.on(this).getServerBaseURL())) {
onboardingView.setVisibility(View.VISIBLE);
ViewHelper.get(backdrop, R.id.backdrop_onboarding).setVisibility(View.VISIBLE);
} else {
loginView.setVisibility(View.VISIBLE);
ViewHelper.get(backdrop, R.id.backdrop_login).setVisibility(View.VISIBLE);
}
// don't slide up if already up
if (backdrop.getY() != 0) {
return;
@@ -497,10 +493,6 @@ public class LoginActivity extends BaseActivity
ViewGroup.LayoutParams lpLogin = loginView.getLayoutParams();
lpLogin.height = val;
loginView.setLayoutParams(lpLogin);
ViewGroup.LayoutParams lpOnboarding = onboardingView.getLayoutParams();
lpOnboarding.height = val;
onboardingView.setLayoutParams(lpOnboarding);
}
});
@@ -509,9 +501,6 @@ public class LoginActivity extends BaseActivity
animators.add(frameAnimation);
animators.add(getAlphaRevAnimator(findViewById(R.id.backdrop_title)));
animators.add(getAlphaAnimator(findViewById(R.id.gradient)));
animators.add(getAlphaAnimator(findViewById(R.id.backdrop_login)));
animators.add(getAlphaAnimator(findViewById(R.id.backdrop_onboarding)));
animators.add(getAlphaAnimator(findViewById(R.id.button_bar)));
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animators);
@@ -540,13 +529,6 @@ public class LoginActivity extends BaseActivity
transaction.commit();
}
private class BackdropClick implements View.OnClickListener {
@Override
public void onClick(View view) {
slideUpBackdrop();
}
}
private class IntentReceiver extends BroadcastReceiver {
@Override
@@ -555,6 +537,8 @@ public class LoginActivity extends BaseActivity
if (AppConstants.INTENT_ACTION_RESET.equals(action)) {
resetData();
} else if (AppConstants.INTENT_ACTION_VALID_DATA.equals(action)) {
resetData();
}
}
}
@@ -2,29 +2,69 @@ package com.sap.sailing.racecommittee.app.ui.fragments;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import org.json.simple.JSONObject;
import android.app.Activity;
import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.UnderlineSpan;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.android.shared.data.LoginData;
import com.sap.sailing.android.shared.data.http.UnauthorizedException;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.util.AuthCheckTask;
import com.sap.sailing.android.shared.util.BroadcastManager;
import com.sap.sailing.android.shared.util.LoginTask;
import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.domain.common.impl.DeviceConfigurationQRCodeUtils;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.domain.BackPressListener;
import com.sap.sailing.racecommittee.app.ui.activities.LoginActivity;
import com.sap.sailing.racecommittee.app.ui.activities.PreferenceActivity;
import com.sap.sailing.racecommittee.app.ui.activities.SystemInformationActivity;
import com.sap.sailing.racecommittee.app.ui.fragments.preference.GeneralPreferenceFragment;
import com.sap.sailing.racecommittee.app.utils.UrlHelper;
import com.sap.sailing.racecommittee.app.utils.autoupdate.AutoUpdater;
public class LoginBackdrop extends Fragment {
public class LoginBackdrop extends Fragment implements LoginTask.LoginTaskListener, AuthCheckTask.AuthCheckTaskListener, BackPressListener {
private static final String TAG = LoginBackdrop.class.getName();
private static final int requestCodeQR = 45392;
private IntentReceiver receiver;
private View login;
private View onboarding;
private boolean useBack;
private String server;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.login_backdrop, container, false);
@@ -61,71 +101,55 @@ public class LoginBackdrop extends Fragment {
ImageView more = ViewHelper.get(layout, R.id.more);
if (more != null) {
more.setOnClickListener(new View.OnClickListener() {
//Because of massive usage of reflection (try {} catch ())
//Don't know how to fix the warning a better way
@Override
public void onClick(View view) {
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
popupMenu.inflate(R.menu.login_menu);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.technical_info:
openInfo();
break;
case R.id.settings_button:
openSettings();
break;
default:
refreshData();
}
return true;
}
});
popupMenu.show();
// Try to force some vertical offset
try {
Object menuHelper;
Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
fMenuHelper.setAccessible(true);
menuHelper = fMenuHelper.get(popupMenu);
Field fListPopup = menuHelper.getClass().getDeclaredField("mPopup");
fListPopup.setAccessible(true);
Object listPopup = fListPopup.get(menuHelper);
Class<?> listPopupClass = listPopup.getClass();
int height = view.getHeight();
// Invoke setVerticalOffset() with the negative height to move up by that distance
Method setVerticalOffset = listPopupClass.getDeclaredMethod("setVerticalOffset", int.class);
setVerticalOffset.invoke(listPopup, -height);
int width = (Integer) listPopupClass.getDeclaredMethod("getWidth").invoke(listPopup);
width -= view.getWidth();
// Invoke setHorizontalOffset() with the negative height to move up by that distance
Method setHorizontalOffset = listPopupClass.getDeclaredMethod("setHorizontalOffset", int.class);
setHorizontalOffset.invoke(listPopup, -width);
// Invoke show() to update the window's position
Method show = listPopupClass.getDeclaredMethod("show");
show.invoke(listPopup);
} catch (Exception e) {
// an exception here indicates a programming error rather than an exceptional condition
// at runtime
ExLog.w(getActivity(), TAG, "Unable to force offset" + e.getLocalizedMessage());
}
}
});
more.setOnClickListener(new OverFlowButton());
}
setupOnboarding(layout);
setupLogin(layout);
receiver = new IntentReceiver();
return layout;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof LoginActivity) {
LoginActivity login = (LoginActivity) activity;
login.setBackPressListener(this);
}
}
@Override
public void onDetach() {
super.onDetach();
if (getActivity() instanceof LoginActivity) {
LoginActivity activity = (LoginActivity) getActivity();
activity.setBackPressListener(null);
}
}
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(AppConstants.INTENT_ACTION_CHECK_LOGIN);
filter.addAction(AppConstants.INTENT_ACTION_SHOW_LOGIN);
filter.addAction(AppConstants.INTENT_ACTION_SHOW_ONBOARDING);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, filter);
}
@Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
}
private void refreshData() {
Intent intent = new Intent(AppConstants.INTENT_ACTION_RESET);
BroadcastManager.getInstance(getActivity()).addIntent(intent);
@@ -141,4 +165,306 @@ public class LoginBackdrop extends Fragment {
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, GeneralPreferenceFragment.class.getName());
startActivity(intent);
}
private void setupOnboarding(View layout) {
onboarding = ViewHelper.get(layout, R.id.login_onboarding);
TextView link = ViewHelper.get(layout, R.id.get_started);
if (link != null) {
underlineText(link);
link.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.get_started_url)));
startActivity(intent);
}
});
}
Button scan = ViewHelper.get(layout, R.id.scanQr);
if (scan != null) {
scan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, requestCodeQR);
} catch (Exception e) {
Uri marketUri = Uri.parse("market://details?id=com.google.zxing.client.android");
Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
startActivity(marketIntent);
}
}
});
}
Button manual = ViewHelper.get(layout, R.id.manual_input);
if (manual != null) {
manual.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View view = View.inflate(v.getContext(), R.layout.login_onboarding_edit, null);
final EditText url = (EditText) view.findViewById(R.id.url);
final EditText device_id = (EditText) view.findViewById(R.id.device_id);
device_id.setText(AppPreferences.on(v.getContext()).getDeviceIdentifier());
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext(), R.style.AppTheme_AlertDialog);
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (saveData(
url.getText().toString() + "#" + DeviceConfigurationQRCodeUtils.deviceIdentifierKey + "=" + device_id.getText()
.toString())) {
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(AppConstants.INTENT_ACTION_CHECK_LOGIN));
}
}
});
builder.setNegativeButton(android.R.string.cancel, null);
AlertDialog dialog = builder.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
});
}
}
private void setupLogin(View layout) {
login = ViewHelper.get(layout, R.id.login_form);
View change = ViewHelper.get(layout, R.id.change_server);
if (change != null) {
change.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
useBack = true;
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_ONBOARDING));
}
});
}
TextView url = ViewHelper.get(layout, R.id.server_url);
if (url != null) {
if ("\"SAP Sailing Analytics\"".equals(server)) {
server = null;
}
if (TextUtils.isEmpty(server)) {
server = AppPreferences.on(getActivity()).getServerBaseURL();
}
if (TextUtils.isEmpty(server)) {
server = getString(R.string.not_available);
}
url.setText(server.replace("\"", ""));
}
final EditText userName = ViewHelper.get(layout, R.id.user_name);
final EditText userPassword = ViewHelper.get(layout, R.id.user_password);
Button login = ViewHelper.get(layout, R.id.login_request);
if (login != null) {
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LoginTask task = new LoginTask(getActivity(), AppPreferences.on(getActivity()).getServerBaseURL(), LoginBackdrop.this);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new LoginData(userName.getText().toString(), userPassword.getText()
.toString()));
}
});
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != requestCodeQR) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
switch (resultCode) {
case Activity.RESULT_CANCELED:
break;
case Activity.RESULT_OK:
if (saveData(data.getStringExtra("SCAN_RESULT"))) {
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_CHECK_LOGIN));
}
break;
default:
Toast.makeText(getActivity(), getString(R.string.error_scanning_qr, resultCode), Toast.LENGTH_LONG).show();
}
}
private boolean saveData(String content) {
try {
DeviceConfigurationQRCodeUtils.DeviceConfigurationDetails connectionConfiguration = DeviceConfigurationQRCodeUtils
.splitQRContent(content);
final String identifier = connectionConfiguration.getDeviceIdentifier();
final URL apkUrl = UrlHelper.tryConvertToURL(connectionConfiguration.getApkUrl());
final String accessToken = connectionConfiguration.getAccessToken();
if (apkUrl != null) {
String serverUrl = UrlHelper.getServerUrl(apkUrl);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
editor.putString(getString(R.string.preference_identifier_key), identifier);
editor.putString(getString(R.string.preference_server_url_key), serverUrl);
editor.putString(getString(R.string.preference_access_token_key), accessToken);
editor.commit();
new AutoUpdater(getActivity()).checkForUpdate(false);
return true;
} else {
Toast.makeText(getActivity(), getString(R.string.error_scanning_qr_malformed), Toast.LENGTH_LONG).show();
}
} catch (IllegalArgumentException e) {
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
}
return false;
}
private void underlineText(TextView textView) {
if (textView != null) {
SpannableString string = new SpannableString(textView.getText());
string.setSpan(new UnderlineSpan(), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(string);
}
}
@Override
public void onTokenReceived(String accessToken) {
if (login != null) {
login.setVisibility(View.GONE);
}
AppPreferences.on(getActivity()).setAccessToken(accessToken);
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_VALID_DATA));
}
@Override
public void onRequestReceived(JSONObject json) {
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_VALID_DATA));
}
@Override
public void onException(Exception exception) {
if (login != null) {
if (login.getVisibility() == View.VISIBLE) {
if (exception instanceof UnauthorizedException) {
Toast.makeText(getActivity(), R.string.wrong_credentials, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), R.string.unexpected_error, Toast.LENGTH_LONG).show();
}
} else {
server = null;
if (exception instanceof UnauthorizedException) {
server = exception.getMessage().split("=")[1];
}
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_LOGIN));
}
}
}
@Override
public boolean handleBackPress() {
if (useBack) {
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_LOGIN));
useBack = false;
return true;
} else {
return false;
}
}
private class OverFlowButton implements View.OnClickListener {
//Because of massive usage of reflection (try {} catch ())
//Don't know how to fix the warning a better way
@Override
public void onClick(View view) {
if (view.getVisibility() == View.VISIBLE && view.getAlpha() == 1) {
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
popupMenu.inflate(R.menu.login_menu);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.technical_info:
openInfo();
break;
case R.id.settings_button:
openSettings();
break;
default:
refreshData();
}
return true;
}
});
popupMenu.show();
// Try to force some vertical offset
try {
Object menuHelper;
Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
fMenuHelper.setAccessible(true);
menuHelper = fMenuHelper.get(popupMenu);
Field fListPopup = menuHelper.getClass().getDeclaredField("mPopup");
fListPopup.setAccessible(true);
Object listPopup = fListPopup.get(menuHelper);
Class<?> listPopupClass = listPopup.getClass();
int height = view.getHeight();
// Invoke setVerticalOffset() with the negative height to move up by that distance
Method setVerticalOffset = listPopupClass.getDeclaredMethod("setVerticalOffset", int.class);
setVerticalOffset.invoke(listPopup, -height);
int width = (Integer) listPopupClass.getDeclaredMethod("getWidth").invoke(listPopup);
width -= view.getWidth();
// Invoke setHorizontalOffset() with the negative height to move up by that distance
Method setHorizontalOffset = listPopupClass.getDeclaredMethod("setHorizontalOffset", int.class);
setHorizontalOffset.invoke(listPopup, -width);
// Invoke show() to update the window's position
Method show = listPopupClass.getDeclaredMethod("show");
show.invoke(listPopup);
} catch (Exception e) {
// an exception here indicates a programming error rather than an exceptional condition
// at runtime
ExLog.w(getActivity(), TAG, "Unable to force offset" + e.getLocalizedMessage());
}
}
}
}
private class IntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
setVisibility(onboarding, View.GONE);
setVisibility(login, View.GONE);
if (AppConstants.INTENT_ACTION_CHECK_LOGIN.equals(action)) {
AppPreferences pref = AppPreferences.on(getActivity());
if (TextUtils.isEmpty(pref.getServerBaseURL())) {
BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_ONBOARDING));
} else {
AuthCheckTask task = new AuthCheckTask(getActivity(), pref.getServerBaseURL(), LoginBackdrop.this);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
} else if (AppConstants.INTENT_ACTION_SHOW_ONBOARDING.equals(action)) {
setVisibility(onboarding, View.VISIBLE);
} else if (AppConstants.INTENT_ACTION_SHOW_LOGIN.equals(action)) {
setupLogin(getView());
setVisibility(login, View.VISIBLE);
}
}
private void setVisibility(View view, int visibility) {
if (view != null) {
view.setVisibility(visibility);
}
}
}
}
@@ -1,153 +0,0 @@
package com.sap.sailing.racecommittee.app.ui.fragments;
import java.net.URL;
import android.app.Activity;
import android.app.Fragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.UnderlineSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.sap.sailing.android.shared.util.ViewHelper;
import com.sap.sailing.domain.common.impl.DeviceConfigurationQRCodeUtils;
import com.sap.sailing.domain.common.impl.DeviceConfigurationQRCodeUtils.DeviceConfigurationDetails;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.AppPreferences;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.utils.UrlHelper;
import com.sap.sailing.racecommittee.app.utils.autoupdate.AutoUpdater;
public class LoginOnboarding extends Fragment {
private static final int requestCodeQR = 45392;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.login_onboarding, container, false);
TextView link = ViewHelper.get(layout, R.id.get_started);
if (link != null) {
SpannableString string = new SpannableString(link.getText());
string.setSpan(new UnderlineSpan(), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
link.setText(string);
link.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.get_started_url)));
startActivity(intent);
}
});
}
Button scan = ViewHelper.get(layout, R.id.scanQr);
if (scan != null) {
scan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, requestCodeQR);
} catch (Exception e) {
Uri marketUri = Uri.parse("market://details?id=com.google.zxing.client.android");
Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
startActivity(marketIntent);
}
}
});
}
Button manual = ViewHelper.get(layout, R.id.manual_input);
if (manual != null) {
manual.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View view = View.inflate(v.getContext(), R.layout.login_onboarding_edit, null);
final EditText url = (EditText) view.findViewById(R.id.url);
final EditText device_id = (EditText) view.findViewById(R.id.device_id);
device_id.setText(AppPreferences.on(v.getContext()).getDeviceIdentifier());
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext(), R.style.AppTheme_AlertDialog);
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (saveData(url.getText().toString() + "#" + DeviceConfigurationQRCodeUtils.deviceIdentifierKey + "=" + device_id.getText().toString())) {
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(AppConstants.INTENT_ACTION_RESET));
}
}
});
builder.setNegativeButton(android.R.string.cancel, null);
AlertDialog dialog = builder.show();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
});
}
return layout;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != requestCodeQR) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
switch (resultCode) {
case Activity.RESULT_CANCELED:
break;
case Activity.RESULT_OK:
saveData(data.getStringExtra("SCAN_RESULT"));
break;
default:
Toast.makeText(getActivity(), getString(R.string.error_scanning_qr, resultCode), Toast.LENGTH_LONG).show();
}
}
private boolean saveData(String content) {
try {
DeviceConfigurationDetails connectionConfiguration = DeviceConfigurationQRCodeUtils.splitQRContent(content);
final String identifier = connectionConfiguration.getDeviceIdentifier();
final URL apkUrl = UrlHelper.tryConvertToURL(connectionConfiguration.getApkUrl());
final String accessToken = connectionConfiguration.getAccessToken();
if (apkUrl != null) {
String serverUrl = UrlHelper.getServerUrl(apkUrl);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit();
editor.putString(getString(R.string.preference_identifier_key), identifier);
editor.putString(getString(R.string.preference_server_url_key), serverUrl);
editor.putString(getString(R.string.preference_access_token_key), accessToken);
editor.commit();
new AutoUpdater(getActivity()).checkForUpdate(false);
return true;
} else {
Toast.makeText(getActivity(), getString(R.string.error_scanning_qr_malformed), Toast.LENGTH_LONG).show();
}
} catch (IllegalArgumentException e) {
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
}
return false;
}
}