Initial work on details view

This commit is contained in:
mvp76 2016-11-10 12:44:18 +01:00
parent f355232bd5
commit 34450569b3
18 changed files with 929 additions and 3 deletions

View File

@ -404,6 +404,17 @@
android:value=".FDroid" />
</activity>
<activity
android:name=".AppDetails2"
android:label="@string/app_details"
android:exported="true"
android:parentActivityName=".FDroid"
android:theme="@style/AppThemeLight.NoActionBar"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:label="@string/menu_settings"
android:name=".PreferencesActivity"

View File

@ -0,0 +1,500 @@
package org.fdroid.fdroid;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.text.AllCapsTransformationMethod;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.views.LinearLayoutManagerSnapHelper;
import java.util.ArrayList;
import static android.support.v7.widget.RecyclerView.NO_POSITION;
public class AppDetails2 extends AppCompatActivity {
private static final String TAG = "AppDetails2";
private App mApp;
private RecyclerView mRecyclerView;
private AppDetailsRecyclerViewAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_details2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(""); // Nice and clean toolbar
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
App app = null;
String packageName = getPackageNameFromIntent(getIntent());
if (!TextUtils.isEmpty(packageName)) {
app = AppProvider.Helper.findHighestPriorityMetadata(getContentResolver(), packageName);
}
setApp(app); // Will call finish if empty or unknown
mRecyclerView = (RecyclerView) findViewById(R.id.rvDetails);
mAdapter = new AppDetailsRecyclerViewAdapter(this);
LinearLayoutManager lm = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
lm.setStackFromEnd(false);
mRecyclerView.setLayoutManager(lm);
mRecyclerView.setAdapter(mAdapter);
}
private String getPackageNameFromIntent(Intent intent) {
if (!intent.hasExtra(AppDetails.EXTRA_APPID)) {
Log.e(TAG, "No package name found in the intent!");
return null;
}
return intent.getStringExtra(AppDetails.EXTRA_APPID);
}
/**
* If passed null, this will show a message to the user ("Could not find app ..." or something
* like that) and then finish the activity.
*/
private void setApp(App newApp) {
if (newApp == null) {
Toast.makeText(this, R.string.no_such_app, Toast.LENGTH_LONG).show();
finish();
return;
}
mApp = newApp;
}
public class AppDetailsRecyclerViewAdapter
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int VIEWTYPE_HEADER = 0;
private final int VIEWTYPE_SCREENSHOTS = 1;
private final int VIEWTYPE_WHATS_NEW = 2;
private final Context mContext;
private ArrayList<Integer> mItems;
public AppDetailsRecyclerViewAdapter(Context context) {
mContext = context;
updateItems();
}
private void updateItems() {
if (mItems == null)
mItems = new ArrayList<>();
else
mItems.clear();
mItems.add(Integer.valueOf(VIEWTYPE_HEADER));
mItems.add(Integer.valueOf(VIEWTYPE_SCREENSHOTS));
mItems.add(Integer.valueOf(VIEWTYPE_WHATS_NEW));
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEWTYPE_HEADER) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_details2_header, parent, false);
return new HeaderViewHolder(view);
} else if (viewType == VIEWTYPE_SCREENSHOTS) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_details2_screenshots, parent, false);
return new ScreenShotsViewHolder(view);
} else if (viewType == VIEWTYPE_WHATS_NEW) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_details2_whatsnew, parent, false);
return new WhatsNewViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
int viewType = mItems.get(position);
if (viewType == VIEWTYPE_HEADER) {
final HeaderViewHolder vh = (HeaderViewHolder) holder;
ImageLoader.getInstance().displayImage(mApp.iconUrlLarge, vh.iconView, vh.displayImageOptions);
vh.titleView.setText(mApp.name);
if (!TextUtils.isEmpty(mApp.author)) {
vh.authorView.setText(getString(R.string.by_author) + " " + mApp.author);
vh.authorView.setVisibility(View.VISIBLE);
} else {
vh.authorView.setVisibility(View.GONE);
}
vh.summaryView.setText(mApp.summary);
final Spanned desc = Html.fromHtml(mApp.description, null, new Utils.HtmlTagHandler());
vh.descriptionView.setMovementMethod(AppDetails2.SafeLinkMovementMethod.getInstance(mContext));
vh.descriptionView.setText(trimNewlines(desc));
vh.descriptionView.post(new Runnable() {
@Override
public void run() {
if (vh.descriptionView.getLineCount() < HeaderViewHolder.MAX_LINES) {
vh.descriptionMoreView.setVisibility(View.GONE);
} else {
vh.descriptionMoreView.setVisibility(View.VISIBLE);
}
}
});
vh.buttonSecondaryView.setText(R.string.menu_uninstall);
vh.buttonSecondaryView.setVisibility(mApp.isInstalled() ? View.VISIBLE : View.INVISIBLE);
vh.buttonPrimaryView.setText(R.string.menu_install);
vh.buttonPrimaryView.setVisibility(View.VISIBLE);
/* if (appDetails.activeDownloadUrlString != null) {
btMain.setText(R.string.downloading);
btMain.setEnabled(false);
} else if (!app.isInstalled() && app.suggestedVersionCode > 0 &&
appDetails.adapter.getCount() > 0) {
// Check count > 0 due to incompatible apps resulting in an empty list.
// If App isn't installed
installed = false;
statusView.setText(R.string.details_notinstalled);
NfcHelper.disableAndroidBeam(appDetails);
// Set Install button and hide second button
btMain.setText(R.string.menu_install);
btMain.setOnClickListener(mOnClickListener);
btMain.setEnabled(true);
} else if (app.isInstalled()) {
// If App is installed
installed = true;
statusView.setText(getString(R.string.details_installed, app.installedVersionName));
NfcHelper.setAndroidBeam(appDetails, app.packageName);
if (app.canAndWantToUpdate(appDetails)) {
updateWanted = true;
btMain.setText(R.string.menu_upgrade);
} else {
updateWanted = false;
if (appDetails.packageManager.getLaunchIntentForPackage(app.packageName) != null) {
btMain.setText(R.string.menu_launch);
} else {
btMain.setText(R.string.menu_uninstall);
}
}
btMain.setOnClickListener(mOnClickListener);
btMain.setEnabled(true);
}
TextView currentVersion = (TextView) view.findViewById(R.id.current_version);
if (!appDetails.getApks().isEmpty()) {
currentVersion.setText(appDetails.getApks().getItem(0).versionName + " (" + app.license + ")");
} else {
currentVersion.setVisibility(View.GONE);
btMain.setVisibility(View.GONE);
}*/
} else if (viewType == VIEWTYPE_SCREENSHOTS) {
ScreenShotsViewHolder vh = (ScreenShotsViewHolder) holder;
LinearLayoutManager lm = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
vh.recyclerView.setLayoutManager(lm);
ScreenShotsRecyclerViewAdapter adapter = new ScreenShotsRecyclerViewAdapter(mApp);
vh.recyclerView.setAdapter(adapter);
vh.recyclerView.setHasFixedSize(true);
vh.recyclerView.setNestedScrollingEnabled(false);
LinearLayoutManagerSnapHelper helper = new LinearLayoutManagerSnapHelper(lm);
helper.setLinearSnapHelperListener(adapter);
helper.attachToRecyclerView(vh.recyclerView);
} else if (viewType == VIEWTYPE_WHATS_NEW) {
WhatsNewViewHolder vh = (WhatsNewViewHolder) holder;
vh.textView.setText("WHATS NEW GOES HERE");
}
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public int getItemViewType(int position) {
return mItems.get(position);
}
public class HeaderViewHolder extends RecyclerView.ViewHolder {
private static final int MAX_LINES = 5;
final ImageView iconView;
final TextView titleView;
final TextView authorView;
final TextView summaryView;
final TextView descriptionView;
final TextView descriptionMoreView;
final Button buttonPrimaryView;
final Button buttonSecondaryView;
final DisplayImageOptions displayImageOptions;
HeaderViewHolder(View view) {
super(view);
iconView = (ImageView) view.findViewById(R.id.icon);
titleView = (TextView) view.findViewById(R.id.title);
authorView = (TextView) view.findViewById(R.id.author);
summaryView = (TextView) view.findViewById(R.id.summary);
descriptionView = (TextView) view.findViewById(R.id.description);
descriptionMoreView = (TextView) view.findViewById(R.id.description_more);
buttonPrimaryView = (Button) view.findViewById(R.id.primaryButtonView);
buttonSecondaryView = (Button) view.findViewById(R.id.secondaryButtonView);
displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.imageScaleType(ImageScaleType.NONE)
.showImageOnLoading(R.drawable.ic_repo_app_default)
.showImageForEmptyUri(R.drawable.ic_repo_app_default)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
descriptionView.setMaxLines(MAX_LINES);
descriptionView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
descriptionMoreView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Remember current scroll position so that we can restore it
LinearLayoutManager lm = (LinearLayoutManager)mRecyclerView.getLayoutManager();
int pos = lm.findFirstVisibleItemPosition();
int posOffset = 0;
if (pos != NO_POSITION) {
View view = lm.findViewByPosition(pos);
if (view != null)
posOffset = lm.getDecoratedTop(view);
}
if (TextViewCompat.getMaxLines(descriptionView) != MAX_LINES) {
descriptionView.setMaxLines(MAX_LINES);
descriptionMoreView.setText(R.string.more);
} else {
descriptionView.setMaxLines(Integer.MAX_VALUE);
descriptionMoreView.setText(R.string.less);
}
if (pos != NO_POSITION) {
// Restore scroll position
lm.scrollToPositionWithOffset(pos, posOffset);
}
}
});
// Set ALL caps (in a way compatible with SDK 10)
AllCapsTransformationMethod allCapsTransformation = new AllCapsTransformationMethod(view.getContext());
buttonPrimaryView.setTransformationMethod(allCapsTransformation);
buttonSecondaryView.setTransformationMethod(allCapsTransformation);
descriptionMoreView.setTransformationMethod(allCapsTransformation);
}
@Override
public String toString() {
return super.toString() + " '" + titleView.getText() + "'";
}
}
public class ScreenShotsViewHolder extends RecyclerView.ViewHolder {
final RecyclerView recyclerView;
ScreenShotsViewHolder(View view) {
super(view);
recyclerView = (RecyclerView) view.findViewById(R.id.screenshots);
}
@Override
public String toString() {
return super.toString() + " screenshots";
}
}
public class WhatsNewViewHolder extends RecyclerView.ViewHolder {
final TextView textView;
WhatsNewViewHolder(View view) {
super(view);
textView = (TextView) view.findViewById(R.id.text);
}
@Override
public String toString() {
return super.toString() + " " + textView.getText();
}
}
class ScreenShotsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements LinearLayoutManagerSnapHelper.LinearSnapHelperListener {
private final App app;
private final DisplayImageOptions displayImageOptions;
private View selectedView;
private int selectedPosition;
private int selectedItemElevation;
private int unselectedItemMargin;
public ScreenShotsRecyclerViewAdapter(App app) {
super();
this.app = app;
selectedPosition = 0;
selectedItemElevation = getResources().getDimensionPixelSize(R.dimen.details_screenshot_selected_elevation);
unselectedItemMargin = getResources().getDimensionPixelSize(R.dimen.details_screenshot_margin);
displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.imageScaleType(ImageScaleType.NONE)
.showImageOnLoading(R.drawable.ic_repo_app_default)
.showImageForEmptyUri(R.drawable.ic_repo_app_default)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ScreenShotViewHolder vh = (ScreenShotViewHolder) holder;
setViewSelected(vh.itemView, position == selectedPosition);
if (position == selectedPosition)
this.selectedView = vh.itemView;
ImageLoader.getInstance().displayImage(mApp.iconUrlLarge, vh.image, displayImageOptions);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_details2_screenshot_item, parent, false);
return new ScreenShotViewHolder(view);
}
@Override
public int getItemCount() {
return 7;
}
@Override
public void onSnappedToView(View view, int snappedPosition) {
setViewSelected(selectedView, false);
selectedView = view;
selectedPosition = snappedPosition;
setViewSelected(selectedView, true);
}
private void setViewSelected(View view, boolean selected) {
if (view != null) {
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)view.getLayoutParams();
if (selected)
lp.setMargins(0,selectedItemElevation,0,selectedItemElevation);
else
lp.setMargins(0,unselectedItemMargin,0,unselectedItemMargin);
ViewCompat.setElevation(view, selected ? selectedItemElevation : 0);
view.setLayoutParams(lp);
}
}
public class ScreenShotViewHolder extends RecyclerView.ViewHolder {
final ImageView image;
ScreenShotViewHolder(View view) {
super(view);
image = (ImageView) view.findViewById(R.id.image);
}
@Override
public String toString() {
return super.toString() + " screenshot";
}
}
}
}
// The HTML formatter adds "\n\n" at the end of every paragraph. This
// is desired between paragraphs, but not at the end of the whole
// string as it adds unwanted spacing at the end of the TextView.
// Remove all trailing newlines.
// Use this function instead of a trim() as that would require
// converting to String and thus losing formatting (e.g. bold).
private static CharSequence trimNewlines(CharSequence s) {
if (s == null || s.length() < 1) {
return s;
}
int i;
for (i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) != '\n') {
break;
}
}
if (i == s.length() - 1) {
return s;
}
return s.subSequence(0, i + 1);
}
private static final class SafeLinkMovementMethod extends LinkMovementMethod {
private static AppDetails2.SafeLinkMovementMethod instance;
private final Context ctx;
private SafeLinkMovementMethod(Context ctx) {
this.ctx = ctx;
}
public static AppDetails2.SafeLinkMovementMethod getInstance(Context ctx) {
if (instance == null) {
instance = new AppDetails2.SafeLinkMovementMethod(ctx);
}
return instance;
}
private static CharSequence getLink(TextView widget, Spannable buffer,
MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
final int line = layout.getLineForVertical(y);
final int off = layout.getOffsetForHorizontal(line, x);
final ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
if (links.length > 0) {
final ClickableSpan link = links[0];
final Spanned s = (Spanned) widget.getText();
return s.subSequence(s.getSpanStart(link), s.getSpanEnd(link));
}
return "null";
}
@Override
public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer,
@NonNull MotionEvent event) {
try {
return super.onTouchEvent(widget, buffer, event);
} catch (ActivityNotFoundException ex) {
Selection.removeSelection(buffer);
final CharSequence link = getLink(widget, buffer, event);
Toast.makeText(ctx,
ctx.getString(R.string.no_handler_app, link),
Toast.LENGTH_LONG).show();
return true;
}
}
}
}

