Fixed crash on pre-Lollipop devices (API levels 20 and prior) due to trying to load attributes in drawables.

This commit is contained in:
Vavassor 2017-02-16 21:11:05 -05:00
parent 8964c61397
commit af4af94775
19 changed files with 130 additions and 46 deletions

View file

@ -107,7 +107,7 @@ public class AccountActivity extends BaseActivity {
int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
getResources().getDisplayMetrics()); getResources().getDisplayMetrics());
viewPager.setPageMargin(pageMargin); viewPager.setPageMargin(pageMargin);
viewPager.setPageMarginDrawable(R.drawable.tab_page_margin); viewPager.setPageMarginDrawable(R.drawable.tab_page_margin_dark);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
tabLayout = (TabLayout) findViewById(R.id.tab_layout); tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);

View file

@ -99,7 +99,7 @@ public class AccountFragment extends Fragment implements AccountActionListener,
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration( DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation()); context, layoutManager.getOrientation());
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider); Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider_dark);
divider.setDrawable(drawable); divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider); recyclerView.addItemDecoration(divider);
scrollListener = new EndlessOnScrollListener(layoutManager) { scrollListener = new EndlessOnScrollListener(layoutManager) {

View file

@ -242,15 +242,6 @@ public class ComposeActivity extends BaseActivity {
} }
} }
private static int getThemeColor(Context context, int attribute) {
TypedValue value = new TypedValue();
if (context.getTheme().resolveAttribute(attribute, value, true)) {
return value.data;
} else {
return android.R.color.black;
}
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -270,7 +261,7 @@ public class ComposeActivity extends BaseActivity {
textEditor = (EditText) findViewById(R.id.field_status); textEditor = (EditText) findViewById(R.id.field_status);
final TextView charactersLeft = (TextView) findViewById(R.id.characters_left); final TextView charactersLeft = (TextView) findViewById(R.id.characters_left);
final int mentionColour = getThemeColor(this, R.attr.compose_mention_color); final int mentionColour = ThemeUtils.getColor(this, R.attr.compose_mention_color);
TextWatcher textEditorWatcher = new TextWatcher() { TextWatcher textEditorWatcher = new TextWatcher() {
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {

View file

@ -205,10 +205,12 @@ public class LoginActivity extends BaseActivity {
editor.putString("domain", domain); editor.putString("domain", domain);
editor.putString("clientId", clientId); editor.putString("clientId", clientId);
editor.putString("clientSecret", clientSecret); editor.putString("clientSecret", clientSecret);
editor.commit(); editor.apply();
} }
private void onLoginSuccess(String accessToken) { private void onLoginSuccess(String accessToken) {
preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString("accessToken", accessToken); editor.putString("accessToken", accessToken);
editor.apply(); editor.apply();
@ -233,6 +235,8 @@ public class LoginActivity extends BaseActivity {
/* During the redirect roundtrip this Activity usually dies, which wipes out the /* During the redirect roundtrip this Activity usually dies, which wipes out the
* instance variables, so they have to be recovered from where they were saved in * instance variables, so they have to be recovered from where they were saved in
* SharedPreferences. */ * SharedPreferences. */
preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
domain = preferences.getString("domain", null); domain = preferences.getString("domain", null);
clientId = preferences.getString("clientId", null); clientId = preferences.getString("clientId", null);
clientSecret = preferences.getString("clientSecret", null); clientSecret = preferences.getString("clientSecret", null);
@ -247,6 +251,7 @@ public class LoginActivity extends BaseActivity {
parameters.put("grant_type", "authorization_code"); parameters.put("grant_type", "authorization_code");
} catch (JSONException e) { } catch (JSONException e) {
errorText.setText("Heck."); errorText.setText("Heck.");
return;
//TODO: I don't even know how to handle this error state. //TODO: I don't even know how to handle this error state.
} }
String endpoint = getString(R.string.endpoint_token); String endpoint = getString(R.string.endpoint_token);
@ -256,11 +261,12 @@ public class LoginActivity extends BaseActivity {
new Response.Listener<JSONObject>() { new Response.Listener<JSONObject>() {
@Override @Override
public void onResponse(JSONObject response) { public void onResponse(JSONObject response) {
String accessToken = ""; String accessToken;
try { try {
accessToken = response.getString("access_token"); accessToken = response.getString("access_token");
} catch(JSONException e) { } catch(JSONException e) {
errorText.setText("Heck."); errorText.setText("Heck.");
return;
//TODO: I don't even know how to handle this error state. //TODO: I don't even know how to handle this error state.
} }
onLoginSuccess(accessToken); onLoginSuccess(accessToken);

View file

@ -20,6 +20,7 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
@ -72,7 +73,9 @@ public class MainActivity extends BaseActivity {
ViewPager viewPager = (ViewPager) findViewById(R.id.pager); ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin); int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
viewPager.setPageMargin(pageMargin); viewPager.setPageMargin(pageMargin);
viewPager.setPageMarginDrawable(R.drawable.tab_page_margin); Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
R.drawable.tab_page_margin_dark);
viewPager.setPageMarginDrawable(pageMarginDrawable);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);

View file

@ -77,7 +77,7 @@ public class NotificationsFragment extends SFragment implements
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration( DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation()); context, layoutManager.getOrientation());
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider); Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider_dark);
divider.setDrawable(drawable); divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider); recyclerView.addItemDecoration(divider);
scrollListener = new EndlessOnScrollListener(layoutManager) { scrollListener = new EndlessOnScrollListener(layoutManager) {

View file

@ -154,6 +154,7 @@ public class PullNotificationService extends IntentService {
} }
private void loadAvatar(final List<MentionResult> mentions, String url) { private void loadAvatar(final List<MentionResult> mentions, String url) {
if (url != null) {
ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() { ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override @Override
public void onResponse(Bitmap response) { public void onResponse(Bitmap response) {
@ -166,6 +167,9 @@ public class PullNotificationService extends IntentService {
} }
}); });
VolleySingleton.getInstance(this).addToRequestQueue(request); VolleySingleton.getInstance(this).addToRequestQueue(request);
} else {
updateNotification(mentions, null);
}
} }
private void updateNotification(List<MentionResult> mentions, @Nullable Bitmap icon) { private void updateNotification(List<MentionResult> mentions, @Nullable Bitmap icon) {

View file

@ -80,10 +80,12 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
mediaPreview1 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_1); mediaPreview1 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_1);
mediaPreview2 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_2); mediaPreview2 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_2);
mediaPreview3 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_3); mediaPreview3 = (NetworkImageView) itemView.findViewById(R.id.status_media_preview_3);
mediaPreview0.setDefaultImageResId(R.drawable.media_preview_unloaded); int mediaPreviewUnloadedId = ThemeUtils.getDrawableId(itemView.getContext(),
mediaPreview1.setDefaultImageResId(R.drawable.media_preview_unloaded); R.attr.media_preview_unloaded_drawable, android.R.color.black);
mediaPreview2.setDefaultImageResId(R.drawable.media_preview_unloaded); mediaPreview0.setDefaultImageResId(mediaPreviewUnloadedId);
mediaPreview3.setDefaultImageResId(R.drawable.media_preview_unloaded); mediaPreview1.setDefaultImageResId(mediaPreviewUnloadedId);
mediaPreview2.setDefaultImageResId(mediaPreviewUnloadedId);
mediaPreview3.setDefaultImageResId(mediaPreviewUnloadedId);
sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning); sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning);
contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar); contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar);
contentWarningDescription = contentWarningDescription =

