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() {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public String generate(String imageUri) {
 | 
			
		||||
                                return imageUri.substring(
 | 
			
		||||
                                        imageUri.lastIndexOf('/') + 1);
 | 
			
		||||
                                return imageUri.substring(imageUri.lastIndexOf('/') + 1);
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        // 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
 | 
			
		||||
                    // is indeed cached now, so we won't get stuck in an endless loop.
 | 
			
		||||
                    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!
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -696,6 +696,7 @@ public class AppProvider extends FDroidProvider {
 | 
			
		||||
 | 
			
		||||
            case INSTALLED:
 | 
			
		||||
                selection = selection.add(queryInstalled());
 | 
			
		||||
                sortOrder = Cols.NAME;
 | 
			
		||||
                includeSwap = false;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import android.content.pm.PackageInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.content.pm.Signature;
 | 
			
		||||
 | 
			
		||||
import org.acra.ACRA;
 | 
			
		||||
import org.fdroid.fdroid.Utils;
 | 
			
		||||
import org.spongycastle.util.encoders.Hex;
 | 
			
		||||
 | 
			
		||||
@ -51,6 +52,15 @@ class ApkSignatureVerifier {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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[] fdroidSig = getFDroidSignature();
 | 
			
		||||
 | 
			
		||||
@ -66,7 +76,15 @@ class ApkSignatureVerifier {
 | 
			
		||||
 | 
			
		||||
    private byte[] getApkSignature(File apkFile) {
 | 
			
		||||
        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);
 | 
			
		||||
        if (pkgInfo == null) {
 | 
			
		||||
            throw new IllegalArgumentException("Could not find PackageInfo for package at \"" + pkgPath + "\".");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return signatureToBytes(pkgInfo.signatures);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -372,17 +372,21 @@ public class AppDetailsRecyclerViewAdapter
 | 
			
		||||
                progressBar.setMax(totalBytes);
 | 
			
		||||
                progressBar.setProgress(bytesDownloaded);
 | 
			
		||||
                progressBar.setIndeterminate(totalBytes == -1);
 | 
			
		||||
                progressLabel.setContentDescription("");
 | 
			
		||||
                if (resIdString != 0) {
 | 
			
		||||
                    progressLabel.setText(resIdString);
 | 
			
		||||
                    progressLabel.setContentDescription(context.getString(R.string.downloading));
 | 
			
		||||
                    progressPercent.setText("");
 | 
			
		||||
                } else if (totalBytes > 0 && bytesDownloaded >= 0) {
 | 
			
		||||
                    float percent = bytesDownloaded * 100 / 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();
 | 
			
		||||
                    format.setMaximumFractionDigits(0);
 | 
			
		||||
                    progressPercent.setText(format.format(percent / 100));
 | 
			
		||||
                } else if (bytesDownloaded >= 0) {
 | 
			
		||||
                    progressLabel.setText(Utils.getFriendlySize(bytesDownloaded));
 | 
			
		||||
                    progressLabel.setContentDescription(context.getString(R.string.downloading));
 | 
			
		||||
                    progressPercent.setText("");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -322,7 +322,11 @@ public class AppListItemController extends RecyclerView.ViewHolder {
 | 
			
		||||
        actionButton.setVisibility(View.VISIBLE);
 | 
			
		||||
 | 
			
		||||
        if (wasSuccessfullyInstalled(app) != null) {
 | 
			
		||||
            if (activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName) != null) {
 | 
			
		||||
                actionButton.setText(R.string.menu_launch);
 | 
			
		||||
            } else {
 | 
			
		||||
                actionButton.setVisibility(View.GONE);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (isReadyToInstall(app)) {
 | 
			
		||||
            if (app.isInstalled()) {
 | 
			
		||||
                actionButton.setText(R.string.app__install_downloaded_update);
 | 
			
		||||
@ -520,12 +524,14 @@ public class AppListItemController extends RecyclerView.ViewHolder {
 | 
			
		||||
            AppUpdateStatusManager.AppUpdateStatus successfullyInstalledStatus = wasSuccessfullyInstalled(currentApp);
 | 
			
		||||
            if (successfullyInstalledStatus != null) {
 | 
			
		||||
                Intent intent = activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName);
 | 
			
		||||
                if (intent != null) {
 | 
			
		||||
                    activity.startActivity(intent);
 | 
			
		||||
 | 
			
		||||
                    // 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
 | 
			
		||||
                    // apparent to the user because they just launched it.
 | 
			
		||||
                    AppUpdateStatusManager.getInstance(activity).removeApk(successfullyInstalledStatus.getUniqueKey());
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,21 @@
 | 
			
		||||
package org.fdroid.fdroid.views.categories;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.database.Cursor;
 | 
			
		||||
import android.support.annotation.NonNull;
 | 
			
		||||
import android.support.v4.app.LoaderManager;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
 | 
			
		||||
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> {
 | 
			
		||||
 | 
			
		||||
    private Cursor cursor;
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private List<String> unlocalizedCategoryNames = Collections.<String>emptyList();
 | 
			
		||||
 | 
			
		||||
    private final Activity activity;
 | 
			
		||||
    private final LoaderManager loaderManager;
 | 
			
		||||
 | 
			
		||||
@ -27,17 +31,16 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryController> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(CategoryController holder, int position) {
 | 
			
		||||
        cursor.moveToPosition(position);
 | 
			
		||||
        holder.bindModel(cursor.getString(cursor.getColumnIndex(Schema.CategoryTable.Cols.NAME)));
 | 
			
		||||
        holder.bindModel(unlocalizedCategoryNames.get(position));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return cursor == null ? 0 : cursor.getCount();
 | 
			
		||||
        return unlocalizedCategoryNames.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setCategoriesCursor(Cursor cursor) {
 | 
			
		||||
        this.cursor = cursor;
 | 
			
		||||
    public void setCategories(@NonNull List<String> unlocalizedCategoryNames) {
 | 
			
		||||
        this.unlocalizedCategoryNames = unlocalizedCategoryNames;
 | 
			
		||||
        notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -75,11 +75,15 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
 | 
			
		||||
                .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) {
 | 
			
		||||
        currentCategory = categoryName;
 | 
			
		||||
 | 
			
		||||
        int categoryNameId = getCategoryResource(activity, categoryName, "string", false);
 | 
			
		||||
        String translatedName = categoryNameId == 0 ? categoryName : activity.getString(categoryNameId);
 | 
			
		||||
        String translatedName = translateCategory(activity, categoryName);
 | 
			
		||||
        heading.setText(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.views.apps.AppListActivity;
 | 
			
		||||
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.
 | 
			
		||||
@ -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
 | 
			
		||||
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
 | 
			
		||||
        if (loader.getId() != LOADER_ID) {
 | 
			
		||||
        if (loader.getId() != LOADER_ID || cursor == null) {
 | 
			
		||||
            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) {
 | 
			
		||||
            emptyState.setVisibility(View.VISIBLE);
 | 
			
		||||
@ -90,6 +121,8 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
 | 
			
		||||
            emptyState.setVisibility(View.GONE);
 | 
			
		||||
            categoriesList.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cursor.close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -98,7 +131,7 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        categoryAdapter.setCategoriesCursor(null);
 | 
			
		||||
        categoryAdapter.setCategories(Collections.<String>emptyList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,13 @@
 | 
			
		||||
            android:layout_marginRight="8dp"
 | 
			
		||||
            >
 | 
			
		||||
 | 
			
		||||
            <!-- Icon, Name, Author (optional), Updated date -->
 | 
			
		||||
            <RelativeLayout
 | 
			
		||||
                android:id="@+id/icon_and_name"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:paddingBottom="8dp">
 | 
			
		||||
 | 
			
		||||
                <ImageView
 | 
			
		||||
                    android:id="@+id/icon"
 | 
			
		||||
                    android:layout_width="72dp"
 | 
			
		||||
@ -33,11 +40,11 @@
 | 
			
		||||
                    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" />
 | 
			
		||||
 | 
			
		||||
                <!-- Name, Author (optional), Updated date -->
 | 
			
		||||
                <LinearLayout
 | 
			
		||||
                    android:layout_width="0dp"
 | 
			
		||||
                    android:layout_height="wrap_content"
 | 
			
		||||
@ -54,7 +61,7 @@
 | 
			
		||||
                        android:layout_width="match_parent"
 | 
			
		||||
                        android:layout_height="wrap_content"
 | 
			
		||||
                        android:textAppearance="@style/DetailsAppTitleStyle"
 | 
			
		||||
                    tools:text="App Title" />
 | 
			
		||||
                        tools:text="Really Long App Title Which Wraps Around Onto the Second Line" />
 | 
			
		||||
 | 
			
		||||
                    <TextView
 | 
			
		||||
                        android:id="@+id/author"
 | 
			
		||||
@ -73,6 +80,9 @@
 | 
			
		||||
 | 
			
		||||
                </LinearLayout>
 | 
			
		||||
 | 
			
		||||
            </RelativeLayout>
 | 
			
		||||
 | 
			
		||||
            <!-- Progress info -->
 | 
			
		||||
            <RelativeLayout
 | 
			
		||||
                android:id="@+id/progress_layout"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
@ -81,7 +91,8 @@
 | 
			
		||||
                android:layout_alignParentLeft="true"
 | 
			
		||||
                android:layout_alignParentRight="true"
 | 
			
		||||
                android:layout_alignParentStart="true"
 | 
			
		||||
                android:layout_below="@id/icon">
 | 
			
		||||
                android:layout_below="@id/icon_and_name"
 | 
			
		||||
                tools:visibility="gone">
 | 
			
		||||
 | 
			
		||||
                <ImageView
 | 
			
		||||
                    android:id="@+id/progress_cancel"
 | 
			
		||||
@ -90,6 +101,7 @@
 | 
			
		||||
                    android:layout_alignParentEnd="true"
 | 
			
		||||
                    android:layout_alignParentRight="true"
 | 
			
		||||
                    android:layout_centerVertical="true"
 | 
			
		||||
                    android:contentDescription="@string/app__tts__cancel_download"
 | 
			
		||||
                    android:src="@android:drawable/ic_menu_close_clear_cancel" />
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
@ -99,6 +111,8 @@
 | 
			
		||||
                    android:layout_alignParentLeft="true"
 | 
			
		||||
                    android:layout_alignParentStart="true"
 | 
			
		||||
                    android:text="@string/downloading"
 | 
			
		||||
                    android:contentDescription="@string/downloading"
 | 
			
		||||
                    android:focusable="true"
 | 
			
		||||
                    android:textAppearance="@style/TextAppearance.AppCompat.Small" />
 | 
			
		||||
 | 
			
		||||
                <TextView
 | 
			
		||||
@ -122,18 +136,19 @@
 | 
			
		||||
                    android:layout_toStartOf="@id/progress_cancel" />
 | 
			
		||||
            </RelativeLayout>
 | 
			
		||||
 | 
			
		||||
            <!-- Install, Uninstall, Upgrade, Run buttons -->
 | 
			
		||||
            <LinearLayout
 | 
			
		||||
                android:id="@+id/button_layout"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_below="@id/icon"
 | 
			
		||||
                android:layout_below="@id/icon_and_name"
 | 
			
		||||
                android:layout_alignParentStart="true"
 | 
			
		||||
                android:layout_alignParentLeft="true"
 | 
			
		||||
                android:layout_alignParentEnd="true"
 | 
			
		||||
                android:layout_alignParentRight="true"
 | 
			
		||||
                android:paddingBottom="2dp"
 | 
			
		||||
                android:clipToPadding="false"
 | 
			
		||||
                android:visibility="gone"
 | 
			
		||||
                android:visibility="visible"
 | 
			
		||||
                >
 | 
			
		||||
 | 
			
		||||
                <Button
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,7 @@
 | 
			
		||||
    <string name="app_recommended_version_installed">Version %1$s (Recommended)</string>
 | 
			
		||||
    <string name="app__newly_added">New</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_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_description_app">Provided by %1$s.</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="installing">Installing…</string>
 | 
			
		||||
    <string name="uninstalling">Uninstalling…</string>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user