View File

@ -20,6 +20,7 @@ package org.fdroid.fdroid;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
@ -31,6 +32,7 @@ import android.text.Html;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
@ -596,4 +598,9 @@ public final class Utils {
return data;
}
public static int dpToPx(int dp, Context ctx)
{
Resources r = ctx.getResources();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
}

View File

@ -0,0 +1,74 @@
package org.fdroid.fdroid.views;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSnapHelper;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import static android.support.v7.widget.RecyclerView.NO_POSITION;
public class LinearLayoutManagerSnapHelper extends LinearSnapHelper {
public interface LinearSnapHelperListener {
/**
* Tells the listener that we have selected a view to snap to.
* @param view The selected view (may be null)
* @param position Adapter position of the snapped to view (or NO_POSITION if none)
*/
void onSnappedToView(View view, int position);
};
private LinearLayoutManager mLlm;
private OrientationHelper mOrientationHelper;
private LinearSnapHelperListener mListener;
public LinearLayoutManagerSnapHelper(LinearLayoutManager llm) {
this.mLlm = llm;
this.mOrientationHelper = OrientationHelper.createHorizontalHelper(mLlm);
};
public void setLinearSnapHelperListener(LinearSnapHelperListener listener) {
mListener = listener;
}
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
View snappedView = super.findSnapView(layoutManager);
if (layoutManager.canScrollHorizontally()) {
if (layoutManager instanceof LinearLayoutManager) {
int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
if (firstChild == 0) {
View child = layoutManager.findViewByPosition(firstChild);
if (mOrientationHelper.getDecoratedEnd(child) >= mOrientationHelper.getDecoratedMeasurement(child) / 2
&& mOrientationHelper.getDecoratedEnd(child) > 0) {
int dist1 = super.calculateDistanceToFinalSnap(layoutManager, snappedView)[0];
int dist2 = mOrientationHelper.getDecoratedStart(child);
if (Math.abs(dist1) > Math.abs(dist2)) {
snappedView = child;
}
}
} else if (lastChild == (mLlm.getItemCount() - 1)) {
View child = layoutManager.findViewByPosition(lastChild);
if (mOrientationHelper.getDecoratedStart(child) < mOrientationHelper.getTotalSpace() - mOrientationHelper.getDecoratedMeasurement(child) / 2
&& mOrientationHelper.getDecoratedStart(child) < mOrientationHelper.getTotalSpace()) {
int dist1 = super.calculateDistanceToFinalSnap(layoutManager, snappedView)[0];
int dist2 = mOrientationHelper.getTotalSpace() - mOrientationHelper.getDecoratedEnd(child);
if (Math.abs(dist1) > Math.abs(dist2)) {
snappedView = child;
}
}
}
}
}
if (mListener != null) {
int snappedPosition = 0;
if (snappedView != null)
snappedPosition = mLlm.getPosition(snappedView);
mListener.onSnappedToView(snappedView, snappedPosition);
}
return snappedView;
}
}