View file

@ -0,0 +1,52 @@
/* Copyright 2017 Andrew Dawson
*
* This file is part of Tusky.
*
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky. If not, see
* <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.util.TypedValue;
public class ThemeUtils {
public static Drawable getDrawable(Context context, int attribute, int fallbackDrawable) {
TypedValue value = new TypedValue();
int resourceId;
if (context.getTheme().resolveAttribute(attribute, value, true)) {
resourceId = value.resourceId;
} else {
resourceId = fallbackDrawable;
}
return ContextCompat.getDrawable(context, resourceId);
}
public static int getDrawableId(Context context, int attribute, int fallbackDrawableId) {
TypedValue value = new TypedValue();
if (context.getTheme().resolveAttribute(attribute, value, true)) {
return value.resourceId;
} else {
return fallbackDrawableId;
}
}
public static int getColor(Context context, int attribute) {
TypedValue value = new TypedValue();
if (context.getTheme().resolveAttribute(attribute, value, true)) {
return value.data;
} else {
return android.R.color.black;
}
}
}

View file

@ -102,7 +102,8 @@ public class TimelineFragment extends SFragment implements
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration( DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation()); context, layoutManager.getOrientation());
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider); Drawable drawable = ThemeUtils.getDrawable(context, R.attr.status_divider_drawable,
R.drawable.status_divider_dark);
divider.setDrawable(drawable); divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider); recyclerView.addItemDecoration(divider);
scrollListener = new EndlessOnScrollListener(layoutManager) { scrollListener = new EndlessOnScrollListener(layoutManager) {

View file

@ -60,7 +60,7 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration divider = new DividerItemDecoration( DividerItemDecoration divider = new DividerItemDecoration(
context, layoutManager.getOrientation()); context, layoutManager.getOrientation());
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider); Drawable drawable = ContextCompat.getDrawable(context, R.drawable.status_divider_dark);
divider.setDrawable(drawable); divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider); recyclerView.addItemDecoration(divider);
adapter = new ThreadAdapter(this); adapter = new ThreadAdapter(this);

View file

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="?attr/media_preview_unloaded_background_color" /> <solid android:color="@color/media_preview_unloaded_background_dark" />
<stroke <stroke
android:dashWidth="4dp" android:dashWidth="4dp"

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/media_preview_unloaded_background_light" />
<stroke
android:dashWidth="4dp"
android:dashGap="4dp"
android:width="2dp"
android:color="#AFAFAF" />
</shape>

View file

@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<size android:height="1dp" /> <size android:height="1dp" />
<solid android:color="?attr/status_divider_color" /> <solid android:color="@color/status_divider_dark" />
</shape> </shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="1dp" />
<solid android:color="@color/status_divider_light" />
</shape>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="?attr/tab_page_margin_color" /> <solid android:color="@color/tab_page_margin_dark" />
</shape> </shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tab_page_margin_light" />
</shape>

View file

@ -18,10 +18,10 @@
<attr name="status_reblog_button_style" format="reference" /> <attr name="status_reblog_button_style" format="reference" />
<attr name="content_warning_button" format="reference" /> <attr name="content_warning_button" format="reference" />
<attr name="sensitive_media_warning_background_color" format="reference|color" /> <attr name="sensitive_media_warning_background_color" format="reference|color" />
<attr name="media_preview_unloaded_background_color" format="reference|color" />
<attr name="status_text_color_secondary" format="reference|color" /> <attr name="status_text_color_secondary" format="reference|color" />
<attr name="status_divider_color" format="reference|color" /> <attr name="media_preview_unloaded_drawable" format="reference" />
<attr name="tab_page_margin_color" format="reference|color" /> <attr name="status_divider_drawable" format="reference" />
<attr name="tab_page_margin_drawable" format="reference" />
<attr name="notification_content" format="reference" /> <attr name="notification_content" format="reference" />
<attr name="notification_icon_tint" format="reference|color" /> <attr name="notification_icon_tint" format="reference|color" />
<attr name="account_header_background_color" format="reference|color" /> <attr name="account_header_background_color" format="reference|color" />

View file

@ -31,10 +31,10 @@
<item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Dark</item> <item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Dark</item>
<item name="content_warning_button">@drawable/toggle_small</item> <item name="content_warning_button">@drawable/toggle_small</item>
<item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_dark</item> <item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_dark</item>
<item name="media_preview_unloaded_background_color">@color/media_preview_unloaded_background_dark</item>
<item name="status_text_color_secondary">@color/status_text_secondary_dark</item> <item name="status_text_color_secondary">@color/status_text_secondary_dark</item>
<item name="status_divider_color">@color/status_divider_dark</item> <item name="media_preview_unloaded_drawable">@drawable/media_preview_unloaded_dark</item>
<item name="tab_page_margin_color">@color/tab_page_margin_dark</item> <item name="status_divider_drawable">@drawable/status_divider_dark</item>
<item name="tab_page_margin_drawable">@drawable/tab_page_margin_dark</item>
<item name="account_header_background_color">@color/account_header_background_dark</item> <item name="account_header_background_color">@color/account_header_background_dark</item>
<item name="compose_media_button_tint">@color/media_button_dark</item> <item name="compose_media_button_tint">@color/media_button_dark</item>
<item name="compose_mention_color">@color/compose_mention_dark</item> <item name="compose_mention_color">@color/compose_mention_dark</item>
@ -86,10 +86,10 @@
<item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Light</item> <item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Light</item>
<item name="content_warning_button">@drawable/toggle_small_light</item> <item name="content_warning_button">@drawable/toggle_small_light</item>
<item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_light</item> <item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_light</item>
<item name="media_preview_unloaded_background_color">@color/media_preview_unloaded_background_light</item>
<item name="status_text_color_secondary">@color/status_text_secondary_light</item> <item name="status_text_color_secondary">@color/status_text_secondary_light</item>
<item name="status_divider_color">@color/status_divider_light</item> <item name="media_preview_unloaded_drawable">@drawable/media_preview_unloaded_light</item>
<item name="tab_page_margin_color">@color/tab_page_margin_light</item> <item name="status_divider_drawable">@drawable/status_divider_light</item>
<item name="tab_page_margin_drawable">@drawable/tab_page_margin_light</item>
<item name="account_header_background_color">@color/account_header_background_light</item> <item name="account_header_background_color">@color/account_header_background_light</item>
<item name="compose_media_button_tint">@color/media_button_light</item> <item name="compose_media_button_tint">@color/media_button_light</item>
<item name="compose_mention_color">@color/compose_mention_light</item> <item name="compose_mention_color">@color/compose_mention_light</item>