diff --git a/app/build.gradle b/app/build.gradle
index e6a515e023d8d2c082fa20dd5f73cf5b5cae6de5..4195957e08b9ecf42a748d9626b49255d05d924b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -52,7 +52,7 @@ dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
- compile ('com.squareup.retrofit:converter-simplexml:2.0.0-beta2') {
+ compile('com.squareup.retrofit:converter-simplexml:2.0.0-beta2') {
exclude group: 'xpp3', module: 'xpp3'
exclude group: 'stax', module: 'stax-api'
exclude group: 'stax', module: 'stax'
@@ -82,4 +82,13 @@ dependencies {
compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
transitive = true;
}
+ compile('com.github.afollestad:app-theme-engine:1.0.1@aar') {
+ transitive = true
+ }
+ compile('com.github.afollestad.material-dialogs:core:0.8.5.3@aar') {
+ transitive = true
+ }
+ compile('com.github.afollestad.material-dialogs:commons:0.8.5.3@aar') {
+ transitive = true
+ }
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3909174678c99d60d3fc75b9e0ceb909c5f8a041..6b1aedca895a6994b4e5c2ed887027461fcb7a19 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,11 +30,9 @@
-
+
-
+
-
+
-
+
+
diff --git a/app/src/main/java/com/commit451/gitlab/activity/AboutActivity.java b/app/src/main/java/com/commit451/gitlab/activity/AboutActivity.java
index 1120874c210a52c89fae54a303f80eed7f1c41f5..775fcf37dacb99e25411e21414c6a1d6065c966a 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/AboutActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/AboutActivity.java
@@ -56,8 +56,6 @@ public class AboutActivity extends BaseActivity {
ViewGroup mRoot;
@Bind(R.id.toolbar)
Toolbar mToolbar;
- @Bind(R.id.toolbar_title)
- TextView mToolbarTitle;
@Bind(R.id.contributors)
TextView mContributors;
@Bind(R.id.physics_layout)
@@ -118,7 +116,7 @@ public class AboutActivity extends BaseActivity {
onBackPressed();
}
});
- mToolbarTitle.setText(R.string.about);
+ mToolbar.setTitle(R.string.about);
mPhysicsLayout.getPhysics().enableFling();
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
diff --git a/app/src/main/java/com/commit451/gitlab/activity/AddIssueActivity.java b/app/src/main/java/com/commit451/gitlab/activity/AddIssueActivity.java
index 613c3846a1f4f3ae3317197a127b8df8c099cfc4..c98937630fad31944d14831ecc679d3e320157d0 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/AddIssueActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/AddIssueActivity.java
@@ -3,6 +3,7 @@ package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
@@ -13,6 +14,7 @@ import android.view.View;
import android.widget.EditText;
import android.widget.Spinner;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
import com.commit451.elasticdragdismisslayout.ElasticDragDismissFrameLayout;
import com.commit451.elasticdragdismisslayout.ElasticDragDismissListener;
import com.commit451.gitlab.LabCoatApp;
@@ -42,7 +44,13 @@ import timber.log.Timber;
/**
* Activity to input new issues, but not really a dialog at all wink wink
*/
-public class AddIssueActivity extends MorphActivity {
+public class AddIssueActivity extends MorphActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_Translucent : R.style.ActivityLight_Translucent;
+ }
private static final String KEY_PROJECT = "project";
private static final String KEY_ISSUE = "issue";
diff --git a/app/src/main/java/com/commit451/gitlab/activity/AddMilestoneActivity.java b/app/src/main/java/com/commit451/gitlab/activity/AddMilestoneActivity.java
index fd02f83d3eb4bc516c86854dd32855f1156c4366..19ae72e25e852f7641953387bdc65d09456cc3a4 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/AddMilestoneActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/AddMilestoneActivity.java
@@ -4,6 +4,7 @@ package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
@@ -14,6 +15,7 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
import com.commit451.elasticdragdismisslayout.ElasticDragDismissFrameLayout;
import com.commit451.elasticdragdismisslayout.ElasticDragDismissListener;
import com.commit451.gitlab.LabCoatApp;
@@ -23,6 +25,7 @@ import com.commit451.gitlab.api.GitLabClient;
import com.commit451.gitlab.event.MilestoneChangedEvent;
import com.commit451.gitlab.event.MilestoneCreatedEvent;
import com.commit451.gitlab.model.api.Milestone;
+import com.commit451.gitlab.util.AppThemeUtil;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import org.parceler.Parcels;
@@ -36,7 +39,13 @@ import butterknife.OnClick;
import retrofit.Callback;
import timber.log.Timber;
-public class AddMilestoneActivity extends MorphActivity {
+public class AddMilestoneActivity extends MorphActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_Translucent : R.style.ActivityLight_Translucent;
+ }
private static final String KEY_PROJECT_ID = "project_id";
private static final String KEY_MILESTONE = "milestone";
@@ -81,6 +90,7 @@ public class AddMilestoneActivity extends MorphActivity {
now.get(Calendar.MONTH),
now.get(Calendar.DAY_OF_MONTH)
);
+ dpd.setAccentColor(AppThemeUtil.resolveAccentColor(this));
dpd.show(getFragmentManager(), "date_picker");
}
diff --git a/app/src/main/java/com/commit451/gitlab/activity/BaseActivity.java b/app/src/main/java/com/commit451/gitlab/activity/BaseActivity.java
index 7c45a1efbbb6a29f0de7007750dc5488d2df5805..64bb6c77ddaaed0f0bf60428157672c790c66ba6 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/BaseActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/BaseActivity.java
@@ -1,15 +1,25 @@
package com.commit451.gitlab.activity;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
+import com.afollestad.appthemeengine.ATEActivity;
import com.commit451.gitlab.R;
/**
* Created by Jawn on 7/27/2015.
*/
-public class BaseActivity extends AppCompatActivity {
+public class BaseActivity extends ATEActivity {
+
+ @Nullable
+ @Override
+ public final String getATEKey() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ "dark_theme" : "light_theme";
+ }
public boolean hasEmptyFields(TextInputLayout... textInputLayouts) {
boolean hasEmptyField = false;
diff --git a/app/src/main/java/com/commit451/gitlab/activity/GitlabActivity.java b/app/src/main/java/com/commit451/gitlab/activity/GitlabActivity.java
index 09e2ffcdedd4f462f31edbc1b558029d4ba1c838..3681583ca7c4b611e7ade6a9dba73e2133c25287 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/GitlabActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/GitlabActivity.java
@@ -6,6 +6,7 @@ import android.os.Bundle;
import com.commit451.gitlab.BuildConfig;
import com.commit451.gitlab.data.Prefs;
import com.commit451.gitlab.model.Account;
+import com.commit451.gitlab.util.AppThemeUtil;
import com.commit451.gitlab.ssl.CustomKeyManager;
import com.commit451.gitlab.util.NavigationManager;
@@ -21,6 +22,7 @@ public class GitlabActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+ AppThemeUtil.setupDefaultConfigs(this);
super.onCreate(savedInstanceState);
int savedVersion = Prefs.getSavedVersion(this);
diff --git a/app/src/main/java/com/commit451/gitlab/activity/GroupActivity.java b/app/src/main/java/com/commit451/gitlab/activity/GroupActivity.java
index 55b85f645e7b20f77b6fc79981d3a78874d06f78..b01538c7b3cd19eb0d33cb300c361f43d9c73940 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/GroupActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/GroupActivity.java
@@ -8,6 +8,7 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.Snackbar;
@@ -18,6 +19,9 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
+import com.afollestad.appthemeengine.Config;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
+import com.afollestad.appthemeengine.util.ATEUtil;
import com.commit451.easel.Easel;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.GroupPagerAdapter;
@@ -26,6 +30,7 @@ import com.commit451.gitlab.api.GitLabClient;
import com.commit451.gitlab.model.api.Group;
import com.commit451.gitlab.model.api.GroupDetail;
import com.commit451.gitlab.transformation.PaletteTransformation;
+import com.commit451.gitlab.util.AppThemeUtil;
import org.parceler.Parcels;
@@ -37,7 +42,13 @@ import timber.log.Timber;
/**
* See the things about the group
*/
-public class GroupActivity extends BaseActivity {
+public class GroupActivity extends BaseActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_Group : R.style.ActivityLight_Group;
+ }
private static final String KEY_GROUP = "key_group";
private static final String KEY_GROUP_ID = "key_group_id";
@@ -80,6 +91,12 @@ public class GroupActivity extends BaseActivity {
setContentView(R.layout.activity_group);
ButterKnife.bind(this);
+ // Default content and scrim colors
+ mCollapsingToolbarLayout.setContentScrimColor(
+ Config.primaryColor(this, AppThemeUtil.resolveThemeKey(this)));
+ mCollapsingToolbarLayout.setStatusBarScrimColor(
+ Config.primaryColorDark(this, AppThemeUtil.resolveThemeKey(this)));
+
mToolbar.setNavigationIcon(R.drawable.ic_back_24dp);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
@@ -122,8 +139,8 @@ public class GroupActivity extends BaseActivity {
private void bindPalette(Palette palette) {
int animationTime = 1000;
- int vibrantColor = palette.getVibrantColor(Easel.getThemeAttrColor(this, R.attr.colorPrimary));
- int darkerColor = Easel.getDarkerColor(vibrantColor);
+ int vibrantColor = palette.getVibrantColor(AppThemeUtil.resolvePrimaryColor(this));
+ int darkerColor = ATEUtil.darkenColor(vibrantColor);
if (Build.VERSION.SDK_INT >= 21) {
Easel.getNavigationBarColorAnimator(getWindow(), darkerColor)
diff --git a/app/src/main/java/com/commit451/gitlab/activity/GroupsActivity.java b/app/src/main/java/com/commit451/gitlab/activity/GroupsActivity.java
index 09f9bb5b723aa33e5d24e53c572f4ea6fd1a1d5a..6b27764c812ffd24e55133b68ac0471a0db71174 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/GroupsActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/GroupsActivity.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
@@ -15,6 +16,7 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.GroupAdapter;
import com.commit451.gitlab.api.EasyCallback;
@@ -35,7 +37,13 @@ import timber.log.Timber;
* Displays the groups of the current user
* Created by Jawn on 10/4/2015.
*/
-public class GroupsActivity extends BaseActivity {
+public class GroupsActivity extends BaseActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_Groups : R.style.ActivityLight_Groups;
+ }
public static Intent newInstance(Context context) {
Intent intent = new Intent(context, GroupsActivity.class);
diff --git a/app/src/main/java/com/commit451/gitlab/activity/LoginActivity.java b/app/src/main/java/com/commit451/gitlab/activity/LoginActivity.java
index f44ce449a23fad083fa1918b0679526d8a8247b6..383afd82788b60b2c505dbb737e6f7a87ecb09a1 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/LoginActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/LoginActivity.java
@@ -1,6 +1,7 @@
package com.commit451.gitlab.activity;
import android.Manifest;
+import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -16,9 +17,13 @@ import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.AppCompatAutoCompleteTextView;
+import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
+import android.util.Patterns;
import android.view.KeyEvent;
import android.view.View;
+import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.commit451.gitlab.LabCoatApp;
@@ -43,8 +48,12 @@ import com.squareup.okhttp.HttpUrl;
import java.net.ConnectException;
import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.regex.Pattern;
import javax.net.ssl.SSLHandshakeException;
@@ -72,7 +81,7 @@ public class LoginActivity extends BaseActivity {
@Bind(R.id.url_hint) TextInputLayout mUrlHint;
@Bind(R.id.url_input) TextView mUrlInput;
@Bind(R.id.user_input_hint) TextInputLayout mUserHint;
- @Bind(R.id.user_input) EmailAutoCompleteTextView mUserInput;
+ @Bind(R.id.user_input) AppCompatAutoCompleteTextView mUserInput;
@Bind(R.id.password_hint) TextInputLayout mPasswordHint;
@Bind(R.id.password_input) TextView mPasswordInput;
@Bind(R.id.token_hint) TextInputLayout mTokenHint;
@@ -283,7 +292,7 @@ public class LoginActivity extends BaseActivity {
@TargetApi(23)
private void checkAccountPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED) {
- mUserInput.retrieveAccounts();
+ retrieveAccounts();
} else {
requestPermissions(new String[]{Manifest.permission.GET_ACCOUNTS}, PERMISSION_REQUEST_GET_ACCOUNTS);
}
@@ -294,7 +303,7 @@ public class LoginActivity extends BaseActivity {
switch (requestCode) {
case PERMISSION_REQUEST_GET_ACCOUNTS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- mUserInput.retrieveAccounts();
+ retrieveAccounts();
}
}
}
@@ -467,4 +476,34 @@ public class LoginActivity extends BaseActivity {
}
return false;
}
+
+ /**
+ * Manually retrieve the accounts, typically used for API 23+ after getting the permission. Called automatically
+ * on creation, but needs to be recalled if the permission is granted later
+ */
+ public void retrieveAccounts() {
+ Collection accounts = getEmailAccounts();
+ if (accounts != null && !accounts.isEmpty()) {
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.support_simple_spinner_dropdown_item,
+ new ArrayList<>(accounts));
+ mUserInput.setAdapter(adapter);
+ }
+ }
+
+ /**
+ * Get all the accounts that appear to be email accounts. HashSet so that we do not get duplicates
+ * @return list of email accounts
+ */
+ private Set getEmailAccounts() {
+ HashSet emailAccounts = new HashSet<>();
+ AccountManager manager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
+ final android.accounts.Account[] accounts = manager.getAccounts();
+ for (android.accounts.Account account : accounts) {
+ if (!TextUtils.isEmpty(account.name) && Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
+ emailAccounts.add(account.name);
+ }
+ }
+ return emailAccounts;
+ }
}
diff --git a/app/src/main/java/com/commit451/gitlab/activity/MilestoneActivity.java b/app/src/main/java/com/commit451/gitlab/activity/MilestoneActivity.java
index 2b4e934ba0e1fc9ead3e4ca15897b996da75d422..07cccd98f372a9d92213a52bb11093a9123afeb0 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/MilestoneActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/MilestoneActivity.java
@@ -17,6 +17,7 @@ import android.widget.TextView;
import com.commit451.gitlab.LabCoatApp;
import com.commit451.gitlab.R;
+import com.commit451.gitlab.adapter.DividerItemDecoration;
import com.commit451.gitlab.adapter.MilestoneIssuesAdapter;
import com.commit451.gitlab.api.EasyCallback;
import com.commit451.gitlab.api.GitLabClient;
@@ -201,6 +202,7 @@ public class MilestoneActivity extends BaseActivity {
mIssuesRecyclerView.setAdapter(mMilestoneIssuesAdapter);
mIssuesLayoutManager = new LinearLayoutManager(this);
mIssuesRecyclerView.setLayoutManager(mIssuesLayoutManager);
+ mIssuesRecyclerView.addItemDecoration(new DividerItemDecoration(this));
mIssuesRecyclerView.addOnScrollListener(mOnScrollListener);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
diff --git a/app/src/main/java/com/commit451/gitlab/activity/MorphActivity.java b/app/src/main/java/com/commit451/gitlab/activity/MorphActivity.java
index 2941f844d5c59998a63b85ba814939c0788decfd..4794354d175ed4b93c9e1d404848625faf94dbef 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/MorphActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/MorphActivity.java
@@ -10,10 +10,12 @@ import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.afollestad.appthemeengine.Config;
import com.commit451.easel.Easel;
import com.commit451.gitlab.R;
import com.commit451.gitlab.transition.MorphDialogToFab;
import com.commit451.gitlab.transition.MorphFabToDialog;
+import com.commit451.gitlab.util.AppThemeUtil;
/**
* Activity that morphs from a FAB. Make sure the view you want to morph has the view id R.id.mRoot and
@@ -27,7 +29,7 @@ public class MorphActivity extends BaseActivity {
throw new IllegalStateException("Cannot pass an empty view");
}
if (Build.VERSION.SDK_INT >= 21) {
- int fabColor = Easel.getThemeAttrColor(this, R.attr.colorAccent);
+ int fabColor = Config.accentColor(this, AppThemeUtil.resolveThemeKey(this));
int dialogColor = Easel.getThemeAttrColor(this, android.R.attr.windowBackground);
setupSharedElementTransitionsFab(this, root,
fabColor,
diff --git a/app/src/main/java/com/commit451/gitlab/activity/ProjectActivity.java b/app/src/main/java/com/commit451/gitlab/activity/ProjectActivity.java
index b77932e4ca71b54c544e00b57ccce51dee394a3d..e25536d505567cde97ab6767820ca6bf5031dc5e 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/ProjectActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/ProjectActivity.java
@@ -20,6 +20,7 @@ import android.widget.TextView;
import com.commit451.gitlab.LabCoatApp;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.SectionsPagerAdapter;
+import com.commit451.gitlab.adapter.ThemedArrayAdapter;
import com.commit451.gitlab.animation.HideRunnable;
import com.commit451.gitlab.api.EasyCallback;
import com.commit451.gitlab.api.GitLabClient;
@@ -121,7 +122,7 @@ public class ProjectActivity extends BaseActivity {
mBranchSpinner.setAlpha(0.0f);
mBranchSpinner.animate().alpha(1.0f);
// Set up the dropdown list navigation in the action bar.
- mBranchSpinner.setAdapter(new ArrayAdapter<>(ProjectActivity.this, android.R.layout.simple_list_item_1, android.R.id.text1, response));
+ mBranchSpinner.setAdapter(new ThemedArrayAdapter<>(ProjectActivity.this, android.R.layout.simple_list_item_1, android.R.id.text1, response));
}
for (int i = 0; i < response.size(); i++) {
if (response.get(i).getName().equals(mProject.getDefaultBranch())) {
diff --git a/app/src/main/java/com/commit451/gitlab/activity/ProjectsActivity.java b/app/src/main/java/com/commit451/gitlab/activity/ProjectsActivity.java
index a6812207c74ec7fcda9ff6b5cd642579ed89a970..e007fef1c111081dfe25a9e29b64c957d5ab49ab 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/ProjectsActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/ProjectsActivity.java
@@ -3,6 +3,7 @@ package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.view.GravityCompat;
@@ -12,6 +13,7 @@ import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
import com.commit451.gitlab.LabCoatApp;
import com.commit451.gitlab.R;
import com.commit451.gitlab.adapter.ProjectsPagerAdapter;
@@ -26,7 +28,13 @@ import butterknife.ButterKnife;
* Shows the projects
* Created by Jawn on 9/21/2015.
*/
-public class ProjectsActivity extends BaseActivity {
+public class ProjectsActivity extends BaseActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_Projects : R.style.ActivityLight_Projects;
+ }
public static Intent newInstance(Context context) {
Intent intent = new Intent(context, ProjectsActivity.class);
diff --git a/app/src/main/java/com/commit451/gitlab/activity/SettingsActivity.java b/app/src/main/java/com/commit451/gitlab/activity/SettingsActivity.java
index 098b75560c075195a685a08020e00f2da8efd434..b2fc337e1fec7f56214e665da7f008a6d8d164ea 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/SettingsActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/SettingsActivity.java
@@ -2,27 +2,131 @@ package com.commit451.gitlab.activity;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Color;
import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.widget.TextView;
+import com.afollestad.appthemeengine.ATE;
+import com.afollestad.appthemeengine.Config;
+import com.afollestad.appthemeengine.prefs.ATEColorPreference;
+import com.afollestad.materialdialogs.color.ColorChooserDialog;
import com.commit451.gitlab.R;
+import butterknife.Bind;
import butterknife.ButterKnife;
/**
* Settings screens are fun!
* Created by John on 11/10/15.
*/
-public class SettingsActivity extends BaseActivity {
+public class SettingsActivity extends BaseActivity
+ implements ColorChooserDialog.ColorCallback {
public static Intent newInstance(Context context) {
Intent intent = new Intent(context, SettingsActivity.class);
return intent;
}
+ @Bind(R.id.toolbar) Toolbar toolbar;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
ButterKnife.bind(this);
+ toolbar.setNavigationIcon(R.drawable.ic_back_24dp);
+ toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onBackPressed();
+ }
+ });
+ toolbar.setTitle(R.string.settings);
+
+ if (savedInstanceState == null) {
+ getFragmentManager().beginTransaction().replace(R.id.settings_container, new SettingsFragment()).commit();
+ } else {
+ SettingsFragment frag = (SettingsFragment) getFragmentManager().findFragmentById(R.id.settings_container);
+ if (frag != null) frag.invalidateSettings();
+ }
+ }
+
+ @Override
+ public void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor) {
+ final Config config = ATE.config(this, getATEKey());
+ switch (dialog.getTitle()) {
+ case R.string.primary_color:
+ config.primaryColor(selectedColor);
+ break;
+ case R.string.accent_color:
+ config.accentColor(selectedColor);
+ break;
+ }
+ config.commit();
+ recreate(); // recreation needed to reach the checkboxes in the preferences layout
+ }
+
+ public static class SettingsFragment extends PreferenceFragment {
+
+ String mAteKey;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ invalidateSettings();
+ }
+
+ public void invalidateSettings() {
+ mAteKey = ((SettingsActivity) getActivity()).getATEKey();
+
+ ATEColorPreference primaryColorPref = (ATEColorPreference) findPreference("primary_color");
+ primaryColorPref.setColor(Config.primaryColor(getActivity(), mAteKey), Color.BLACK);
+ primaryColorPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ new ColorChooserDialog.Builder((SettingsActivity) getActivity(), R.string.primary_color)
+ .preselect(Config.primaryColor(getActivity(), mAteKey))
+ .show();
+ return true;
+ }
+ });
+
+ ATEColorPreference accentColorPref = (ATEColorPreference) findPreference("accent_color");
+ accentColorPref.setColor(Config.accentColor(getActivity(), mAteKey), Color.BLACK);
+ accentColorPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ new ColorChooserDialog.Builder((SettingsActivity) getActivity(), R.string.accent_color)
+ .preselect(Config.accentColor(getActivity(), mAteKey))
+ .show();
+ return true;
+ }
+ });
+
+ findPreference("dark_theme").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ // Marks both theme configs as changed so MainActivity restarts itself on return
+ Config.markChanged(getActivity(), "light_theme");
+ Config.markChanged(getActivity(), "dark_theme");
+ // The dark_theme preference value gets saved by Android in the default PreferenceManager.
+ // It's used in getATEKey() of both the Activities.
+ getActivity().recreate();
+ return true;
+ }
+ });
+ }
}
}
diff --git a/app/src/main/java/com/commit451/gitlab/activity/UserActivity.java b/app/src/main/java/com/commit451/gitlab/activity/UserActivity.java
index c0702e7c12397ed03e118faa994796905cc81fd8..9012685a30823e53cf976fbc0eff3b86f26a9928 100644
--- a/app/src/main/java/com/commit451/gitlab/activity/UserActivity.java
+++ b/app/src/main/java/com/commit451/gitlab/activity/UserActivity.java
@@ -9,6 +9,7 @@ import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.graphics.Palette;
@@ -16,11 +17,15 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
+import com.afollestad.appthemeengine.Config;
+import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer;
+import com.afollestad.appthemeengine.util.ATEUtil;
import com.commit451.easel.Easel;
import com.commit451.gitlab.R;
import com.commit451.gitlab.api.GitLabClient;
import com.commit451.gitlab.fragment.FeedFragment;
import com.commit451.gitlab.model.api.UserBasic;
+import com.commit451.gitlab.util.AppThemeUtil;
import com.commit451.gitlab.transformation.PaletteTransformation;
import com.commit451.gitlab.util.ImageUtil;
@@ -33,7 +38,13 @@ import butterknife.ButterKnife;
* User activity, which shows the user!
* Created by Jawn on 9/21/2015.
*/
-public class UserActivity extends BaseActivity {
+public class UserActivity extends BaseActivity implements ATEActivityThemeCustomizer {
+
+ @Override
+ public int getActivityTheme() {
+ return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", true) ?
+ R.style.Activity_User : R.style.ActivityLight_User;
+ }
private static final String KEY_USER = "user";
@@ -55,6 +66,15 @@ public class UserActivity extends BaseActivity {
setContentView(R.layout.activity_user);
ButterKnife.bind(this);
mUser = Parcels.unwrap(getIntent().getParcelableExtra(KEY_USER));
+
+ // Default content and scrim colors
+ mCollapsingToolbarLayout.setContentScrimColor(
+ Config.primaryColor(this, AppThemeUtil.resolveThemeKey(this)));
+ mCollapsingToolbarLayout.setStatusBarScrimColor(
+ Config.primaryColorDark(this, AppThemeUtil.resolveThemeKey(this)));
+ mCollapsingToolbarLayout.setCollapsedTitleTextColor(Color.WHITE);
+ mCollapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);
+
mToolbar.setNavigationIcon(R.drawable.ic_back_24dp);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
@@ -91,8 +111,8 @@ public class UserActivity extends BaseActivity {
private void bindPalette(Palette palette) {
int animationTime = 1000;
- int vibrantColor = palette.getVibrantColor(Easel.getThemeAttrColor(this, R.attr.colorPrimary));
- int darkerColor = Easel.getDarkerColor(vibrantColor);
+ int vibrantColor = palette.getVibrantColor(AppThemeUtil.resolvePrimaryColor(this));
+ int darkerColor = ATEUtil.darkenColor(vibrantColor);
if (Build.VERSION.SDK_INT >= 21) {
Easel.getNavigationBarColorAnimator(getWindow(), darkerColor)
diff --git a/app/src/main/java/com/commit451/gitlab/adapter/DividerItemDecoration.java b/app/src/main/java/com/commit451/gitlab/adapter/DividerItemDecoration.java
new file mode 100644
index 0000000000000000000000000000000000000000..762dde0c84c5ec59306a7fabd9f63c266396750f
--- /dev/null
+++ b/app/src/main/java/com/commit451/gitlab/adapter/DividerItemDecoration.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.commit451.gitlab.adapter;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.commit451.gitlab.R;
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+ private static final int[] ATTRS = new int[]{
+ android.R.attr.listDivider
+ };
+ private Drawable mDivider;
+ private int mDividerHeight;
+
+ private boolean mShowFirstDivider = true;
+ private boolean mShowLastDivider = false;
+
+ public DividerItemDecoration(Context context) {
+ final TypedArray a = context.obtainStyledAttributes(ATTRS);
+ mDivider = a.getDrawable(0);
+ a.recycle();
+ mDividerHeight = context.getResources().getDimensionPixelSize(R.dimen.divider_height);
+ }
+
+ public DividerItemDecoration(Context context, boolean first) {
+ this(context);
+ mShowFirstDivider = first;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ drawVertical(c, parent);
+ }
+
+ public void drawVertical(Canvas c, RecyclerView parent) {
+ final int left = parent.getPaddingLeft();
+ final int right = parent.getWidth() - parent.getPaddingRight();
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ if (isSkipped(child, parent)) continue;
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int top = child.getBottom() + params.bottomMargin;
+ final int bottom = top + mDividerHeight;
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ if (isSkipped(view, parent)) return;
+ outRect.set(0, 0, 0, mDividerHeight);
+ }
+
+ private boolean isSkipped(View child, RecyclerView parent) {
+ int position = parent.getChildAdapterPosition(child);
+ // Skip the first divider if needed
+ if (!mShowFirstDivider && position == 0) return true;
+ // Skip the last divider if needed
+ if (!mShowLastDivider && position == parent.getAdapter().getItemCount() - 1) return true;
+ // Otherwise don't skip
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/commit451/gitlab/adapter/ThemedArrayAdapter.java b/app/src/main/java/com/commit451/gitlab/adapter/ThemedArrayAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..b04c3a57a1c5e8b1d4cae4c3d6f490aa0b944eba
--- /dev/null
+++ b/app/src/main/java/com/commit451/gitlab/adapter/ThemedArrayAdapter.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.commit451.gitlab.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.IdRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.v7.view.ContextThemeWrapper;
+import android.support.v7.widget.ThemedSpinnerAdapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.TextView;
+
+import com.afollestad.appthemeengine.Config;
+import com.afollestad.appthemeengine.util.ATEUtil;
+import com.commit451.gitlab.util.AppThemeUtil;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An Adapter derived from ArrayAdapter that also allows theming with AppThemeEngine
+ */
+public class ThemedArrayAdapter extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
+ /**
+ * Lock used to modify the content of {@link #mObjects}. Any write operation
+ * performed on the array should be synchronized on this lock. This lock is also
+ * used by the filter (see {@link #getFilter()} to make a synchronized copy of
+ * the original array of data.
+ */
+ private final Object mLock = new Object();
+
+ private final LayoutInflater mInflater;
+
+ /**
+ * Contains the list of objects that represent the data of this ThemedArrayAdapter.
+ * The content of this list is referred to as "the array" in the documentation.
+ */
+ private List mObjects;
+
+ /**
+ * The resource indicating what views to inflate to display the content of this
+ * array adapter.
+ */
+ private int mResource;
+
+ /**
+ * The resource indicating what views to inflate to display the content of this
+ * array adapter in a drop down widget.
+ */
+ private int mDropDownResource;
+
+ /**
+ * If the inflated resource is not a TextView, {@link #mFieldId} is used to find
+ * a TextView inside the inflated views hierarchy. This field must contain the
+ * identifier that matches the one defined in the resource file.
+ */
+ private int mFieldId = 0;
+
+ /**
+ * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
+ * {@link #mObjects} is modified.
+ */
+ private boolean mNotifyOnChange = true;
+
+ private Context mContext;
+
+ // A copy of the original mObjects array, initialized from and then used instead as soon as
+ // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
+ private ArrayList mOriginalValues;
+ private ArrayFilter mFilter;
+
+ /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
+ private LayoutInflater mDropDownInflater;
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a TextView to use when
+ * instantiating views.
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource) {
+ this(context, resource, 0, new ArrayList());
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a layout to use when
+ * instantiating views.
+ * @param textViewResourceId The id of the TextView within the layout resource to be populated
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
+ this(context, resource, textViewResourceId, new ArrayList());
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a TextView to use when
+ * instantiating views.
+ * @param objects The objects to represent in the ListView.
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource, @NonNull T[] objects) {
+ this(context, resource, 0, Arrays.asList(objects));
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a layout to use when
+ * instantiating views.
+ * @param textViewResourceId The id of the TextView within the layout resource to be populated
+ * @param objects The objects to represent in the ListView.
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
+ @NonNull T[] objects) {
+ this(context, resource, textViewResourceId, Arrays.asList(objects));
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a TextView to use when
+ * instantiating views.
+ * @param objects The objects to represent in the ListView.
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource, @NonNull List objects) {
+ this(context, resource, 0, objects);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context The current context.
+ * @param resource The resource ID for a layout file containing a layout to use when
+ * instantiating views.
+ * @param textViewResourceId The id of the TextView within the layout resource to be populated
+ * @param objects The objects to represent in the ListView.
+ */
+ public ThemedArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
+ @NonNull List objects) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ mResource = mDropDownResource = resource;
+ mObjects = objects;
+ mFieldId = textViewResourceId;
+ }
+
+ /**
+ * Adds the specified object at the end of the array.
+ *
+ * @param object The object to add at the end of the array.
+ */
+ public void add(T object) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ mOriginalValues.add(object);
+ } else {
+ mObjects.add(object);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Adds the specified Collection at the end of the array.
+ *
+ * @param collection The Collection to add at the end of the array.
+ */
+ public void addAll(Collection extends T> collection) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ mOriginalValues.addAll(collection);
+ } else {
+ mObjects.addAll(collection);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Adds the specified items at the end of the array.
+ *
+ * @param items The items to add at the end of the array.
+ */
+ public void addAll(T ... items) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ Collections.addAll(mOriginalValues, items);
+ } else {
+ Collections.addAll(mObjects, items);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Inserts the specified object at the specified index in the array.
+ *
+ * @param object The object to insert into the array.
+ * @param index The index at which the object must be inserted.
+ */
+ public void insert(T object, int index) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ mOriginalValues.add(index, object);
+ } else {
+ mObjects.add(index, object);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Removes the specified object from the array.
+ *
+ * @param object The object to remove.
+ */
+ public void remove(T object) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ mOriginalValues.remove(object);
+ } else {
+ mObjects.remove(object);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Remove all elements from the list.
+ */
+ public void clear() {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ mOriginalValues.clear();
+ } else {
+ mObjects.clear();
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * Sorts the content of this adapter using the specified comparator.
+ *
+ * @param comparator The comparator used to sort the objects contained
+ * in this adapter.
+ */
+ public void sort(Comparator super T> comparator) {
+ synchronized (mLock) {
+ if (mOriginalValues != null) {
+ Collections.sort(mOriginalValues, comparator);
+ } else {
+ Collections.sort(mObjects, comparator);
+ }
+ }
+ if (mNotifyOnChange) notifyDataSetChanged();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ mNotifyOnChange = true;
+ }
+
+ /**
+ * Control whether methods that change the list ({@link #add},
+ * {@link #insert}, {@link #remove}, {@link #clear}) automatically call
+ * {@link #notifyDataSetChanged}. If set to false, caller must
+ * manually call notifyDataSetChanged() to have the changes
+ * reflected in the attached view.
+ *
+ * The default is true, and calling notifyDataSetChanged()
+ * resets the flag to true.
+ *
+ * @param notifyOnChange if true, modifications to the list will
+ * automatically call {@link
+ * #notifyDataSetChanged}
+ */
+ public void setNotifyOnChange(boolean notifyOnChange) {
+ mNotifyOnChange = notifyOnChange;
+ }
+
+ /**
+ * Returns the context associated with this array adapter. The context is used
+ * to create views from the resource passed to the constructor.
+ *
+ * @return The Context associated with this adapter.
+ */
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getCount() {
+ return mObjects.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getItem(int position) {
+ return mObjects.get(position);
+ }
+
+ /**
+ * Returns the position of the specified item in the array.
+ *
+ * @param item The item to retrieve the position of.
+ *
+ * @return The position of the specified item.
+ */
+ public int getPosition(T item) {
+ return mObjects.indexOf(item);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return createViewFromResource(mInflater, position, convertView, parent, mResource, false);
+ }
+
+ private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
+ ViewGroup parent, int resource, boolean dropdown) {
+ View view;
+ TextView text;
+
+ if (convertView == null) {
+ view = inflater.inflate(resource, parent, false);
+ } else {
+ view = convertView;
+ }
+
+ try {
+ if (mFieldId == 0) {
+ // If no custom field is assigned, assume the whole resource is a TextView
+ text = (TextView) view;
+ } else {
+ // Otherwise, find the TextView field within the layout
+ text = (TextView) view.findViewById(mFieldId);
+ }
+ } catch (ClassCastException e) {
+ Log.e("ThemedArrayAdapter", "You must supply a resource ID for a TextView");
+ throw new IllegalStateException(
+ "ThemedArrayAdapter requires the resource ID to be a TextView", e);
+ }
+
+ // Theme the TextView with AppThemeEngine values
+ if (dropdown) {
+ text.setTextColor(Config.textColorPrimary(view.getContext(),
+ AppThemeUtil.resolveThemeKey(view.getContext())));
+ } else {
+ text.setTextColor(ATEUtil.isColorLight(Config.primaryColor(view.getContext(),
+ AppThemeUtil.resolveThemeKey(view.getContext()))) ? Color.BLACK : Color.WHITE);
+ }
+
+ T item = getItem(position);
+ if (item instanceof CharSequence) {
+ text.setText((CharSequence)item);
+ } else {
+ text.setText(item.toString());
+ }
+
+ return view;
+ }
+
+ /**
+ *
Sets the layout resource to create the drop down views.
+ *
+ * @param resource the layout resource defining the drop down views
+ * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
+ */
+ public void setDropDownViewResource(@LayoutRes int resource) {
+ this.mDropDownResource = resource;
+ }
+
+ /**
+ * Sets the {@link Resources.Theme} against which drop-down views are
+ * inflated.
+ *
+ * By default, drop-down views are inflated against the theme of the
+ * {@link Context} passed to the adapter's constructor.
+ *
+ * @param theme the theme against which to inflate drop-down views or
+ * {@code null} to use the theme from the adapter's context
+ * @see #getDropDownView(int, View, ViewGroup)
+ */
+ @Override
+ public void setDropDownViewTheme(Resources.Theme theme) {
+ if (theme == null) {
+ mDropDownInflater = null;
+ } else if (theme == mInflater.getContext().getTheme()) {
+ mDropDownInflater = mInflater;
+ } else {
+ final Context context = new ContextThemeWrapper(mContext, theme);
+ mDropDownInflater = LayoutInflater.from(context);
+ }
+ }
+
+ @Override
+ public Resources.Theme getDropDownViewTheme() {
+ return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
+ return createViewFromResource(inflater, position, convertView, parent, mDropDownResource, true);
+ }
+
+ /**
+ * Creates a new ThemedArrayAdapter from external resources. The content of the array is
+ * obtained through {@link android.content.res.Resources#getTextArray(int)}.
+ *
+ * @param context The application's environment.
+ * @param textArrayResId The identifier of the array to use as the data source.
+ * @param textViewResId The identifier of the layout used to create views.
+ *
+ * @return An ThemedArrayAdapter.
+ */
+ public static ThemedArrayAdapter createFromResource(Context context,
+ @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
+ CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
+ return new ThemedArrayAdapter(context, textViewResId, strings);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Filter getFilter() {
+ if (mFilter == null) {
+ mFilter = new ArrayFilter();
+ }
+ return mFilter;
+ }
+
+ /**
+ *
An array filter constrains the content of the array adapter with
+ * a prefix. Each item that does not start with the supplied prefix
+ * is removed from the list.