View File

@ -20,6 +20,7 @@ import android.widget.AdapterView;
import android.widget.TextView;
import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
@ -30,6 +31,7 @@ import org.fdroid.fdroid.views.AppListAdapter;
public abstract class AppListFragment extends ListFragment implements
AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener,
Preferences.ChangeListener,
LoaderManager.LoaderCallbacks<Cursor> {
@ -109,6 +111,7 @@ public abstract class AppListFragment extends ListFragment implements
// returns the list view is "called between onCreate and
// onActivityCreated" according to the docs.
getListView().setOnItemClickListener(this);
getListView().setOnItemLongClickListener(this);
}
@Override
@ -155,11 +158,21 @@ public abstract class AppListFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showItemDetails(view, position, false);
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
showItemDetails(view, position, true);
return true;
}
private void showItemDetails(View view, int position, boolean useNewDetailsActivity) {
// Cursor is null in the swap list when touching the first item.
Cursor cursor = (Cursor) getListView().getItemAtPosition(position);
if (cursor != null) {
final App app = new App(cursor);
Intent intent = getAppDetailsIntent();
Intent intent = getAppDetailsIntent(useNewDetailsActivity);
intent.putExtra(AppDetails.EXTRA_APPID, app.packageName);
intent.putExtra(AppDetails.EXTRA_FROM, getFromTitle());
if (Build.VERSION.SDK_INT >= 21) {
@ -176,8 +189,8 @@ public abstract class AppListFragment extends ListFragment implements
}
}
private Intent getAppDetailsIntent() {
return new Intent(getActivity(), AppDetails.class);
private Intent getAppDetailsIntent(boolean useNewDetailsActivity) {
return new Intent(getActivity(), useNewDetailsActivity ? AppDetails2.class : AppDetails.class);
}
@Override

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue_dark" />
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue_dark" />
</shape>
</item>
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue_dark" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue" />
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue" />
</shape>
</item>
<item android:state_enabled="false">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/fdroid_blue" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@android:color/white" />
<stroke android:color="@color/fdroid_blue" android:width="2dp" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="org.fdroid.fdroid.AppDetails2">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/AppThemeLight.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
android:src="@drawable/feature_placeholder"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppThemeLight.PopupOverlay" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_app_details2" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:cardBackgroundColor="#efefef"
app:cardCornerRadius="3dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/icon"
android:layout_width="72dp"
android:layout_height="72dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/ic_repo_app_default" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/icon"
android:layout_toRightOf="@id/icon"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
tools:text="App Title" />
<TextView
android:id="@+id/author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/title"
android:layout_alignLeft="@id/title"
android:layout_alignRight="@id/title"
android:layout_alignStart="@id/title"
android:layout_below="@id/title"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Author" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/icon"
android:layout_toEndOf="@id/icon"
android:layout_toRightOf="@id/icon"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true">
<Button
android:id="@+id/secondaryButtonView"
style="@style/DetailsSecondaryButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:ellipsize="marquee"
android:text="THIS IS BUTTON 1" />
<Button
android:id="@+id/primaryButtonView"
style="@style/DetailsPrimaryButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:maxLines="1"
android:ellipsize="marquee"
android:text="THIS IS 2" />
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="This app is awezome"
android:scrollbars="none"
android:textStyle="bold"
/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="This is the app description of this awezome app. It can be several lines long, but will be truncated at just a few if it is. A 'read more' button will appear so that you can expand the view and view the full text, if you wish. Yes, it will be blue and beautiful."
android:scrollbars="none"
/>
<TextView
android:id="@+id/description_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
style="@style/DetailsMoreButtonStyle"
android:gravity="right|end"
android:text="@string/more"
tools:text="more" />
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="3dp"
app:cardBackgroundColor="#ffffff"
android:padding="10dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
>
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
</android.support.v7.widget.CardView>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/screenshots"
android:layout_width="match_parent"
android:layout_height="150dp">
</android.support.v7.widget.RecyclerView>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
/>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rvDetails"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:behavior_overlapTop="40dp"
app:layoutManager="LinearLayoutManager"
tools:context="org.fdroid.fdroid.AppDetails2"
tools:showIn="@layout/activity_app_details2">
</android.support.v7.widget.RecyclerView>

View File

@ -18,4 +18,11 @@
<item name="android:colorButtonNormal">#04b9e6</item>
</style>
<style name="AppThemeLight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -3,5 +3,10 @@
<dimen name="layout_horizontal_margin">16dp</dimen>
<dimen name="material_listitem_height">48dp</dimen>
<dimen name="app_bar_height">180dp</dimen>
<!-- "Not selected" items are inset by this value, while the selected one is not -->
<dimen name="details_screenshot_margin">16dp</dimen>
<!-- The selected item stands out from the background by this elevation -->
<dimen name="details_screenshot_selected_elevation">3dp</dimen>
</resources>

View File

@ -246,4 +246,13 @@
<style name="AppDetailsSubheaderText" parent="AppDetailsSubheaderTextBase" />
<style name="AppThemeTransparent" parent="@android:style/Theme.NoDisplay" />
<style name="AppThemeLight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppThemeLight.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppThemeLight.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DetailsPrimaryButtonStyle">
<item name="android:padding">5dp</item>
<item name="android:textSize">12sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ffffff</item>
<item name="android:background">@drawable/button_primary_background_selector</item>
</style>
<style name="DetailsSecondaryButtonStyle">
<item name="android:padding">5dp</item>
<item name="android:textSize">12sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/fdroid_blue</item>
<item name="android:background">@drawable/button_secondary_background_selector</item>
</style>
<style name="DetailsMoreButtonStyle">
<item name="android:padding">5dp</item>
<item name="android:textSize">15sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/fdroid_blue</item>
</style>
</resources>