Merge branch '0.103-alpha3--ui-fixes' into 'master'
0.103-alpha3 UI fixes Closes #981, #967, and #965 See merge request !490
This commit is contained in:
		
						commit
						f8c9d7ec8e
					
				@ -279,8 +279,7 @@ public class FDroidApp extends Application {
 | 
				
			|||||||
                        new FileNameGenerator() {
 | 
					                        new FileNameGenerator() {
 | 
				
			||||||
                            @Override
 | 
					                            @Override
 | 
				
			||||||
                            public String generate(String imageUri) {
 | 
					                            public String generate(String imageUri) {
 | 
				
			||||||
                                return imageUri.substring(
 | 
					                                return imageUri.substring(imageUri.lastIndexOf('/') + 1);
 | 
				
			||||||
                                        imageUri.lastIndexOf('/') + 1);
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        // 30 days in secs: 30*24*60*60 = 2592000
 | 
					                        // 30 days in secs: 30*24*60*60 = 2592000
 | 
				
			||||||
 | 
				
			|||||||
@ -531,7 +531,10 @@ class NotificationHelper {
 | 
				
			|||||||
                    // Need to check that the notification is still valid, and also that the image
 | 
					                    // Need to check that the notification is still valid, and also that the image
 | 
				
			||||||
                    // is indeed cached now, so we won't get stuck in an endless loop.
 | 
					                    // is indeed cached now, so we won't get stuck in an endless loop.
 | 
				
			||||||
                    AppUpdateStatusManager.AppUpdateStatus oldEntry = appUpdateStatusManager.get(entry.getUniqueKey());
 | 
					                    AppUpdateStatusManager.AppUpdateStatus oldEntry = appUpdateStatusManager.get(entry.getUniqueKey());
 | 
				
			||||||
                    if (oldEntry != null && DiskCacheUtils.findInCache(oldEntry.app.iconUrl, ImageLoader.getInstance().getDiskCache()) != null) {
 | 
					                    if (oldEntry != null
 | 
				
			||||||
 | 
					                            && oldEntry.app != null
 | 
				
			||||||
 | 
					                            && oldEntry.app.iconUrl != null
 | 
				
			||||||
 | 
					                            && DiskCacheUtils.findInCache(oldEntry.app.iconUrl, ImageLoader.getInstance().getDiskCache()) != null) {
 | 
				
			||||||
                        createNotification(oldEntry); // Update with new image!
 | 
					                        createNotification(oldEntry); // Update with new image!
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -696,6 +696,7 @@ public class AppProvider extends FDroidProvider {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            case INSTALLED:
 | 
					            case INSTALLED:
 | 
				
			||||||
                selection = selection.add(queryInstalled());
 | 
					                selection = selection.add(queryInstalled());
 | 
				
			||||||
 | 
					                sortOrder = Cols.NAME;
 | 
				
			||||||
                includeSwap = false;
 | 
					                includeSwap = false;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ import android.content.pm.PackageInfo;
 | 
				
			|||||||
import android.content.pm.PackageManager;
 | 
					import android.content.pm.PackageManager;
 | 
				
			||||||
import android.content.pm.Signature;
 | 
					import android.content.pm.Signature;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.acra.ACRA;
 | 
				
			||||||
import org.fdroid.fdroid.Utils;
 | 
					import org.fdroid.fdroid.Utils;
 | 
				
			||||||
import org.spongycastle.util.encoders.Hex;
 | 
					import org.spongycastle.util.encoders.Hex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -51,6 +52,15 @@ class ApkSignatureVerifier {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean hasFDroidSignature(File apkFile) {
 | 
					    public boolean hasFDroidSignature(File apkFile) {
 | 
				
			||||||
 | 
					        if (!apkFile.exists()) {
 | 
				
			||||||
 | 
					            ACRA.getErrorReporter().handleException(
 | 
				
			||||||
 | 
					                    new Exception("Failed to install Privileged Extension, because " + apkFile.getAbsolutePath() + " does not exist."),
 | 
				
			||||||
 | 
					                    false
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        byte[] apkSig = getApkSignature(apkFile);
 | 
					        byte[] apkSig = getApkSignature(apkFile);
 | 
				
			||||||
        byte[] fdroidSig = getFDroidSignature();
 | 
					        byte[] fdroidSig = getFDroidSignature();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,7 +76,15 @@ class ApkSignatureVerifier {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private byte[] getApkSignature(File apkFile) {
 | 
					    private byte[] getApkSignature(File apkFile) {
 | 
				
			||||||
        final String pkgPath = apkFile.getAbsolutePath();
 | 
					        final String pkgPath = apkFile.getAbsolutePath();
 | 
				
			||||||
 | 
					        if (!apkFile.exists()) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Could not find APK at \"" + pkgPath + "\" when checking for signature.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        PackageInfo pkgInfo = pm.getPackageArchiveInfo(pkgPath, PackageManager.GET_SIGNATURES);
 | 
					        PackageInfo pkgInfo = pm.getPackageArchiveInfo(pkgPath, PackageManager.GET_SIGNATURES);
 | 
				
			||||||
 | 
					        if (pkgInfo == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Could not find PackageInfo for package at \"" + pkgPath + "\".");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return signatureToBytes(pkgInfo.signatures);
 | 
					        return signatureToBytes(pkgInfo.signatures);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -372,17 +372,21 @@ public class AppDetailsRecyclerViewAdapter
 | 
				
			|||||||
                progressBar.setMax(totalBytes);
 | 
					                progressBar.setMax(totalBytes);
 | 
				
			||||||
                progressBar.setProgress(bytesDownloaded);
 | 
					                progressBar.setProgress(bytesDownloaded);
 | 
				
			||||||
                progressBar.setIndeterminate(totalBytes == -1);
 | 
					                progressBar.setIndeterminate(totalBytes == -1);
 | 
				
			||||||
 | 
					                progressLabel.setContentDescription("");
 | 
				
			||||||
                if (resIdString != 0) {
 | 
					                if (resIdString != 0) {
 | 
				
			||||||
                    progressLabel.setText(resIdString);
 | 
					                    progressLabel.setText(resIdString);
 | 
				
			||||||
 | 
					                    progressLabel.setContentDescription(context.getString(R.string.downloading));
 | 
				
			||||||
                    progressPercent.setText("");
 | 
					                    progressPercent.setText("");
 | 
				
			||||||
                } else if (totalBytes > 0 && bytesDownloaded >= 0) {
 | 
					                } else if (totalBytes > 0 && bytesDownloaded >= 0) {
 | 
				
			||||||
                    float percent = bytesDownloaded * 100 / totalBytes;
 | 
					                    float percent = bytesDownloaded * 100 / totalBytes;
 | 
				
			||||||
                    progressLabel.setText(Utils.getFriendlySize(bytesDownloaded) + " / " + Utils.getFriendlySize(totalBytes));
 | 
					                    progressLabel.setText(Utils.getFriendlySize(bytesDownloaded) + " / " + Utils.getFriendlySize(totalBytes));
 | 
				
			||||||
 | 
					                    progressLabel.setContentDescription(context.getString(R.string.app__tts__downloading_progress, (int) percent));
 | 
				
			||||||
                    NumberFormat format = NumberFormat.getPercentInstance();
 | 
					                    NumberFormat format = NumberFormat.getPercentInstance();
 | 
				
			||||||
                    format.setMaximumFractionDigits(0);
 | 
					                    format.setMaximumFractionDigits(0);
 | 
				
			||||||
                    progressPercent.setText(format.format(percent / 100));
 | 
					                    progressPercent.setText(format.format(percent / 100));
 | 
				
			||||||
                } else if (bytesDownloaded >= 0) {
 | 
					                } else if (bytesDownloaded >= 0) {
 | 
				
			||||||
                    progressLabel.setText(Utils.getFriendlySize(bytesDownloaded));
 | 
					                    progressLabel.setText(Utils.getFriendlySize(bytesDownloaded));
 | 
				
			||||||
 | 
					                    progressLabel.setContentDescription(context.getString(R.string.downloading));
 | 
				
			||||||
                    progressPercent.setText("");
 | 
					                    progressPercent.setText("");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -322,7 +322,11 @@ public class AppListItemController extends RecyclerView.ViewHolder {
 | 
				
			|||||||
        actionButton.setVisibility(View.VISIBLE);
 | 
					        actionButton.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (wasSuccessfullyInstalled(app) != null) {
 | 
					        if (wasSuccessfullyInstalled(app) != null) {
 | 
				
			||||||
            actionButton.setText(R.string.menu_launch);
 | 
					            if (activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName) != null) {
 | 
				
			||||||
 | 
					                actionButton.setText(R.string.menu_launch);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                actionButton.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else if (isReadyToInstall(app)) {
 | 
					        } else if (isReadyToInstall(app)) {
 | 
				
			||||||
            if (app.isInstalled()) {
 | 
					            if (app.isInstalled()) {
 | 
				
			||||||
                actionButton.setText(R.string.app__install_downloaded_update);
 | 
					                actionButton.setText(R.string.app__install_downloaded_update);
 | 
				
			||||||
@ -520,12 +524,14 @@ public class AppListItemController extends RecyclerView.ViewHolder {
 | 
				
			|||||||
            AppUpdateStatusManager.AppUpdateStatus successfullyInstalledStatus = wasSuccessfullyInstalled(currentApp);
 | 
					            AppUpdateStatusManager.AppUpdateStatus successfullyInstalledStatus = wasSuccessfullyInstalled(currentApp);
 | 
				
			||||||
            if (successfullyInstalledStatus != null) {
 | 
					            if (successfullyInstalledStatus != null) {
 | 
				
			||||||
                Intent intent = activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName);
 | 
					                Intent intent = activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName);
 | 
				
			||||||
                activity.startActivity(intent);
 | 
					                if (intent != null) {
 | 
				
			||||||
 | 
					                    activity.startActivity(intent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Once it is explicitly launched by the user, then we can pretty much forget about
 | 
					                    // Once it is explicitly launched by the user, then we can pretty much forget about
 | 
				
			||||||
                // any sort of notification that the app was successfully installed. It should be
 | 
					                    // any sort of notification that the app was successfully installed. It should be
 | 
				
			||||||
                // apparent to the user because they just launched it.
 | 
					                    // apparent to the user because they just launched it.
 | 
				
			||||||
                AppUpdateStatusManager.getInstance(activity).removeApk(successfullyInstalledStatus.getUniqueKey());
 | 
					                    AppUpdateStatusManager.getInstance(activity).removeApk(successfullyInstalledStatus.getUniqueKey());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,21 @@
 | 
				
			|||||||
package org.fdroid.fdroid.views.categories;
 | 
					package org.fdroid.fdroid.views.categories;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.app.Activity;
 | 
					import android.app.Activity;
 | 
				
			||||||
import android.database.Cursor;
 | 
					import android.support.annotation.NonNull;
 | 
				
			||||||
import android.support.v4.app.LoaderManager;
 | 
					import android.support.v4.app.LoaderManager;
 | 
				
			||||||
import android.support.v7.widget.RecyclerView;
 | 
					import android.support.v7.widget.RecyclerView;
 | 
				
			||||||
import android.view.ViewGroup;
 | 
					import android.view.ViewGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.fdroid.fdroid.R;
 | 
					import org.fdroid.fdroid.R;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema;
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
 | 
					public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Cursor cursor;
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    private List<String> unlocalizedCategoryNames = Collections.<String>emptyList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final Activity activity;
 | 
					    private final Activity activity;
 | 
				
			||||||
    private final LoaderManager loaderManager;
 | 
					    private final LoaderManager loaderManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,17 +31,16 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onBindViewHolder(CategoryController holder, int position) {
 | 
					    public void onBindViewHolder(CategoryController holder, int position) {
 | 
				
			||||||
        cursor.moveToPosition(position);
 | 
					        holder.bindModel(unlocalizedCategoryNames.get(position));
 | 
				
			||||||
        holder.bindModel(cursor.getString(cursor.getColumnIndex(Schema.CategoryTable.Cols.NAME)));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public int getItemCount() {
 | 
					    public int getItemCount() {
 | 
				
			||||||
        return cursor == null ? 0 : cursor.getCount();
 | 
					        return unlocalizedCategoryNames.size();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void setCategoriesCursor(Cursor cursor) {
 | 
					    public void setCategories(@NonNull List<String> unlocalizedCategoryNames) {
 | 
				
			||||||
        this.cursor = cursor;
 | 
					        this.unlocalizedCategoryNames = unlocalizedCategoryNames;
 | 
				
			||||||
        notifyDataSetChanged();
 | 
					        notifyDataSetChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -75,11 +75,15 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
 | 
				
			|||||||
                .build();
 | 
					                .build();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String translateCategory(Context context, String categoryName) {
 | 
				
			||||||
 | 
					        int categoryNameId = getCategoryResource(context, categoryName, "string", false);
 | 
				
			||||||
 | 
					        return categoryNameId == 0 ? categoryName : context.getString(categoryNameId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void bindModel(@NonNull String categoryName) {
 | 
					    void bindModel(@NonNull String categoryName) {
 | 
				
			||||||
        currentCategory = categoryName;
 | 
					        currentCategory = categoryName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int categoryNameId = getCategoryResource(activity, categoryName, "string", false);
 | 
					        String translatedName = translateCategory(activity, categoryName);
 | 
				
			||||||
        String translatedName = categoryNameId == 0 ? categoryName : activity.getString(categoryNameId);
 | 
					 | 
				
			||||||
        heading.setText(translatedName);
 | 
					        heading.setText(translatedName);
 | 
				
			||||||
        heading.setContentDescription(activity.getString(R.string.tts_category_name, translatedName));
 | 
					        heading.setContentDescription(activity.getString(R.string.tts_category_name, translatedName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,12 @@ import org.fdroid.fdroid.data.CategoryProvider;
 | 
				
			|||||||
import org.fdroid.fdroid.data.Schema;
 | 
					import org.fdroid.fdroid.data.Schema;
 | 
				
			||||||
import org.fdroid.fdroid.views.apps.AppListActivity;
 | 
					import org.fdroid.fdroid.views.apps.AppListActivity;
 | 
				
			||||||
import org.fdroid.fdroid.views.categories.CategoryAdapter;
 | 
					import org.fdroid.fdroid.views.categories.CategoryAdapter;
 | 
				
			||||||
 | 
					import org.fdroid.fdroid.views.categories.CategoryController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.Comparator;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Responsible for ensuring that the categories view is inflated and then populated correctly.
 | 
					 * Responsible for ensuring that the categories view is inflated and then populated correctly.
 | 
				
			||||||
@ -75,13 +81,38 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads all categories from the cursor and stores them in memory to provide to the {@link CategoryAdapter}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * It does this so it is easier to deal with localized/unlocalized categories without having
 | 
				
			||||||
 | 
					     * to store the localized version in the database. It is not expected that the list of categories
 | 
				
			||||||
 | 
					     * will grow so large as to make this a performance concern. If it does in the future, the
 | 
				
			||||||
 | 
					     * {@link CategoryAdapter} can be reverted to wrap the cursor again, and localized category
 | 
				
			||||||
 | 
					     * names can be stored in the database (allowing sorting in their localized form).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
 | 
					    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
 | 
				
			||||||
        if (loader.getId() != LOADER_ID) {
 | 
					        if (loader.getId() != LOADER_ID || cursor == null) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        categoryAdapter.setCategoriesCursor(cursor);
 | 
					        List<String> categoryNames = new ArrayList<>(cursor.getCount());
 | 
				
			||||||
 | 
					        cursor.moveToFirst();
 | 
				
			||||||
 | 
					        while (!cursor.isAfterLast()) {
 | 
				
			||||||
 | 
					            categoryNames.add(cursor.getString(cursor.getColumnIndex(Schema.CategoryTable.Cols.NAME)));
 | 
				
			||||||
 | 
					            cursor.moveToNext();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Collections.sort(categoryNames, new Comparator<String>() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public int compare(String categoryOne, String categoryTwo) {
 | 
				
			||||||
 | 
					                String localizedCategoryOne = CategoryController.translateCategory(activity, categoryOne);
 | 
				
			||||||
 | 
					                String localizedCategoryTwo = CategoryController.translateCategory(activity, categoryTwo);
 | 
				
			||||||
 | 
					                return localizedCategoryOne.compareTo(localizedCategoryTwo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        categoryAdapter.setCategories(categoryNames);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (categoryAdapter.getItemCount() == 0) {
 | 
					        if (categoryAdapter.getItemCount() == 0) {
 | 
				
			||||||
            emptyState.setVisibility(View.VISIBLE);
 | 
					            emptyState.setVisibility(View.VISIBLE);
 | 
				
			||||||
@ -90,6 +121,8 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
 | 
				
			|||||||
            emptyState.setVisibility(View.GONE);
 | 
					            emptyState.setVisibility(View.GONE);
 | 
				
			||||||
            categoriesList.setVisibility(View.VISIBLE);
 | 
					            categoriesList.setVisibility(View.VISIBLE);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cursor.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -98,7 +131,7 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        categoryAdapter.setCategoriesCursor(null);
 | 
					        categoryAdapter.setCategories(Collections.<String>emptyList());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -26,53 +26,63 @@
 | 
				
			|||||||
            android:layout_marginRight="8dp"
 | 
					            android:layout_marginRight="8dp"
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <ImageView
 | 
					            <!-- Icon, Name, Author (optional), Updated date -->
 | 
				
			||||||
                android:id="@+id/icon"
 | 
					            <RelativeLayout
 | 
				
			||||||
                android:layout_width="72dp"
 | 
					                android:id="@+id/icon_and_name"
 | 
				
			||||||
                android:layout_height="72dp"
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
                android:layout_alignParentLeft="true"
 | 
					 | 
				
			||||||
                android:layout_alignParentStart="true"
 | 
					 | 
				
			||||||
                android:layout_alignParentTop="true"
 | 
					 | 
				
			||||||
                android:paddingBottom="8dp"
 | 
					 | 
				
			||||||
                android:paddingRight="8dp"
 | 
					 | 
				
			||||||
                android:paddingEnd="8dp"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_repo_app_default" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:layout_alignParentEnd="true"
 | 
					                android:paddingBottom="8dp">
 | 
				
			||||||
                android:layout_alignParentRight="true"
 | 
					 | 
				
			||||||
                android:layout_alignParentTop="true"
 | 
					 | 
				
			||||||
                android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                android:layout_toEndOf="@id/icon"
 | 
					 | 
				
			||||||
                android:layout_toRightOf="@id/icon"
 | 
					 | 
				
			||||||
                android:orientation="vertical">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <TextView
 | 
					                <ImageView
 | 
				
			||||||
                    android:id="@+id/title"
 | 
					                    android:id="@+id/icon"
 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					                    android:layout_width="72dp"
 | 
				
			||||||
 | 
					                    android:layout_height="72dp"
 | 
				
			||||||
 | 
					                    android:layout_alignParentLeft="true"
 | 
				
			||||||
 | 
					                    android:layout_alignParentStart="true"
 | 
				
			||||||
 | 
					                    android:layout_alignParentTop="true"
 | 
				
			||||||
 | 
					                    android:paddingRight="8dp"
 | 
				
			||||||
 | 
					                    android:paddingEnd="8dp"
 | 
				
			||||||
 | 
					                    android:src="@drawable/ic_repo_app_default" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Name, Author (optional), Updated date -->
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
                    android:textAppearance="@style/DetailsAppTitleStyle"
 | 
					                    android:layout_alignParentEnd="true"
 | 
				
			||||||
                    tools:text="App Title" />
 | 
					                    android:layout_alignParentRight="true"
 | 
				
			||||||
 | 
					                    android:layout_alignParentTop="true"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                    android:layout_toEndOf="@id/icon"
 | 
				
			||||||
 | 
					                    android:layout_toRightOf="@id/icon"
 | 
				
			||||||
 | 
					                    android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <TextView
 | 
					                    <TextView
 | 
				
			||||||
                    android:id="@+id/author"
 | 
					                        android:id="@+id/title"
 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                    android:textAppearance="@style/TextAppearance.AppCompat.Body1"
 | 
					                        android:textAppearance="@style/DetailsAppTitleStyle"
 | 
				
			||||||
                    tools:text="Author" />
 | 
					                        tools:text="Really Long App Title Which Wraps Around Onto the Second Line" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <TextView
 | 
					                    <TextView
 | 
				
			||||||
                    android:id="@+id/text_last_update"
 | 
					                        android:id="@+id/author"
 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                    android:textAppearance="@style/DetailsLastUpdatedStyle"
 | 
					                        android:textAppearance="@style/TextAppearance.AppCompat.Body1"
 | 
				
			||||||
                    android:textColor="@android:color/darker_gray"
 | 
					                        tools:text="Author" />
 | 
				
			||||||
                    tools:text="Update released 12 days ago" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </LinearLayout>
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:id="@+id/text_last_update"
 | 
				
			||||||
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:textAppearance="@style/DetailsLastUpdatedStyle"
 | 
				
			||||||
 | 
					                        android:textColor="@android:color/darker_gray"
 | 
				
			||||||
 | 
					                        tools:text="Update released 12 days ago" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </RelativeLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Progress info -->
 | 
				
			||||||
            <RelativeLayout
 | 
					            <RelativeLayout
 | 
				
			||||||
                android:id="@+id/progress_layout"
 | 
					                android:id="@+id/progress_layout"
 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
@ -81,7 +91,8 @@
 | 
				
			|||||||
                android:layout_alignParentLeft="true"
 | 
					                android:layout_alignParentLeft="true"
 | 
				
			||||||
                android:layout_alignParentRight="true"
 | 
					                android:layout_alignParentRight="true"
 | 
				
			||||||
                android:layout_alignParentStart="true"
 | 
					                android:layout_alignParentStart="true"
 | 
				
			||||||
                android:layout_below="@id/icon">
 | 
					                android:layout_below="@id/icon_and_name"
 | 
				
			||||||
 | 
					                tools:visibility="gone">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <ImageView
 | 
					                <ImageView
 | 
				
			||||||
                    android:id="@+id/progress_cancel"
 | 
					                    android:id="@+id/progress_cancel"
 | 
				
			||||||
@ -90,6 +101,7 @@
 | 
				
			|||||||
                    android:layout_alignParentEnd="true"
 | 
					                    android:layout_alignParentEnd="true"
 | 
				
			||||||
                    android:layout_alignParentRight="true"
 | 
					                    android:layout_alignParentRight="true"
 | 
				
			||||||
                    android:layout_centerVertical="true"
 | 
					                    android:layout_centerVertical="true"
 | 
				
			||||||
 | 
					                    android:contentDescription="@string/app__tts__cancel_download"
 | 
				
			||||||
                    android:src="@android:drawable/ic_menu_close_clear_cancel" />
 | 
					                    android:src="@android:drawable/ic_menu_close_clear_cancel" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <TextView
 | 
					                <TextView
 | 
				
			||||||
@ -99,6 +111,8 @@
 | 
				
			|||||||
                    android:layout_alignParentLeft="true"
 | 
					                    android:layout_alignParentLeft="true"
 | 
				
			||||||
                    android:layout_alignParentStart="true"
 | 
					                    android:layout_alignParentStart="true"
 | 
				
			||||||
                    android:text="@string/downloading"
 | 
					                    android:text="@string/downloading"
 | 
				
			||||||
 | 
					                    android:contentDescription="@string/downloading"
 | 
				
			||||||
 | 
					                    android:focusable="true"
 | 
				
			||||||
                    android:textAppearance="@style/TextAppearance.AppCompat.Small" />
 | 
					                    android:textAppearance="@style/TextAppearance.AppCompat.Small" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <TextView
 | 
					                <TextView
 | 
				
			||||||
@ -122,18 +136,19 @@
 | 
				
			|||||||
                    android:layout_toStartOf="@id/progress_cancel" />
 | 
					                    android:layout_toStartOf="@id/progress_cancel" />
 | 
				
			||||||
            </RelativeLayout>
 | 
					            </RelativeLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Install, Uninstall, Upgrade, Run buttons -->
 | 
				
			||||||
            <LinearLayout
 | 
					            <LinearLayout
 | 
				
			||||||
                android:id="@+id/button_layout"
 | 
					                android:id="@+id/button_layout"
 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:layout_below="@id/icon"
 | 
					                android:layout_below="@id/icon_and_name"
 | 
				
			||||||
                android:layout_alignParentStart="true"
 | 
					                android:layout_alignParentStart="true"
 | 
				
			||||||
                android:layout_alignParentLeft="true"
 | 
					                android:layout_alignParentLeft="true"
 | 
				
			||||||
                android:layout_alignParentEnd="true"
 | 
					                android:layout_alignParentEnd="true"
 | 
				
			||||||
                android:layout_alignParentRight="true"
 | 
					                android:layout_alignParentRight="true"
 | 
				
			||||||
                android:paddingBottom="2dp"
 | 
					                android:paddingBottom="2dp"
 | 
				
			||||||
                android:clipToPadding="false"
 | 
					                android:clipToPadding="false"
 | 
				
			||||||
                android:visibility="gone"
 | 
					                android:visibility="visible"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <Button
 | 
					                <Button
 | 
				
			||||||
 | 
				
			|||||||
@ -75,6 +75,7 @@
 | 
				
			|||||||
    <string name="app_recommended_version_installed">Version %1$s (Recommended)</string>
 | 
					    <string name="app_recommended_version_installed">Version %1$s (Recommended)</string>
 | 
				
			||||||
    <string name="app__newly_added">New</string>
 | 
					    <string name="app__newly_added">New</string>
 | 
				
			||||||
    <string name="added_on">Added on %s</string>
 | 
					    <string name="added_on">Added on %s</string>
 | 
				
			||||||
 | 
					    <string name="app__tts__cancel_download">Cancel download</string>
 | 
				
			||||||
    <string name="app__install_downloaded_update">Update</string>
 | 
					    <string name="app__install_downloaded_update">Update</string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <string name="app_list__name__downloaded_and_ready_to_update">Update %1$s</string>
 | 
					    <string name="app_list__name__downloaded_and_ready_to_update">Update %1$s</string>
 | 
				
			||||||
@ -475,6 +476,7 @@
 | 
				
			|||||||
    <string name="perms_new_perm_prefix">New:</string>
 | 
					    <string name="perms_new_perm_prefix">New:</string>
 | 
				
			||||||
    <string name="perms_description_app">Provided by %1$s.</string>
 | 
					    <string name="perms_description_app">Provided by %1$s.</string>
 | 
				
			||||||
    <string name="downloading">Downloading…</string>
 | 
					    <string name="downloading">Downloading…</string>
 | 
				
			||||||
 | 
					    <string name="app__tts__downloading_progress">Downloading, %1$d%% complete</string>
 | 
				
			||||||
    <string name="downloading_apk">Downloading %1$s</string>
 | 
					    <string name="downloading_apk">Downloading %1$s</string>
 | 
				
			||||||
    <string name="installing">Installing…</string>
 | 
					    <string name="installing">Installing…</string>
 | 
				
			||||||
    <string name="uninstalling">Uninstalling…</string>
 | 
					    <string name="uninstalling">Uninstalling…</string>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user