Skip to content
Snippets Groups Projects
Commit fc066499 authored by Benjamin Dengler's avatar Benjamin Dengler
Browse files

Initial commit.

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 1749 additions and 0 deletions
/.gradle
/.idea
/build
local.properties
GitLabAndroid.iml
.DS_Store
# GitLab for Android [![Google Play](http://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.bd.gitlab)
This is the source code for the unofficial GitLab Android app.
Please see the [issues](https://github.com/ekx/GitLabAndroid/issues) section to
report any bugs or feature requests and to see the list of known issues.
## License
* [Apache Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
## Building
To build the project create a file called 'signing.gradle' in the 'app' directory. It should contain your signing information as below.
```
android {
signingConfigs {
release {
storeFile file("..")
storePassword ".."
keyAlias ".."
keyPassword ".."
}
}
}
```
Then execute 'gradlew assembleRelease' in the project's root directory. If everthing went fine, the APK file can be found in 'pathtoproject/app/build/outputs/apk'
## Acknowledgements
This project uses many open source libraries such as:
* [Retrofit](https://github.com/square/retrofit)
* [Picasso](https://github.com/square/picasso)
* [Butter Knife](https://github.com/JakeWharton/butterknife)
* [Gson](https://code.google.com/p/google-gson/)
* [joda-time-android](https://github.com/dlew/joda-time-android)
* [ActionBar-PullToRefresh](https://github.com/chrisbanes/ActionBar-PullToRefresh)
* [Crouton](https://github.com/keyboardsurfer/Crouton)
## Contributing
Please fork this repository and contribute back using
[pull requests](https://github.com/ekx/GitLabAndroid/pulls).
\ No newline at end of file
/build
app-release.apk
app.iml
signing.gradle
release.keystore
\ No newline at end of file
import com.android.build.gradle.tasks.PackageApplication
apply plugin: 'android'
apply from: 'signing.gradle'
android {
compileSdkVersion 19
buildToolsVersion '19.1.0'
defaultConfig {
minSdkVersion 14
targetSdkVersion 19
versionCode 36
versionName "1.5.6"
}
buildTypes {
release {
signingConfig signingConfigs.release
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
debuggable false
jniDebugBuild false
zipAlign true
}
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
compile 'commons-io:commons-io:2.4'
compile 'commons-lang:commons-lang:2.6'
compile 'commons-codec:commons-codec:1.3'
compile 'com.google.code.gson:gson:2.2.4'
compile 'net.danlew:android.joda:2.3.2'
compile 'com.squareup.picasso:picasso:2.3.2'
compile 'com.squareup.retrofit:retrofit:1.6.0'
compile 'com.jakewharton:butterknife:5.1.1'
compile 'com.github.chrisbanes.actionbarpulltorefresh:library:0.9.3'
compile 'de.keyboardsurfer.android.widget:crouton:1.8.4'
compile 'com.android.support:support-v4:19.1.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
}
task copyNativeLibs(type: Copy) {
from(new File(getProjectDir(), 'native-libs')) { include '**/*.so' }
into new File(buildDir, 'native-libs')
}
tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(PackageApplication) { pkgTask ->
pkgTask.jniFolders = new HashSet<File>()
pkgTask.jniFolders.add(new File(buildDir, 'native-libs'))
}
\ No newline at end of file
File added
File added
File added
File added
File added
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:/Program Files/Android-Studio/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-keep class * extends java.util.ListResourceBundle {
protected Object[][] getContents();
}
-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
public static final *** NULL;
}
-keepnames @com.google.android.gms.common.annotation.KeepName class *
-keepclassmembernames class * {
@com.google.android.gms.common.annotation.KeepName *;
}
-keepnames class * implements android.os.Parcelable {
public static final ** CREATOR;
}
-dontwarn com.squareup.okhttp.**
-keep class retrofit.** { *; }
-dontwarn retrofit.**
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bd.gitlab" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.android.vending.BILLING" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/CustomTheme" >
<activity
android:name="com.bd.gitlab.MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.bd.gitlab.LoginActivity"
android:label="@string/login_activity"
android:screenOrientation="portrait"
android:theme="@style/NoActionBar" />
<activity
android:name="com.bd.gitlab.FileActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName="com.bd.gitlab.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.bd.gitlab.MainActivity" />
</activity>
<activity
android:name="com.bd.gitlab.IssueActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName="com.bd.gitlab.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.bd.gitlab.MainActivity" />
</activity>
<activity
android:name="com.bd.gitlab.DiffActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName="com.bd.gitlab.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.bd.gitlab.MainActivity" />
</activity>
</application>
</manifest>
\ No newline at end of file
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code {
display: block; padding: 0.5em;
color: #333;
background: #FFFFFF
}
pre .comment,
pre .template_comment,
pre .diff .header,
pre .javadoc {
color: #998;
font-style: italic
}
pre .keyword,
pre .css .rule .keyword,
pre .winutils,
pre .javascript .title,
pre .nginx .title,
pre .subst,
pre .request,
pre .status {
color: #333;
font-weight: bold
}
pre .number,
pre .hexcolor,
pre .ruby .constant {
color: #099;
}
pre .string,
pre .tag .value,
pre .phpdoc,
pre .tex .formula {
color: #d14
}
pre .title,
pre .id {
color: #900;
font-weight: bold
}
pre .javascript .title,
pre .lisp .title,
pre .clojure .title,
pre .subst {
font-weight: normal
}
pre .class .title,
pre .haskell .type,
pre .vhdl .literal,
pre .tex .command {
color: #458;
font-weight: bold
}
pre .tag,
pre .tag .title,
pre .rules .property,
pre .django .tag .keyword {
color: #000080;
font-weight: normal
}
pre .attribute,
pre .variable,
pre .lisp .body {
color: #008080
}
pre .regexp {
color: #009926
}
pre .class {
color: #458;
font-weight: bold
}
pre .symbol,
pre .ruby .symbol .string,
pre .lisp .keyword,
pre .tex .special,
pre .prompt {
color: #990073
}
pre .built_in,
pre .lisp .title,
pre .clojure .built_in {
color: #0086b3
}
pre .preprocessor,
pre .pi,
pre .doctype,
pre .shebang,
pre .cdata {
color: #999;
font-weight: bold
}
pre .deletion {
background: #fdd
}
pre .addition {
background: #dfd
}
pre .diff .change {
background: #0086b3
}
pre .chunk {
color: #aaa
}
This diff is collapsed.
package com.bd.gitlab;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.LinearLayout;
import com.bd.gitlab.model.Diff;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.tools.RetrofitHelper;
import com.bd.gitlab.views.DiffView;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
public class DiffActivity extends Activity {
@InjectView(R.id.diff_container) LinearLayout diffContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_diff);
ButterKnife.inject(this);
init();
}
@Override
public void onDestroy() {
super.onDestroy();
Crouton.cancelAllCroutons();
}
private void init() {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(Repository.selectedCommit.getShortId());
getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_actionbar));
Repository.getService().getCommitDiff(Repository.selectedProject.getId(), Repository.selectedCommit.getId(), diffCallback);
}
private Callback<List<Diff>> diffCallback = new Callback<List<Diff>>() {
@Override
public void success(List<Diff> diffs, Response resp) {
for(Diff diff : diffs) {
diffContainer.addView(new DiffView(DiffActivity.this, diff));
}
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(DiffActivity.this, e);
Crouton.makeText(DiffActivity.this, R.string.connection_error, Style.ALERT);
}
};
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
\ No newline at end of file
package com.bd.gitlab;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import butterknife.ButterKnife;
import butterknife.InjectView;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.tools.RetrofitHelper;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
public class FileActivity extends Activity {
@InjectView(R.id.file_blob) WebView fileBlobView;
private MenuItem openFile;
private MenuItem saveFile;
private byte[] fileBlob;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
ButterKnife.inject(this);
if(Repository.selectedFile != null) {
setupUI();
Repository.getService().getBlob(Repository.selectedProject.getId(), Repository.newestCommit.getId(), getIntent().getExtras().getString("path") + Repository.selectedFile.getName(), blobCallback);
}
}
@Override
public void onDestroy() {
super.onDestroy();
Crouton.cancelAllCroutons();
}
@SuppressLint("SetJavaScriptEnabled")
private void setupUI() {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(Repository.selectedFile.getName());
getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_actionbar));
fileBlobView.getSettings().setJavaScriptEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.file, menu);
openFile = menu.getItem(0);
saveFile = menu.getItem(1);
return true;
}
private void enableMenu() {
if(openFile != null) {
openFile.setEnabled(true);
openFile.setIcon(R.drawable.ic_action_open);
}
if(saveFile != null) {
saveFile.setEnabled(true);
saveFile.setIcon(R.drawable.ic_action_save);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_open:
openFile();
return true;
case R.id.action_save:
saveBlob();
return true;
}
return super.onOptionsItemSelected(item);
}
private Callback<Response> blobCallback = new Callback<Response>() {
@Override
public void success(Response response, Response resp) {
String content = getResources().getString(R.string.file_load_error);
try {
fileBlob = IOUtils.toByteArray(response.getBody().in());
content = new String(fileBlob, "UTF-8");
}
catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
String temp = "<!DOCTYPE html><html><head><link href=\"github.css\" rel=\"stylesheet\" /></head><body><pre><code>" + StringEscapeUtils.escapeHtml(content) + "</code></pre><script src=\"highlight.pack.js\"></script><script>hljs.initHighlightingOnLoad();</script></body></html>";
fileBlobView.loadDataWithBaseURL("file:///android_asset/", temp, "text/html", "utf8", null);
enableMenu();
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(FileActivity.this, e);
Crouton.makeText(FileActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private File saveBlob() {
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state) && fileBlob != null) {
File downloadFolder = new File(Environment.getExternalStorageDirectory(), "Download");
if(!downloadFolder.exists())
downloadFolder.mkdir();
File newFile = new File(downloadFolder, Repository.selectedFile.getName());
try {
FileOutputStream f = new FileOutputStream(newFile);
f.write(fileBlob);
f.close();
Crouton.makeText(this, R.string.file_saved, Style.CONFIRM).show();
return newFile;
}
catch(FileNotFoundException e) {
Crouton.makeText(this, R.string.save_error, Style.ALERT).show();
}
catch(IOException e) {
Crouton.makeText(this, R.string.save_error, Style.ALERT).show();
}
}
else
Crouton.makeText(this, R.string.save_error, Style.ALERT).show();
return null;
}
private void openFile() {
File file = saveBlob();
MimeTypeMap myMime = MimeTypeMap.getSingleton();
Intent newIntent = new Intent(Intent.ACTION_VIEW);
String mimeType = myMime.getMimeTypeFromExtension(fileExt(file.toString()).substring(1));
newIntent.setDataAndType(Uri.fromFile(file), mimeType);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
startActivity(newIntent);
}
catch(android.content.ActivityNotFoundException e) {
Crouton.makeText(this, R.string.open_error, Style.ALERT).show();
}
}
private String fileExt(String url) {
if(url.contains("?")) {
url = url.substring(0, url.indexOf("?"));
}
if(!url.contains(".")) {
return null;
}
else {
String ext = url.substring(url.lastIndexOf("."));
if(ext.contains("%")) {
ext = ext.substring(0, ext.indexOf("%"));
}
if(ext.contains("/")) {
ext = ext.substring(0, ext.indexOf("/"));
}
return ext.toLowerCase();
}
}
}
package com.bd.gitlab;
import in.uncod.android.bypass.Bypass;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import com.bd.gitlab.adapter.MilestonesAdapter;
import com.bd.gitlab.adapter.NoteAdapter;
import com.bd.gitlab.adapter.UserAdapter;
import com.bd.gitlab.model.Issue;
import com.bd.gitlab.model.Milestone;
import com.bd.gitlab.model.Note;
import com.bd.gitlab.model.User;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.tools.RetrofitHelper;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
public class IssueActivity extends Activity {
@InjectView(R.id.scroll1) ScrollView scroll;
@InjectView(R.id.title) TextView title;
@InjectView(R.id.state_spinner) Spinner stateSpinner;
@InjectView(R.id.assignee_spinner) Spinner assigneeSpinner;
@InjectView(R.id.milestone_spinner) Spinner milestoneSpinner;
@InjectView(R.id.description) TextView description;
@InjectView(R.id.note_list) ListView noteList;
@InjectView(R.id.progressbar_loading) ProgressBar progressBar;
@InjectView(R.id.new_note_edit) EditText newNoteEdit;
private ProgressDialog pd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_issue);
ButterKnife.inject(this);
if(Repository.selectedIssue != null) {
setupUI();
loadNotes();
}
else
finish();
}
@Override
public void onDestroy() {
super.onDestroy();
Crouton.cancelAllCroutons();
}
/**
* Set up the {@link android.app.ActionBar}.
*/
private void setupUI() {
long tempId = Repository.selectedIssue.getIid();
if(tempId < 1)
tempId = Repository.selectedIssue.getId();
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle("Issue #" + tempId);
getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_actionbar));
title.setText(Repository.selectedIssue.getTitle());
ArrayList<String> temp3 = new ArrayList<String>();
if(Repository.selectedIssue.getState().equals("opened")) {
temp3.add("opened");
temp3.add("closed");
}
else {
temp3.add("closed");
temp3.add("reopened");
}
stateSpinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, temp3));
stateSpinner.setSelection(temp3.indexOf(Repository.selectedIssue.getState()));
if(Repository.userAdapter != null) {
assigneeSpinner.setAdapter(Repository.userAdapter);
if(Repository.selectedIssue.getAssignee() != null)
assigneeSpinner.setSelection(Repository.userAdapter.getPosition(Repository.selectedIssue.getAssignee()), true);
}
else {
if(Repository.selectedIssue.getAssignee() != null) {
ArrayList<User> temp = new ArrayList<User>();
temp.add(Repository.selectedIssue.getAssignee());
assigneeSpinner.setAdapter(new UserAdapter(this, temp));
}
Repository.getService().getUsersFallback(Repository.selectedProject.getId(), usersCallback);
}
ArrayList<Milestone> temp2 = new ArrayList<Milestone>();
if(Repository.selectedIssue.getMilestone() != null)
temp2.add(Repository.selectedIssue.getMilestone());
milestoneSpinner.setAdapter(new MilestonesAdapter(this, temp2));
Repository.getService().getMilestones(Repository.selectedProject.getId(), milestonesCallback);
Bypass bypass = new Bypass();
String desc = Repository.selectedIssue.getDescription();
if(desc == null)
desc = "";
description.setText(bypass.markdownToSpannable(desc));
description.setMovementMethod(LinkMovementMethod.getInstance());
Repository.setListViewSize(noteList);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.issue, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_save:
save();
return true;
}
return super.onOptionsItemSelected(item);
}
private void loadNotes() {
progressBar.setVisibility(View.VISIBLE);
Repository.getService().getIssueNotes(Repository.selectedProject.getId(), Repository.selectedIssue.getId(), notesCallback);
}
private Callback<List<Note>> notesCallback = new Callback<List<Note>>() {
@Override
public void success(List<Note> notes, Response resp) {
progressBar.setVisibility(View.GONE);
NoteAdapter noteAdapter = new NoteAdapter(IssueActivity.this, notes);
noteList.setAdapter(noteAdapter);
Repository.setListViewSize(noteList);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(IssueActivity.this, e);
progressBar.setVisibility(View.GONE);
Crouton.makeText(IssueActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
@OnClick(R.id.new_note_button)
public void onNewNoteClick() {
String body = newNoteEdit.getText().toString();
if(body.length() < 1)
return;
pd = ProgressDialog.show(IssueActivity.this, "", getResources().getString(R.string.progress_dialog), true);
// Clear text & collapse keyboard
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(newNoteEdit.getWindowToken(), 0);
newNoteEdit.setText("");
Repository.getService().postIssueNote(Repository.selectedProject.getId(), Repository.selectedIssue.getId(), body, noteCallback);
}
private Callback<Note> noteCallback = new Callback<Note>() {
@Override
public void success(Note note, Response resp) {
if(pd != null && pd.isShowing())
pd.cancel();
((NoteAdapter) noteList.getAdapter()).addNote(note);
Repository.setListViewSize(noteList);
scroll.post(new Runnable() {
@Override
public void run() {
// Scroll to bottom of list
scroll.fullScroll(View.FOCUS_DOWN);
}
});
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(IssueActivity.this, e);
if(pd != null && pd.isShowing())
pd.cancel();
Crouton.makeText(IssueActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private void save() {
pd = ProgressDialog.show(IssueActivity.this, "", getResources().getString(R.string.progress_dialog), true);
String selection = stateSpinner.getSelectedItem().toString();
String value = "";
if(selection.equals("closed") && (Repository.selectedIssue.getState().equals("opened") || Repository.selectedIssue.getState().equals("reopened")))
value = "close";
if((selection.equals("reopened") || selection.equals("opened")) && Repository.selectedIssue.getState().equals("closed"))
value = "reopen";
Repository.getService().editIssue(Repository.selectedProject.getId(), Repository.selectedIssue.getId(), value, assigneeSpinner.getSelectedItemId(), milestoneSpinner.getSelectedItemId(), issueCallback);
}
private Callback<Issue> issueCallback = new Callback<Issue>() {
@Override
public void success(Issue issue, Response resp) {
if(pd != null && pd.isShowing())
pd.cancel();
Repository.selectedIssue.setState(stateSpinner.getSelectedItem().toString());
Repository.selectedIssue.setAssignee((User) assigneeSpinner.getSelectedItem());
Repository.selectedIssue.setMilestone((Milestone) milestoneSpinner.getSelectedItem());
if(Repository.issueAdapter != null)
Repository.issueAdapter.notifyDataSetChanged();
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(IssueActivity.this, e);
if(pd != null && pd.isShowing())
pd.cancel();
Crouton.makeText(IssueActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private Callback<List<User>> usersCallback = new Callback<List<User>>() {
@Override
public void success(List<User> users, Response resp) {
progressBar.setVisibility(View.GONE);
UserAdapter ua = new UserAdapter(IssueActivity.this, users);
assigneeSpinner.setAdapter(ua);
assigneeSpinner.setSelection(ua.getPosition(Repository.selectedIssue.getAssignee()), true);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(IssueActivity.this, e);
progressBar.setVisibility(View.GONE);
Crouton.makeText(IssueActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private Callback<List<Milestone>> milestonesCallback = new Callback<List<Milestone>>() {
@Override
public void success(List<Milestone> milestones, Response resp) {
progressBar.setVisibility(View.GONE);
MilestonesAdapter ma = new MilestonesAdapter(IssueActivity.this, milestones);
milestoneSpinner.setAdapter(ma);
milestoneSpinner.setSelection(ma.getPosition(Repository.selectedIssue.getMilestone()), true);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(IssueActivity.this, e);
progressBar.setVisibility(View.GONE);
Crouton.makeText(IssueActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
}
package com.bd.gitlab;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bd.gitlab.model.Project;
import com.bd.gitlab.model.Session;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.tools.RetrofitHelper;
import javax.net.ssl.SSLHandshakeException;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
public class LoginActivity extends Activity {
@InjectView(R.id.url_input) TextView urlInput;
@InjectView(R.id.user_input) TextView userInput;
@InjectView(R.id.password_input) TextView passwordInput;
@InjectView(R.id.token_input) TextView tokenInput;
@InjectView(R.id.normal_login) RelativeLayout normalLogin;
@InjectView(R.id.token_login) RelativeLayout tokenLogin;
private boolean isNormalLogin = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
}
@Override
public void onDestroy() {
super.onDestroy();
Crouton.cancelAllCroutons();
}
@OnClick(R.id.show_token_link)
public void showTokenLogin() {
normalLogin.setVisibility(View.GONE);
tokenLogin.setVisibility(View.VISIBLE);
isNormalLogin = false;
}
@OnClick(R.id.show_normal_link)
public void showNormalLogin() {
normalLogin.setVisibility(View.VISIBLE);
tokenLogin.setVisibility(View.GONE);
isNormalLogin = true;
}
@OnClick(R.id.login_button)
public void onLoginClick() {
Repository.resetService();
String url = urlInput.getText().toString();
if(url.length() == 0) {
Crouton.makeText(LoginActivity.this, R.string.login_error, Style.ALERT).show();
return;
}
else if(!url.startsWith("http://") && !url.startsWith("https://"))
urlInput.setText("http://" + urlInput.getText().toString());
if(isNormalLogin) {
connect(true);
}
else {
connect(false);
}
}
@Override
public void onBackPressed() {
moveTaskToBack(true);
}
/* --- CONNECT --- */
private ProgressDialog pd;
private void connect(boolean byAuth) {
pd = ProgressDialog.show(LoginActivity.this, "", getResources().getString(R.string.login_progress_dialog), true);
Repository.setServerUrl("");
Repository.setPrivateToken("");
Repository.setLoggedIn(false);
String serverURL = urlInput.getText().toString();
if(serverURL == null)
serverURL = "";
else
serverURL = serverURL.trim();
Repository.setServerUrl(serverURL);
if(byAuth)
connectByAuth();
else
connectByToken();
}
private void connectByAuth() {
if(userInput.getText().toString().contains("@"))
Repository.getService().getSessionByEmail(userInput.getText().toString(), passwordInput.getText().toString(), sessionCallback);
else
Repository.getService().getSessionByUsername(userInput.getText().toString(), passwordInput.getText().toString(), sessionCallback);
}
private Callback<Session> sessionCallback = new Callback<Session>() {
@Override
public void success(Session session, Response resp) {
if(pd != null && pd.isShowing())
pd.cancel();
Repository.setLoggedIn(true);
Repository.setPrivateToken(session.private_token);
Intent i = new Intent(LoginActivity.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
@Override
public void failure(RetrofitError e) {
handleConnectionError(e, true);
}
};
private void connectByToken() {
Repository.setPrivateToken(tokenInput.getText().toString());
Repository.getService().getProjects(tokenCallback);
}
private Callback<List<Project>> tokenCallback = new Callback<List<Project>>() {
@Override
public void success(List<Project> projects, Response resp) {
if(pd != null && pd.isShowing())
pd.cancel();
Repository.setLoggedIn(true);
Intent i = new Intent(LoginActivity.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
@Override
public void failure(RetrofitError e) {
handleConnectionError(e, false);
}
};
private void handleConnectionError(RetrofitError e, boolean auth) {
RetrofitHelper.printDebugInfo(null, e);
if(pd != null && pd.isShowing())
pd.cancel();
if(e.getCause() instanceof SSLHandshakeException) {
Dialog d = new AlertDialog.Builder(this)
.setTitle(R.string.certificate_title)
.setMessage(R.string.certificate_message)
.setNeutralButton(R.string.ok_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
else if(e.getResponse() != null && (e.getResponse().getStatus() == 301 || e.getResponse().getStatus() == 405) && !urlInput.getText().toString().contains("https://")) {
urlInput.setText(urlInput.getText().toString().replace("http://", "https://"));
connect(auth);
}
else
Crouton.makeText(LoginActivity.this, R.string.login_error, Style.ALERT).show();
}
}
package com.bd.gitlab;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import butterknife.ButterKnife;
import butterknife.InjectView;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import android.app.ActionBar;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.bd.gitlab.adapter.DrawerAdapter;
import com.bd.gitlab.fragments.CommitsFragment;
import com.bd.gitlab.fragments.FilesFragment;
import com.bd.gitlab.fragments.IssuesFragment;
import com.bd.gitlab.fragments.UsersFragment;
import com.bd.gitlab.model.Branch;
import com.bd.gitlab.model.Group;
import com.bd.gitlab.model.Project;
import com.bd.gitlab.model.User;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.tools.RetrofitHelper;
import net.danlew.android.joda.ResourceZoneInfoProvider;
import de.keyboardsurfer.android.widget.crouton.Configuration.Builder;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
public class MainActivity extends FragmentActivity implements ActionBar.OnNavigationListener, OnItemClickListener {
@InjectView(R.id.drawer_layout) DrawerLayout drawerLayout;
@InjectView(R.id.left_drawer) ListView drawerList;
@InjectView(R.id.pager) ViewPager viewPager;
private ActionBar actionBar;
private ActionBarDrawerToggle drawerToggle;
private boolean rotationLocked = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
actionBar = getActionBar();
actionBar.setIcon(getResources().getDrawable(R.drawable.ic_actionbar));
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
drawerList.setOnItemClickListener(this);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
drawerList.addHeaderView(inflater.inflate(R.layout.drawer_list_header, null), null, false);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.drawable.ic_navigation_drawer, R.string.drawer_open, R.string.drawer_close) {
public void onDrawerClosed(View view) {
// invalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
// invalidateOptionsMenu();
}
};
drawerLayout.setDrawerListener(drawerToggle);
// Workaround that forces the overflow menu
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
if(menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
}
catch(Exception ex) {
ex.printStackTrace();
}
ResourceZoneInfoProvider.init(this);
Repository.init(this);
if(!Repository.isLoggedIn())
startActivity(new Intent(this, LoginActivity.class));
else
connect();
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
Repository.displayWidth = size.x;
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
viewPager.setAdapter(sectionsPagerAdapter);
viewPager.setOffscreenPageLimit(3);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public void onDestroy() {
super.onDestroy();
Crouton.cancelAllCroutons();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
if(drawerLayout.isDrawerOpen(drawerList))
drawerLayout.closeDrawer(drawerList);
else
drawerLayout.openDrawer(drawerList);
return true;
case R.id.action_logout:
Repository.setLoggedIn(false);
startActivity(new Intent(this, LoginActivity.class));
return true;
case R.id.action_lock_orientation:
if(!rotationLocked) {
setRequestedOrientation(Repository.getScreenOrientation(this));
item.setTitle(R.string.action_unlock_orientation);
rotationLocked = true;
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
item.setTitle(R.string.action_lock_orientation);
rotationLocked = false;
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
Repository.selectedBranch = Repository.branches.get(itemPosition);
loadData();
return true;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(Repository.selectedProject == null || !Repository.selectedProject.equals(Repository.projects.get(position - 1))) {
Repository.selectedProject = Repository.projects.get(position - 1);
Repository.setLastProject(Repository.selectedProject.toString());
Repository.issueAdapter = null;
Repository.userAdapter = null;
Repository.drawerAdapter.notifyDataSetChanged();
Repository.getService().getBranches(Repository.selectedProject.getId(), branchesCallback);
}
if(drawerLayout.isDrawerOpen(drawerList))
drawerLayout.closeDrawer(drawerList);
}
@Override
public void onBackPressed() {
boolean handled = false;
switch(viewPager.getCurrentItem()) {
case 0:
CommitsFragment commitsFragment = (CommitsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":0");
handled = commitsFragment.onBackPressed();
break;
case 1:
IssuesFragment issuesFragment = (IssuesFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":1");
handled = issuesFragment.onBackPressed();
break;
case 2:
FilesFragment filesFragment = (FilesFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":2");
handled = filesFragment.onBackPressed();
break;
case 3:
UsersFragment usersFragment = (UsersFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":3");
handled = usersFragment.onBackPressed();
break;
}
if(!handled)
finish();
}
private void loadData() {
if(Repository.selectedProject == null)
return;
CommitsFragment commitsFragment = (CommitsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":0");
commitsFragment.loadData();
IssuesFragment issuesFragment = (IssuesFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":1");
issuesFragment.loadData();
FilesFragment filesFragment = (FilesFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":2");
filesFragment.loadData();
UsersFragment usersFragment = (UsersFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":3");
usersFragment.loadData();
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position) {
case 0:
fragment = new CommitsFragment();
break;
case 1:
fragment = new IssuesFragment();
break;
case 2:
fragment = new FilesFragment();
break;
case 3:
fragment = new UsersFragment();
break;
}
return fragment;
}
@Override
public int getCount() {
return 4;
}
@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch(position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
case 3:
return getString(R.string.title_section4).toUpperCase(l);
}
return null;
}
}
/* --- CONNECT --- */
private ProgressDialog pd;
private void connect() {
pd = ProgressDialog.show(MainActivity.this, "", getResources().getString(R.string.main_progress_dialog), true);
Repository.getService().getGroups(groupsCallback);
}
private Callback<List<Group>> groupsCallback = new Callback<List<Group>>() {
@Override
public void success(List<Group> groups, Response resp) {
Repository.groups = new ArrayList<Group>(groups);
Repository.getService().getUsers(usersCallback);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(MainActivity.this, e);
Repository.getService().getUsers(usersCallback);
}
};
private Callback<List<User>> usersCallback = new Callback<List<User>>() {
@Override
public void success(List<User> users, Response resp) {
Repository.users = new ArrayList<User>(users);
Repository.getService().getProjects(projectsCallback);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(MainActivity.this, e);
if(pd != null && pd.isShowing())
pd.cancel();
Crouton.makeText(MainActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private Callback<List<Project>> projectsCallback = new Callback<List<Project>>() {
@Override
public void success(List<Project> projects, Response resp) {
Repository.projects = new ArrayList<Project>(projects);
if(Repository.projects.size() != 0) {
if(Repository.getLastProject().length() == 0)
Repository.selectedProject = Repository.projects.get(0);
else if(Repository.projects.size() > 0) {
String lastProject = Repository.getLastProject();
for(Project p : Repository.projects) {
if(p.toString().equals(lastProject))
Repository.selectedProject = p;
}
if(Repository.selectedProject == null)
Repository.selectedProject = Repository.projects.get(0);
}
}
Repository.drawerAdapter = new DrawerAdapter(MainActivity.this, Repository.projects);
drawerList.setAdapter(Repository.drawerAdapter);
if(Repository.selectedProject != null)
Repository.getService().getBranches(Repository.selectedProject.getId(), branchesCallback);
}
@Override
public void failure(RetrofitError e) {
RetrofitHelper.printDebugInfo(MainActivity.this, e);
if(pd != null && pd.isShowing())
pd.cancel();
Crouton.makeText(MainActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
private Callback<List<Branch>> branchesCallback = new Callback<List<Branch>>() {
@Override
public void success(List<Branch> branches, Response resp) {
if(pd != null && pd.isShowing())
pd.cancel();
Repository.branches = new ArrayList<Branch>(branches);
Branch[] spinnerData = new Branch[Repository.branches.size()];
for(int i = 0; i < Repository.branches.size(); i++)
spinnerData[i] = Repository.branches.get(i);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
Context context = actionBar.getThemedContext();
if(context == null) context = MainActivity.this;
// Set up the dropdown list navigation in the action bar.
actionBar.setListNavigationCallbacks(new ArrayAdapter<Branch>(context, android.R.layout.simple_list_item_1, android.R.id.text1, spinnerData), MainActivity.this);
if(Repository.branches.size() == 0) {
Repository.selectedBranch = null;
loadData();
}
}
@Override
public void failure(RetrofitError e) {
if(pd != null && pd.isShowing())
pd.cancel();
if(e.getResponse().getStatus() == 500) {
Repository.selectedBranch = null;
loadData();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setNavigationMode(ActionBar.DISPLAY_SHOW_TITLE);
return;
}
RetrofitHelper.printDebugInfo(MainActivity.this, e);
Crouton.makeText(MainActivity.this, R.string.connection_error, Style.ALERT).show();
}
};
}
package com.bd.gitlab.adapter;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.bd.gitlab.R;
import com.bd.gitlab.model.DiffLine;
import com.bd.gitlab.tools.Repository;
import com.bd.gitlab.views.CompoundTextView;
import com.squareup.picasso.Picasso;
import fr.tkeunebr.gravatar.Gravatar;
public class CommitsAdapter extends BaseAdapter {
private ArrayList<DiffLine> commits;
private LayoutInflater inflater;
public CommitsAdapter(Context context, List<DiffLine> commits) {
this.commits = new ArrayList<DiffLine>(commits);
if(context != null) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
@Override
public int getCount() {
return commits.size();
}
@Override
public DiffLine getItem(int position) {
return commits.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) convertView = inflater.inflate(R.layout.list_item, parent, false);
final TextView title = (TextView) convertView.findViewById(R.id.title);
final CompoundTextView summary = (CompoundTextView) convertView.findViewById(R.id.summary);
final TextView custom = (TextView) convertView.findViewById(R.id.custom);
title.setText(commits.get(position).getTitle());
summary.setText(commits.get(position).getAuthorName());
custom.setText(DateUtils.getRelativeTimeSpanString(commits.get(position).getCreatedAt().getTime()));
float percent = Repository.displayWidth / 720f;
int size = (int) (40f * percent);
String url = Gravatar.init().with(commits.get(position).getAuthorEmail()).size(size).build();
Picasso.with(convertView.getContext()).load(url).into(summary);
return convertView;
}
}
package com.bd.gitlab.adapter;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.bd.gitlab.R;
import com.bd.gitlab.model.Project;
import com.bd.gitlab.model.User;
import com.bd.gitlab.tools.Repository;
public class DrawerAdapter extends BaseAdapter {
private ArrayList<Project> projects;
private LayoutInflater inflater;
public DrawerAdapter(Context context, ArrayList<Project> projects) {
this.projects = projects;
if(context != null)
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return projects.size();
}
@Override
public Project getItem(int position) {
return projects.get(position);
}
@Override
public long getItemId(int position) {
return projects.get(position).getId();
}
public int getPosition(User user) {
return projects.indexOf(user);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) convertView = inflater.inflate(R.layout.simple_list_item, parent, false);
final float scale = convertView.getResources().getDisplayMetrics().density;
convertView.setMinimumHeight((int) (48 * scale + 0.5f));
final TextView text = (TextView) convertView.findViewById(R.id.text);
text.setText(projects.get(position).toString());
if(Repository.selectedProject != null && Repository.selectedProject.equals(projects.get(position))) {
text.setTextColor(Color.WHITE);
text.setCompoundDrawablesWithIntrinsicBounds(null, null, convertView.getResources().getDrawable(R.drawable.ic_selected), null);
}
else {
text.setTextColor(convertView.getResources().getColor(android.R.color.secondary_text_dark));
text.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
return convertView;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment