Android: document details

This commit is contained in:
jendib 2014-11-22 01:09:12 +01:00
parent 2ce5749226
commit 8c5c54125f
15 changed files with 451 additions and 19 deletions

View File

@ -98,7 +98,9 @@
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="recyclerview-v7-21.0.0" level="project" />
<orderEntry type="library" exported="" name="android-easing-1.0.3" level="project" />
<orderEntry type="library" exported="" name="support-annotations-21.0.2" level="project" />
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
<orderEntry type="library" exported="" name="support-v4-21.0.2" level="project" />
<orderEntry type="library" exported="" name="android-query.0.26.8" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-21.0.2" level="project" />

View File

@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.14.2'
classpath 'com.android.tools.build:gradle:0.14.4'
}
}
apply plugin: 'com.android.application'
@ -14,7 +14,7 @@ repositories {
android {
compileSdkVersion 21
buildToolsVersion "21.1"
buildToolsVersion "21.1.1"
defaultConfig {
minSdkVersion 14
@ -27,12 +27,6 @@ android {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
dependencies {
@ -40,4 +34,5 @@ dependencies {
compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.android.support:recyclerview-v7:21.0.0'
compile 'com.loopj.android:android-async-http:1.4.6'
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
}

View File

@ -28,6 +28,11 @@
android:logo="@drawable/ic_launcher"
android:launchMode="singleTop">
</activity>
<activity
android:name=".activity.DocumentActivity"
android:label=""
android:logo="@drawable/ic_launcher">
</activity>
</application>
</manifest>

View File

@ -20,6 +20,16 @@ public class MainApplication extends Application {
JSONObject json = PreferenceUtil.getCachedJson(getApplicationContext(), PreferenceUtil.PREF_CACHED_USER_INFO_JSON);
ApplicationContext.getInstance().setUserInfo(getApplicationContext(), json);
// TODO Fullscreen preview
// TODO Downloading
// TODO Sharing
// TODO Shared status
// TODO Tags on document
// TODO Error feedback
// TODO Infinite scrolling on documents
// TODO Searching
// TODO Printing
super.onCreate();
}

View File

@ -0,0 +1,121 @@
package com.sismics.docs.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.text.format.DateFormat;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.loopj.android.http.JsonHttpResponseHandler;
import com.sismics.docs.R;
import com.sismics.docs.adapter.FilePagerAdapter;
import com.sismics.docs.model.application.ApplicationContext;
import com.sismics.docs.resource.FileResource;
import org.apache.http.Header;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
/**
* Document activity.
*
* @author bgamard
*/
public class DocumentActivity extends ActionBarActivity {
@Override
protected void onCreate(final Bundle args) {
super.onCreate(args);
// Check if logged in
if (!ApplicationContext.getInstance().isLoggedIn()) {
startActivity(new Intent(this, LoginActivity.class));
finish();
return;
}
// Handle activity context
if (getIntent() == null) {
finish();
return;
}
// Parse input document
String documentJson = getIntent().getStringExtra("document");
if (documentJson == null) {
finish();
return;
}
JSONObject document;
try {
document = new JSONObject(documentJson);
} catch (JSONException e) {
finish();
return;
}
// Setup the activity
setContentView(R.layout.document_activity);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
// Grab the document
refreshDocument(document);
}
/**
* Refresh the displayed document.
*
* @param document Document in JSON format
*/
private void refreshDocument(JSONObject document) {
String id = document.optString("id");
String title = document.optString("title");
String date = DateFormat.getDateFormat(this).format(new Date(document.optLong("create_date")));
String description = document.optString("description");
// Fill the layout
setTitle(title);
TextView createdDateTextView = (TextView) findViewById(R.id.createdDateTextView);
createdDateTextView.setText(date);
TextView languageTextView = (TextView) findViewById(R.id.languageTextView);
languageTextView.setText(document.optString("language"));
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
if (description == null || description.isEmpty()) {
descriptionTextView.setVisibility(View.GONE);
} else {
descriptionTextView.setText(description);
}
// Grab the attached files
FileResource.list(this, id, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
ViewPager fileViewPager = (ViewPager) findViewById(R.id.fileViewPager);
fileViewPager.setOffscreenPageLimit(1);
fileViewPager.setAdapter(new FilePagerAdapter(DocumentActivity.this, response.optJSONArray("files")));
findViewById(R.id.progressBar).setVisibility(View.GONE);
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@ -88,7 +88,21 @@ public class DocListAdapter extends RecyclerView.Adapter<DocListAdapter.ViewHold
}
/**
* Update the displayed documents
* Return an item at a given position.
*
* @param position Item position
* @return Item
*/
public JSONObject getItemAt(int position) {
if (documents == null) {
return null;
}
return documents.optJSONObject(position);
}
/**
* Update the displayed documents.
* @param documents Documents
*/
public void setDocuments(JSONArray documents) {

View File

@ -0,0 +1,92 @@
package com.sismics.docs.adapter;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import com.androidquery.AQuery;
import com.androidquery.callback.BitmapAjaxCallback;
import com.sismics.docs.R;
import com.sismics.docs.util.PreferenceUtil;
import org.json.JSONArray;
import org.json.JSONObject;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
import it.sephiroth.android.library.imagezoom.ImageViewTouchBase;
/**
* @author bgamard.
*/
public class FilePagerAdapter extends PagerAdapter {
/**
* Files list.
*/
private JSONArray files;
/**
* AQuery.
*/
private AQuery aq;
/**
* Context.
*/
private Context context;
/**
* Auth token used to download files.
*/
private String authToken;
public FilePagerAdapter(Context context, JSONArray files) {
this.files = files;
this.context = context;
this.authToken = PreferenceUtil.getAuthToken(context);
aq = new AQuery(context);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.file_viewpager_item, container, false);
ImageViewTouch fileImageView = (ImageViewTouch) view.findViewById(R.id.fileImageView);
ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.fileProgressBar);
JSONObject file = files.optJSONObject(position);
String fileUrl = PreferenceUtil.getServerUrl(context) + "/api/file/" + file.optString("id") + "/data?size=web";
aq.id(fileImageView)
.image(new BitmapAjaxCallback()
.url(fileUrl)
.progress(progressBar)
.animation(AQuery.FADE_IN_NETWORK)
.cookie("auth_token", authToken));
fileImageView.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);
container.addView(view, 0);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
if (files == null) {
return 0;
}
return files.length();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}

View File

@ -1,5 +1,6 @@
package com.sismics.docs.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
@ -7,11 +8,11 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.loopj.android.http.JsonHttpResponseHandler;
import com.sismics.docs.DividerItemDecoration;
import com.sismics.docs.R;
import com.sismics.docs.activity.DocumentActivity;
import com.sismics.docs.adapter.DocListAdapter;
import com.sismics.docs.listener.RecyclerItemClickListener;
import com.sismics.docs.resource.DocumentResource;
@ -30,8 +31,8 @@ public class DocListFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Initialize the view
View view = inflater.inflate(R.layout.doc_list_fragment, container, false);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.docList);
recyclerView.setHasFixedSize(true);
recyclerView.setLongClickable(true);
@ -46,7 +47,12 @@ public class DocListFragment extends Fragment {
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(getActivity(), position + " clicked", Toast.LENGTH_SHORT).show();
JSONObject document = adapter.getItemAt(position);
if (document != null) {
Intent intent = new Intent(getActivity(), DocumentActivity.class);
intent.putExtra("document", document.toString());
startActivity(intent);
}
}
}));
@ -64,6 +70,10 @@ public class DocListFragment extends Fragment {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
adapter.setDocuments(response.optJSONArray("documents"));
if (getView() != null) {
getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
}
}
});
}

View File

@ -11,7 +11,6 @@ import com.loopj.android.http.RequestParams;
* @author bgamard
*/
public class DocumentResource extends BaseResource {
/**
* GET /document/list.
*

View File

@ -0,0 +1,25 @@
package com.sismics.docs.resource;
import android.content.Context;
import com.loopj.android.http.JsonHttpResponseHandler;
/**
* Access to /file API.
*
* @author bgamard
*/
public class FileResource extends BaseResource {
/**
* GET /file/list.
*
* @param context Context
* @param documentId Document ID
* @param responseHandler Callback
*/
public static void list(Context context, String documentId, JsonHttpResponseHandler responseHandler) {
init(context);
client.get(getApiUrl(context) + "/file/list?id=" + documentId, responseHandler);
}
}

View File

@ -0,0 +1,33 @@
package com.sismics.docs.ui.view;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
import it.sephiroth.android.library.imagezoom.ImageViewTouch;
/**
* ViewPager for files.
*
* @author bgamard.
*/
public class FileViewPager extends ViewPager {
public FileViewPager(Context context) {
super(context);
}
public FileViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ImageViewTouch) {
return ((ImageViewTouch) v).canScroll(dx);
} else {
return super.canScroll(v, checkV, dx, x, y);
}
}
}

View File

@ -1,9 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/docList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<android.support.v7.widget.RecyclerView
android:id="@+id/docList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true" />
</RelativeLayout>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:background="#eee"
android:elevation="4dp">
<TextView
android:id="@+id/createdDateLabel"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/created_date"/>
<TextView
android:id="@+id/createdDateTextView"
android:layout_toRightOf="@id/createdDateLabel"
android:layout_toEndOf="@id/createdDateLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:fontFamily="sans-serif-light"
android:text="01/12/2014"/>
<TextView
android:id="@+id/languageLabel"
android:layout_marginTop="8dp"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_below="@id/createdDateLabel"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif"
android:text="@string/language"/>
<TextView
android:id="@+id/languageTextView"
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/languageLabel"
android:layout_toEndOf="@id/languageLabel"
android:layout_below="@id/createdDateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:text="fr"/>
<TextView
android:id="@+id/descriptionTextView"
android:layout_marginTop="12dp"
android:layout_below="@id/languageLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:text="Lorem ipsum dolor sit amen lorem ipsum dolor sit amen lorem ipsum dolor sit amen lorem ipsum dolor sit amen lorem ipsum dolor sit amen."/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.sismics.docs.ui.view.FileViewPager
android:id="@+id/fileViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
style="?android:progressBarStyleHorizontal"
android:layout_width="200dp"
android:layout_height="15dip"
android:id="@+id/fileProgressBar"
android:indeterminate="false"
android:layout_centerInParent="true" />
<it.sephiroth.android.library.imagezoom.ImageViewTouch
android:id="@+id/fileImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:src="@drawable/ic_launcher" />
</RelativeLayout>

View File

@ -18,6 +18,8 @@
<string name="invalid_url_title">Invalid URL</string>
<string name="invalid_url">Please check the server URL and try again</string>
<string name="crash_toast_text">A crash occurred, a report has been sent to resolve this problem</string>
<string name="created_date">Created date</string>
<string name="language">Language</string>
<!-- Validation -->
<string name="validate_error_email">Invalid email</string>