Fixes a crash during authentication and another when opening the composer options. Also, sets up the next update to reset the redirect URI for the app during authorization.

main
Vavassor 8 years ago
parent 3c655a25dc
commit 2cb0b96abd
  1. 17
      app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java
  2. 45
      app/src/main/java/com/keylesspalace/tusky/ComposeOptionsFragment.java
  3. 47
      app/src/main/java/com/keylesspalace/tusky/LoginActivity.java
  4. 4
      app/src/main/res/values/donottranslate.xml
  5. 7
      app/src/main/res/values/strings.xml

@ -93,7 +93,7 @@ import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
public class ComposeActivity extends BaseActivity {
public class ComposeActivity extends BaseActivity implements ComposeOptionsFragment.Listener {
private static final String TAG = "ComposeActivity"; // logging tag
private static final int STATUS_CHARACTER_LIMIT = 500;
private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB
@ -525,28 +525,17 @@ public class ComposeActivity extends BaseActivity {
private void showComposeOptions() {
ComposeOptionsFragment fragment = ComposeOptionsFragment.newInstance(
statusVisibility, statusHideText, inReplyToId != null,
new ComposeOptionsFragment.Listener() {
@Override
public int describeContents() {
return 0;
statusVisibility, statusHideText, inReplyToId != null);
fragment.show(getSupportFragmentManager(), null);
}
@Override
public void writeToParcel(Parcel dest, int flags) {}
@Override
public void onVisibilityChanged(String visibility) {
setStatusVisibility(visibility);
}
@Override
public void onContentWarningChanged(boolean hideText) {
showContentWarning(hideText);
}
});
fragment.show(getSupportFragmentManager(), null);
}
private void sendStatus() {
if (statusAlreadyInFlight) {

@ -1,5 +1,6 @@
package com.keylesspalace.tusky;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
@ -12,19 +13,22 @@ import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import java.util.List;
public class ComposeOptionsFragment extends BottomSheetDialogFragment {
public interface Listener extends Parcelable {
interface Listener {
void onVisibilityChanged(String visibility);
void onContentWarningChanged(boolean hideText);
}
private RadioGroup radio;
private CheckBox hideText;
private Listener listener;
public static ComposeOptionsFragment newInstance(String visibility,
boolean hideText, boolean isReply, Listener listener) {
public static ComposeOptionsFragment newInstance(String visibility, boolean hideText,
boolean isReply) {
Bundle arguments = new Bundle();
ComposeOptionsFragment fragment = new ComposeOptionsFragment();
arguments.putParcelable("listener", listener);
arguments.putString("visibility", visibility);
arguments.putBoolean("hideText", hideText);
arguments.putBoolean("isReply", isReply);
@ -32,6 +36,12 @@ public class ComposeOptionsFragment extends BottomSheetDialogFragment {
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
listener = (Listener) context;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@ -39,12 +49,11 @@ public class ComposeOptionsFragment extends BottomSheetDialogFragment {
View rootView = inflater.inflate(R.layout.fragment_compose_options, container, false);
Bundle arguments = getArguments();
listener = arguments.getParcelable("listener");
String statusVisibility = arguments.getString("visibility");
boolean statusHideText = arguments.getBoolean("hideText");
boolean isReply = arguments.getBoolean("isReply");
RadioGroup radio = (RadioGroup) rootView.findViewById(R.id.radio_visibility);
radio = (RadioGroup) rootView.findViewById(R.id.radio_visibility);
int radioCheckedId;
if (!isReply) {
radioCheckedId = R.id.radio_public;
@ -59,6 +68,21 @@ public class ComposeOptionsFragment extends BottomSheetDialogFragment {
}
}
radio.check(radioCheckedId);
if (isReply) {
RadioButton publicButton = (RadioButton) rootView.findViewById(R.id.radio_public);
publicButton.setEnabled(false);
}
hideText = (CheckBox) rootView.findViewById(R.id.compose_hide_text);
hideText.setChecked(statusHideText);
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
@ -81,20 +105,11 @@ public class ComposeOptionsFragment extends BottomSheetDialogFragment {
listener.onVisibilityChanged(visibility);
}
});
if (isReply) {
RadioButton publicButton = (RadioButton) rootView.findViewById(R.id.radio_public);
publicButton.setEnabled(false);
}
CheckBox hideText = (CheckBox) rootView.findViewById(R.id.compose_hide_text);
hideText.setChecked(statusHideText);
hideText.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
listener.onContentWarningChanged(isChecked);
}
});
return rootView;
}
}

@ -20,6 +20,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
@ -33,8 +34,6 @@ import android.widget.TextView;
import com.keylesspalace.tusky.entity.AccessToken;
import com.keylesspalace.tusky.entity.AppCredentials;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
@ -60,14 +59,6 @@ public class LoginActivity extends AppCompatActivity {
@BindView(R.id.button_login) Button button;
@BindView(R.id.whats_an_instance) TextView whatsAnInstance;
private static String urlEncode(String string) {
try {
return URLEncoder.encode(string, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Failed to encode the string.", e);
}
}
/**
* Chain together the key-value pairs into a query string, for either appending to a URL or
* as the content of an HTTP request.
@ -77,9 +68,9 @@ public class LoginActivity extends AppCompatActivity {
String between = "";
for (Map.Entry<String, String> entry : parameters.entrySet()) {
s.append(between);
s.append(urlEncode(entry.getKey()));
s.append(Uri.encode(entry.getKey()));
s.append("=");
s.append(urlEncode(entry.getValue()));
s.append(Uri.encode(entry.getValue()));
between = "&";
}
return s.toString();
@ -98,7 +89,7 @@ public class LoginActivity extends AppCompatActivity {
return scheme + "://" + host + "/";
}
private void redirectUserToAuthorizeAndLogin() {
private void redirectUserToAuthorizeAndLogin(EditText editText) {
/* To authorize this app and log in it's necessary to redirect to the domain given,
* activity_login there, and the server will redirect back to the app with its response. */
String endpoint = MastodonAPI.ENDPOINT_AUTHORIZE;
@ -109,8 +100,12 @@ public class LoginActivity extends AppCompatActivity {
parameters.put("response_type", "code");
parameters.put("scope", OAUTH_SCOPES);
String url = "https://" + domain + endpoint + "?" + toQueryString(parameters);
Intent viewIntent = new Intent("android.intent.action.VIEW", Uri.parse(url));
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (viewIntent.resolveActivity(getPackageManager()) != null) {
startActivity(viewIntent);
} else {
editText.setError(getString(R.string.error_no_web_browser_found));
}
}
private MastodonAPI getApiFor(String domain) {
@ -139,7 +134,7 @@ public class LoginActivity extends AppCompatActivity {
if (prefClientId != null && prefClientSecret != null) {
clientId = prefClientId;
clientSecret = prefClientSecret;
redirectUserToAuthorizeAndLogin();
redirectUserToAuthorizeAndLogin(editText);
} else {
Callback<AppCredentials> callback = new Callback<AppCredentials>() {
@Override
@ -156,7 +151,7 @@ public class LoginActivity extends AppCompatActivity {
editor.putString(domain + "/client_id", clientId);
editor.putString(domain + "/client_secret", clientSecret);
editor.apply();
redirectUserToAuthorizeAndLogin();
redirectUserToAuthorizeAndLogin(editText);
}
@Override
@ -225,6 +220,26 @@ public class LoginActivity extends AppCompatActivity {
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
});
// Apply any updates needed.
int versionCode = 1;
try {
versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "The app version was not found. " + e.getMessage());
}
if (preferences.getInt("lastUpdate", 0) != versionCode) {
SharedPreferences.Editor editor = preferences.edit();
if (versionCode == 14) {
/* This version switches the order of scheme and host in the OAuth redirect URI.
* But to fix it requires forcing the app to re-authenticate with servers. So, clear
* out the stored client id/secret pairs. The only other things that are lost are
* "rememberedVisibility", "loggedInUsername", and "loggedInAccountId". */
editor.clear();
}
editor.putInt("lastUpdate", versionCode);
editor.apply();
}
}
@Override

@ -4,7 +4,7 @@
<string name="app_website">http://tusky.keylesspalace.com</string>
<string name="tusky_api_url">https://tuskynotifier.keylesspalace.com</string>
<string name="oauth_scheme">com.keylesspalace.tusky</string>
<string name="oauth_redirect_host">oauth2redirect</string>
<string name="oauth_scheme">oauth2redirect</string>
<string name="oauth_redirect_host">com.keylesspalace.tusky</string>
<string name="preferences_file_key">com.keylesspalace.tusky.PREFERENCES</string>
</resources>

@ -1,8 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<string name="error_no_web_browser_found">Couldn\'t find a web browser to use.</string>
<string name="error_authorization_unknown">An unidentified authorization error occurred.</string>
<string name="error_authorization_denied" tools:ignore="MissingTranslation">Authorization was denied.</string>
<string name="error_retrieving_oauth_token" tools:ignore="MissingTranslation">Failed getting a login token.</string>
<string name="error_authorization_denied">Authorization was denied.</string>
<string name="error_retrieving_oauth_token">Failed getting a login token.</string>
<string name="error_fetching_notifications">Notifications could not be fetched.</string>
<string name="error_compose_character_limit">The status is too long!</string>
<string name="error_sending_status">The status failed to be sent.</string>

Loading…
Cancel
Save