Fall back to InstalledAppProvider when trying to identify the apk to uninstall.
Extracted `getInstalledApk()` method so that it could be better documented, and makes the `uninstallApk()` more consise. It will now throw an `IllegalStateException` if no apk is found, because as issue #800 shows we will end up with a NPE otherwise. Fixes issue #800.
This commit is contained in:
		
							parent
							
								
									6f0b33a092
								
							
						
					
					
						commit
						a7a52fbfba
					
				| @ -84,6 +84,7 @@ import org.fdroid.fdroid.data.App; | ||||
| import org.fdroid.fdroid.data.AppPrefs; | ||||
| import org.fdroid.fdroid.data.AppPrefsProvider; | ||||
| import org.fdroid.fdroid.data.AppProvider; | ||||
| import org.fdroid.fdroid.data.InstalledApp; | ||||
| import org.fdroid.fdroid.data.InstalledAppProvider; | ||||
| import org.fdroid.fdroid.data.RepoProvider; | ||||
| import org.fdroid.fdroid.data.Schema; | ||||
| @ -961,23 +962,52 @@ public class AppDetails extends AppCompatActivity { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Queue for uninstall based on the instance variable {@link #app} | ||||
|      * Attempts to find the installed {@link Apk} from the database. If not found, will lookup the | ||||
|      * {@link InstalledAppProvider} to find the details of the installed app and use that to | ||||
|      * instantiate an {@link Apk} to be returned. | ||||
|      * | ||||
|      * Cases where an {@link Apk} will not be found in the database and for which we fall back to | ||||
|      * the {@link InstalledAppProvider} include: | ||||
|      *  + System apps which are provided by a repository, but for which the version code bundled | ||||
|      *    with the system is not included in the repository. | ||||
|      *  + Regular apps from a repository, where the installed version is old enough that it is no | ||||
|      *    longer available in the repository. | ||||
|      * | ||||
|      * @throws IllegalStateException If neither the {@link PackageManager} or the | ||||
|      * {@link InstalledAppProvider} can't find a reference to the installed apk. | ||||
|      */ | ||||
|     @NonNull | ||||
|     private Apk getInstalledApk() { | ||||
|         try { | ||||
|             PackageInfo pi = packageManager.getPackageInfo(app.packageName, 0); | ||||
| 
 | ||||
|             Apk apk = ApkProvider.Helper.findApkFromAnyRepo(this, pi.packageName, pi.versionCode); | ||||
|             if (apk == null) { | ||||
|                 InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, pi.packageName); | ||||
|                 if (installedApp == null) { | ||||
|                     throw new IllegalStateException("No installed app found when trying to uninstall"); | ||||
|                 } | ||||
| 
 | ||||
|                 apk = new Apk(installedApp); | ||||
|             } | ||||
|             return apk; | ||||
|         } catch (PackageManager.NameNotFoundException e) { | ||||
|             e.printStackTrace(); | ||||
|             throw new IllegalStateException("Couldn't find app while installing"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Queue for uninstall based on the instance variable {@link #app}. | ||||
|      */ | ||||
|     private void uninstallApk() { | ||||
|         Apk apk = app.installedApk; | ||||
|         if (apk == null) { | ||||
|         if (app.installedApk == null) { | ||||
|             // TODO ideally, app would be refreshed immediately after install, then this | ||||
|             // workaround would be unnecessary | ||||
|             try { | ||||
|                 PackageInfo pi = packageManager.getPackageInfo(app.packageName, 0); | ||||
|                 apk = ApkProvider.Helper.findApkFromAnyRepo(this, pi.packageName, pi.versionCode); | ||||
|                 app.installedApk = apk; | ||||
|             } catch (PackageManager.NameNotFoundException e) { | ||||
|                 e.printStackTrace(); | ||||
|                 return; // not installed | ||||
|             } | ||||
|             app.installedApk = getInstalledApk(); | ||||
|         } | ||||
|         Installer installer = InstallerFactory.create(this, apk); | ||||
| 
 | ||||
|         Installer installer = InstallerFactory.create(this, app.installedApk); | ||||
|         Intent intent = installer.getUninstallScreen(); | ||||
|         if (intent != null) { | ||||
|             // uninstall screen required | ||||
|  | ||||
| @ -6,6 +6,7 @@ import android.database.Cursor; | ||||
| import android.os.Build; | ||||
| import android.os.Parcel; | ||||
| import android.os.Parcelable; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import org.fdroid.fdroid.RepoXMLHandler; | ||||
| import org.fdroid.fdroid.Utils; | ||||
| @ -80,6 +81,44 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable { | ||||
|     public Apk() { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * If you need an {@link Apk} but it is no longer in the database any more (e.g. because the | ||||
|      * version you have installed is no longer in the repository metadata) then you can instantiate | ||||
|      * an {@link Apk} via an {@link InstalledApp} instance. | ||||
|      * | ||||
|      * Note: Many of the fields on this instance will not be known in this circumstance. Currently | ||||
|      * the only things that are known are: | ||||
|      * | ||||
|      *  + {@link Apk#packageName} | ||||
|      *  + {@link Apk#versionName} | ||||
|      *  + {@link Apk#versionCode} | ||||
|      *  + {@link Apk#hash} | ||||
|      *  + {@link Apk#hashType} | ||||
|      * | ||||
|      * This could instead be implemented by accepting a {@link PackageInfo} and it would get much | ||||
|      * the same information, but it wouldn't have the hash of the package. Seeing as we've already | ||||
|      * done the hard work to calculate that hash and stored it in the database, we may as well use | ||||
|      * that. | ||||
|      */ | ||||
|     public Apk(@NonNull InstalledApp app) { | ||||
|         packageName = app.getPackageName(); | ||||
|         versionName = app.getVersionName(); | ||||
|         versionCode = app.getVersionCode(); | ||||
|         hash = app.getHash(); // checksum of the APK, in lowercase hex | ||||
|         hashType = app.getHashType(); | ||||
| 
 | ||||
|         // zero for "we don't know". If we require this in the future, then we could look up the | ||||
|         // file on disk if required. | ||||
|         size = 0; | ||||
| 
 | ||||
|         // Same as size. We could look this up if required but not needed at time of writing. | ||||
|         installedFile = null; | ||||
| 
 | ||||
|         // If we are being created from an InstalledApp, it is because we couldn't load it from the | ||||
|         // apk table in the database, indicating it is not available in any of our repos. | ||||
|         repo = 0; | ||||
|     } | ||||
| 
 | ||||
|     public Apk(Cursor cursor) { | ||||
| 
 | ||||
|         checkCursorPosition(cursor); | ||||
|  | ||||
							
								
								
									
										90
									
								
								app/src/main/java/org/fdroid/fdroid/data/InstalledApp.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								app/src/main/java/org/fdroid/fdroid/data/InstalledApp.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| package org.fdroid.fdroid.data; | ||||
| 
 | ||||
| import android.database.Cursor; | ||||
| 
 | ||||
| public class InstalledApp extends ValueObject { | ||||
| 
 | ||||
|     private long id; | ||||
|     private String packageName; | ||||
|     private int versionCode; | ||||
|     private String versionName; | ||||
|     private String applicationLabel; | ||||
|     private String signature; | ||||
|     private long lastUpdateTime; | ||||
|     private String hashType; | ||||
|     private String hash; | ||||
| 
 | ||||
|     public InstalledApp(Cursor cursor) { | ||||
| 
 | ||||
|         checkCursorPosition(cursor); | ||||
| 
 | ||||
|         for (int i = 0; i < cursor.getColumnCount(); i++) { | ||||
|             String n = cursor.getColumnName(i); | ||||
|             switch (n) { | ||||
|                 case Schema.InstalledAppTable.Cols._ID: | ||||
|                     id = cursor.getLong(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.PACKAGE_NAME: | ||||
|                     packageName = cursor.getString(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.VERSION_CODE: | ||||
|                     versionCode = cursor.getInt(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.VERSION_NAME: | ||||
|                     versionName = cursor.getString(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.APPLICATION_LABEL: | ||||
|                     applicationLabel = cursor.getString(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.SIGNATURE: | ||||
|                     signature = cursor.getString(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.LAST_UPDATE_TIME: | ||||
|                     lastUpdateTime = cursor.getLong(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.HASH_TYPE: | ||||
|                     hashType = cursor.getString(i); | ||||
|                     break; | ||||
|                 case Schema.InstalledAppTable.Cols.HASH: | ||||
|                     hash = cursor.getString(i); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getPackageName() { | ||||
|         return packageName; | ||||
|     } | ||||
| 
 | ||||
|     public int getVersionCode() { | ||||
|         return versionCode; | ||||
|     } | ||||
| 
 | ||||
|     public String getVersionName() { | ||||
|         return versionName; | ||||
|     } | ||||
| 
 | ||||
|     public String getApplicationLabel() { | ||||
|         return applicationLabel; | ||||
|     } | ||||
| 
 | ||||
|     public String getSignature() { | ||||
|         return signature; | ||||
|     } | ||||
| 
 | ||||
|     public long getLastUpdateTime() { | ||||
|         return lastUpdateTime; | ||||
|     } | ||||
| 
 | ||||
|     public String getHashType() { | ||||
|         return hashType; | ||||
|     } | ||||
| 
 | ||||
|     public String getHash() { | ||||
|         return hash; | ||||
|     } | ||||
| } | ||||
| @ -9,6 +9,7 @@ import android.content.pm.PackageManager; | ||||
| import android.content.res.Resources; | ||||
| import android.database.Cursor; | ||||
| import android.net.Uri; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import org.fdroid.fdroid.R; | ||||
| @ -53,6 +54,24 @@ public class InstalledAppProvider extends FDroidProvider { | ||||
|             return cachedInfo; | ||||
|         } | ||||
| 
 | ||||
|         @Nullable | ||||
|         public static InstalledApp findByPackageName(Context context, String packageName) { | ||||
|             Cursor cursor = context.getContentResolver().query(getAppUri(packageName), Cols.ALL, null, null, null); | ||||
|             if (cursor == null) { | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 if (cursor.getCount() == 0) { | ||||
|                     return null; | ||||
|                 } | ||||
| 
 | ||||
|                 cursor.moveToFirst(); | ||||
|                 return new InstalledApp(cursor); | ||||
|             } finally { | ||||
|                 cursor.close(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final String PROVIDER_NAME = "InstalledAppProvider"; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Serwylo
						Peter Serwylo