public class Utils {

    /*
     * Making sure public utility methods remain static
     */
    private Utils() {
    }

    /**
     * Returns the screen/display size
     */
    public static Point getDisplaySize(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        return size;
    }

    /**
     * Shows a (long) toast
     */
    public static void showToast(Context context, String msg) {
        Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
    }

    /**
     * Shows a (long) toast.
     */
    public static void showToast(Context context, int resourceId) {
        Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_LONG).show();
    }

    public static int convertDpToPixel(Context ctx, int dp) {
        float density = ctx.getResources().getDisplayMetrics().density;
        return Math.round((float) dp * density);
    }

    /**
     * Formats time in milliseconds to hh:mm:ss string format.
     */
    public static String formatMillis(int millis) {
        String result = "";
        int hr = millis / 3600000;
        millis %= 3600000;
        int min = millis / 60000;
        millis %= 60000;
        int sec = millis / 1000;
        if (hr > 0) {
            result += hr + ":";
        }
        if (min >= 0) {
            if (min > 9) {
                result += min + ":";
            } else {
                result += "0" + min + ":";
            }
        }
        if (sec > 9) {
            result += sec;
        } else {
            result += "0" + sec;
        }
        return result;
    }
}
 public class DetailsActivity extends Activity {
    public static final String SHARED_ELEMENT_NAME = "hero";
    public static final String APPLICATION = "Apk";

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_details);
    }

}
 public class MainActivity extends Activity {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Fabric.with(this, new Crashlytics());
//        checkSelfVersion();
    }

    @Deprecated
    public void checkSelfVersion() {
        // Make a call to Firebase
    }
}
 public class ApkDownloadHelper extends AbstractDownloadHelper {
    private PackageInstaller mPackageInstaller;

    public ApkDownloadHelper(Activity activity) {
        initialize(activity, null);
    }

    public ApkDownloadHelper initialize(Activity activity, PackageInstaller.DownloadListener listener) {
        super.initialize(activity);
        mPackageInstaller = PackageInstaller.initialize(activity);
        if (listener != null) {
            mPackageInstaller.addListener(listener);
        }
        return this;
    }

    @Override
    public void destroy() {
        super.destroy();
        mPackageInstaller.destroy();
    }

    @Override
    public AbstractDownloadHelper startDownload(String url) {
        mPackageInstaller.wget(url);
        return super.startDownload(url);
    }

    @Override
    public AbstractDownloadHelper startDownload(String url, String adType) {
        mPackageInstaller.wget(url, adType);
        return super.startDownload(url, adType);
    }

    public PackageInstaller getPackageInstaller() {
        return mPackageInstaller;
    }

    @Override
    public String getAppId() {
        return PrivateUtils.COLOR_TV_KEY;
    }

    // Wrapper methods
    public void install(File file) {
        mPackageInstaller.install(file);
    }

    public void addListener(PackageInstaller.DownloadListener listener) {
        mPackageInstaller.addListener(listener);
    }

    public void removeListener(PackageInstaller.DownloadListener listener) {
        mPackageInstaller.removeListener(listener);
    }
}
 @Keep
public class Apk {
    private static final String TAG = Apk.class.getSimpleName();
    private static final boolean DEBUG = true;

    private static final String KEY_NAME = "name";
    private static final String KEY_BANNER = "banner";
    private static final String KEY_ICON = "icon";
    private static final String KEY_DOWNLOAD_URL = "downloadUrl";
    private static final String KEY_PACKAGE_NAME = "packageName";
    private static final String KEY_SUBMISSION_DATE = "submissionDate";
    private static final String KEY_VERSION_CODE = "versionCode";
    private static final String KEY_VERSION_NAME = "versionName";
    private static final String KEY = "key";
    private static final String KEY_DOWNLOADS = "downloads";
    private static final String KEY_VIEWS = "views";

    private String banner;
    private Map<String, String> downloadUrl;
//    private FirebaseMap downloadMap;
    private String icon;
    private String name;
    private String packageName;
    private long submitted;
    private int versionCode;
    private String versionName;
    private String key;
    private long downloads;
    private long views;

    private Apk() {
    }

    public String getBanner() {
        return banner;
    }

    public Map<String, String> getDownloadUrl() {
        return downloadUrl;
    }

    public HashMap<String, String> getDownloadMap() {
        /*if (downloadMap == null) {
            downloadMap = new FirebaseMap(downloadUrl.toString());
        }
        return downloadMap.getMap();*/
        return (HashMap<String, String>) downloadUrl;
    }

    public int getDownloadCount() {
        return getDownloadMap().size();
    }

    public String getDefaultDownloadUrl() {
        return getDownloadMap().get(getDownloadMap().keySet().toArray()[0]);
    }

    public CharSequence[] getDownloadTitleArray() {
        return getDownloadMap().keySet().toArray(new String[getDownloadMap().keySet().size()]);
    }

    public String[] getDownloadUrlArray() {
        return getDownloadMap().values().toArray(new String[getDownloadMap().keySet().size()]);
    }

    public String getIcon() {
        return icon;
    }

    public String getName() {
        return name;
    }

    public String getPackageName() {
        return packageName;
    }

    public long getSubmissionDate() {
        return submitted;
    }

    public int getVersionCode() {
        return versionCode;
    }

    public String getVersionName() {
        return versionName;
    }

    public long getDownloads() {
        return downloads;
    }

    public long getViews() {
        return views;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public void setSubmitted(long submitted) {
        this.submitted = submitted;
    }

    public String toString() {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(KEY_NAME, name);
            jsonObject.put(KEY_BANNER, banner);
            jsonObject.put(KEY_ICON, icon);
            jsonObject.put(KEY_DOWNLOAD_URL, downloadUrl);
            jsonObject.put(KEY_PACKAGE_NAME, packageName);
            jsonObject.put(KEY_SUBMISSION_DATE, submitted);
            jsonObject.put(KEY_VERSION_CODE, versionCode);
            jsonObject.put(KEY_VERSION_NAME, versionName);
            jsonObject.put(KEY, key);
            jsonObject.put(KEY_DOWNLOADS, downloads);
            jsonObject.put(KEY_VIEWS, views);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jsonObject.toString();
    }

    public static class Builder {
        Apk mApk;

        public Builder() {
            mApk = new Apk();
        }

        public Builder(String serial) {
            Log.d(TAG, serial);
            try {
                JSONObject jsonObject = new JSONObject(serial);
                Log.d(TAG, jsonObject.toString());
                mApk = new Apk();
                mApk.name = jsonObject.getString(KEY_NAME);
                mApk.banner = jsonObject.getString(KEY_BANNER);
                mApk.icon = jsonObject.getString(KEY_ICON);
                String downloadUrl = jsonObject.getString(KEY_DOWNLOAD_URL);
                Log.d(TAG, downloadUrl);
                mApk.downloadUrl = new FirebaseMap(downloadUrl).getMap();
                mApk.packageName = jsonObject.getString(KEY_PACKAGE_NAME);
                mApk.submitted = jsonObject.getLong(KEY_SUBMISSION_DATE);
                mApk.versionCode = jsonObject.getInt(KEY_VERSION_CODE);
                mApk.versionName = jsonObject.getString(KEY_VERSION_NAME);
                mApk.key = jsonObject.getString(KEY);
                mApk.downloads = jsonObject.getLong(KEY_DOWNLOADS);
                mApk.views = jsonObject.getLong(KEY_VIEWS);
            } catch (JSONException e) {
                throw new RuntimeException(e.getMessage());
            }
        }

        public Apk build() {
            return mApk;
        }
    }
} @Keep
public class LeanbackShortcut {
/*    private String banner;
    private String icon;
    private String title;*/
    private String download;

/*    public String getBanner() {
        return banner;
    }

    public String getIcon() {
        return icon;
    }

    public String getTitle() {
        return title;
    }*/

    public String getDownload() {
        return download;
    }
}
 public class RepoDatabase {
    private static final String TAG = RepoDatabase.class.getSimpleName();
    private static final boolean DEBUG = true;

    public static final String DATABASE_TYPE_TESTING = "test";
    public static final String DATABASE_TYPE_PROD = "production";
    public static final String DATABASE_TYPE_UNVERIFIED = "unverified";

    public static final String ACTION_APP_ADDED = "news.androidtv.tvapprepo.CHANGED_NEW_APP";

    private static RepoDatabase mRepoDatabase;
    private static HashMap<String, Apk> apps;
    private static List<Listener> listenerList;
    private static DatabaseReference databaseReference;

    private RepoDatabase() {
        listenerList = new ArrayList<>();
    }

    public static RepoDatabase getInstance() {
        return getInstance(DATABASE_TYPE_PROD);
    }

    public static RepoDatabase getInstance(String type) {
        if (apps == null) {
            apps = new HashMap<>();
        }
        // Read from the database
        if (mRepoDatabase == null) {
            mRepoDatabase = new RepoDatabase();
            final FirebaseDatabase database = FirebaseDatabase.getInstance();
            databaseReference = database.getReference(type);
            Log.d(TAG, "Create new instance of RepoDatabase");
            databaseReference.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    // This method is called once with the initial value and again
                    // whenever data at this location is updated.
                    Log.d(TAG, "Got new snapshot " + dataSnapshot.toString());
                    for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
                        try {
                            Log.d(TAG, dataSnapshot1.toString());
                            Apk value = dataSnapshot1.getValue(Apk.class);
                            value.setKey(dataSnapshot1.getKey());
                            Log.d(TAG, "Value is: " + value);
                            if (apps.containsKey(value.getPackageName()) &&
                                    apps.get(value.getPackageName()).getVersionCode() < value.getVersionCode() ||
                                    !apps.containsKey(value.getPackageName())) {
                                for (Listener listener : listenerList) {
                                    listener.onApkAdded(value, apps.size());
                                }
                                apps.put(value.getPackageName(), value);
                            }
                        } catch (RuntimeException e) {
                            // Something weird happened. Debug it.
                            throw new FirebaseIsBeingWeirdException("Something weird happens to Firebase here: " +
                                    dataSnapshot1.toString() + " in " + dataSnapshot.toString());
                        }
                    }
                }

                @Override
                public void onCancelled(DatabaseError error) {
                    // Failed to read value
                    Log.w(TAG, "Failed to read value.", error.toException());
                }
            });
        }
        return mRepoDatabase;
    }

    public void addListener(Listener listener) {
        listenerList.add(listener);
    }

    public void removeListener(Listener listener) {
        listenerList.remove(listener);
    }

    public int getAppCount() {
        return apps.size();
    }

    public HashMap<String, Apk> getApps() {
        return apps;
    }

    public List<Apk> getAppList() {
        return new ArrayList<>(apps.values());
    }

    public void incrementApkDownloads(Apk app) {
        databaseReference.child(app.getKey()).child("downloads").runTransaction(
                new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                Long updatedViews = mutableData.getValue(Long.class) + 1;
                mutableData.setValue(updatedViews);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
            }
        });
    }

    public void incrementApkViews(Apk app) {
        databaseReference.child(app.getKey()).child("views").runTransaction(
                new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                Long updatedViews = mutableData.getValue(Long.class) + 1;
                mutableData.setValue(updatedViews);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
            }
        });
    }

    public static void getLeanbackShortcut(String packageName, final LeanbackShortcutCallback callback) {
        final FirebaseDatabase database = FirebaseDatabase.getInstance();
        DatabaseReference reference = database.getReference("leanbackShortcuts");
        DatabaseReference shortcutReference = reference.child(packageName.replaceAll("[.]", "_"));
        if (DEBUG) {
            Log.d(TAG, "Looking at shortcut reference " + shortcutReference.toString());
            Log.d(TAG, "Looking for package " + packageName);
        }

        shortcutReference.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                if (DEBUG) {
                    Log.d(TAG, "Got value back " + dataSnapshot.toString());
                }
                try {
                    LeanbackShortcut leanbackShortcut =
                            dataSnapshot.getValue(LeanbackShortcut.class);
                    if (leanbackShortcut == null) {
                        if (DEBUG) {
                            Log.i(TAG, "No leanback shortcut");
                        }
                        callback.onNoLeanbackShortcut();
                        return;
                    }
                    if (DEBUG) {
                        Log.i(TAG, "This is a leanback shortcut");
                    }
                    callback.onLeanbackShortcut(leanbackShortcut);
                } catch (Exception e) {
                    if (DEBUG) {
                        Log.i(TAG, "No leanback shortcut");
                        Log.e(TAG, e.getMessage());
                    }
                    callback.onNoLeanbackShortcut();
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                if (DEBUG) {
                    Log.e(TAG, databaseError.toString());
                }
                callback.onDatabaseError(databaseError);
            }
        });
    }

    public interface Listener {
        void onApkAdded(Apk apk, int index);
    }

    public interface LeanbackShortcutCallback {
        void onNoLeanbackShortcut();
        void onLeanbackShortcut(LeanbackShortcut leanbackShortcut);
        void onDatabaseError(DatabaseError error);
    }

    private static class FirebaseIsBeingWeirdException extends RuntimeException {
        public FirebaseIsBeingWeirdException(String s) {
            super(s);
        }
    }
}
 public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
    private static final String TAG = DetailsDescriptionPresenter.class.getSimpleName();

    private Apk mApplication;
    private Context mContext;

    public DetailsDescriptionPresenter(Context context) {
        mContext = context;
    }

    @Override
    protected void onBindDescription(final ViewHolder viewHolder, final Object item) {
        mApplication = (Apk) item;

        if (mApplication != null) {
            viewHolder.getTitle().setText(mApplication.getName());
            viewHolder.getSubtitle().setText("Version " + mApplication.getVersionName() + "  (" +
                    mApplication.getVersionCode() + ")");
            RepoDatabase.getLeanbackShortcut(mApplication.getPackageName(),
                    new RepoDatabase.LeanbackShortcutCallback() {
                @Override
                public void onNoLeanbackShortcut() {
                    Log.d(TAG, "This app does not have a LeanbackShortcut");
                    viewHolder.getBody().setText(R.string.app_leanback);
                }

                @Override
                public void onLeanbackShortcut(LeanbackShortcut leanbackShortcut) {
                    Log.d(TAG, "This app has a LeanbackShortcut");
                    viewHolder.getBody().setText(R.string.app_not_leanback);
                }

                @Override
                public void onDatabaseError(DatabaseError error) {
                    Log.e(TAG, error.getMessage());
                }
            });
        }
    }
}
 public class PackageInstallerUtils {
    /**
     * Compares a new APK to the current version of the app installed on the device, if available.
     *
     * @param apk The apk you want version comparison data for
     * @return true if the installed version is lower.
     */
    public static boolean isUpdateAvailable(Activity activity, Apk apk) {
        PackageManager packageManager = activity.getPackageManager();
        try {
            PackageInfo info = packageManager.getPackageInfo(apk.getPackageName(), 0);
            if (info.versionCode < apk.getVersionCode()) {
                return true;
            }
        } catch (Exception ignored) {
        }
        return false;
    }

    // http://stackoverflow.com/questions/6813322/install-uninstall-apks-programmatically-packagemanager-vs-intents
    public static void uninstallApp(Activity activity, @NonNull String packageName) {
        Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
                activity.getPackageManager().getPackageArchiveInfo(packageName, 0).packageName,
                null));
        activity.startActivity(intent);
    }
}
 public class PackageInstaller {
    private static final String TAG = PackageInstaller.class.getSimpleName();
    private static final boolean DEBUG = true;

    private static final File DOWNLOADS_DIRECTORY = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_DOWNLOADS);
    private static final int REQUEST_READWRITE_STORAGE = 0;
    private static DownloadManager DOWNLOAD_MANAGER;

    private static PackageInstaller mPackageInstaller;

    private Activity mActivity;
    private boolean mInProgress;
    private List<DownloadListener> callbackList;
    private final Set<String> mToDownloadUris;
    private Set<String> mDownloadedUris;
    private int count;

    private BroadcastReceiver mDownloadCompleteReceiver = new BroadcastReceiver() {
        public void onReceive(Context ctxt, Intent intent) {
            Bundle extras = intent.getExtras();
            DownloadManager.Query q = new DownloadManager.Query();
            q.setFilterById(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));
            Cursor c = DOWNLOAD_MANAGER.query(q);
            Log.d(TAG, intent.toString());

            if (c.moveToFirst()) {
                int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
                Log.d(TAG, String.valueOf(count++));
                if (status == DownloadManager.STATUS_SUCCESSFUL) {
                    Log.d(TAG, "Download status successful");
                    String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    // FIXME being called twice in a row.
                    handleDownloadCompleted(uriString);
                } else {
                    int reason = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON));
                    Log.e(TAG, "Error getting APK: " + reason);
                    Toast.makeText(ctxt, "Download Error" + ": " +
                            reason, Toast.LENGTH_LONG).show();
                }
            }
            c.close();
            progressStop();
        }
    };

    private PackageInstaller() {
        callbackList = new ArrayList<>();
        mToDownloadUris = new HashSet<>();
        mDownloadedUris = new HashSet<>();
    }

    public static PackageInstaller initialize(Activity activity) {
        mPackageInstaller = new PackageInstaller();
        activity.registerReceiver(mPackageInstaller.mDownloadCompleteReceiver,
                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

        DOWNLOAD_MANAGER = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
        mPackageInstaller.mActivity = activity;
        mPackageInstaller.chmod();
        return mPackageInstaller;
    }

    public void destroy() {
        mActivity.unregisterReceiver(mDownloadCompleteReceiver);
    }

    public void wget(@NonNull String downloadUrl) {
        wget(downloadUrl, null);
    }

    public void wget(@NonNull String downloadUrl, String filename) {
        if (!chmod()) {
            return;
        }
        progressStart();
        if (downloadUrl.isEmpty()) {
            progressStop();
            if (DEBUG) {
                Log.e(TAG, "Download Url '" + downloadUrl + "' detected as empty");
            }
            Toast.makeText(mActivity, R.string.warning_invalid_tag, Toast.LENGTH_LONG).show();
        } else {
            if (!mToDownloadUris.contains(downloadUrl)) {
                mToDownloadUris.add(downloadUrl);
                if (DEBUG) {
                    Log.i(TAG, "wget " + downloadUrl);
                    Log.i(TAG, "Starting download");
                }
                if (filename == null) {
                    new DownloadFile().execute(downloadUrl);
                } else {
                    new DownloadFile().execute(downloadUrl, filename);
                }
            }
        }
    }

    public boolean chmod() {
        int permissionCheck1 = ContextCompat.checkSelfPermission(mActivity,
                android.Manifest.permission.READ_EXTERNAL_STORAGE);
        int permissionCheck2 = ContextCompat.checkSelfPermission(mActivity,
                android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permissionCheck1 != PackageManager.PERMISSION_GRANTED || permissionCheck2 != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(mActivity,
                    new String[] {
                            android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                            android.Manifest.permission.READ_EXTERNAL_STORAGE
                    },
                    REQUEST_READWRITE_STORAGE);
            return false;
        } else {
            return true;
        }
    }

    private void exec(File file) {
        Uri uri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".provider",
                file); // Nougat style
        try {
            Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE)
                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    .setDataAndType(
                            android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.N ?
                                    Uri.fromFile(file) : uri,
                            "application/vnd.android.package-archive"
                    );
            mActivity.startActivity(intent);
        } catch (Exception ex) {
            Log.e(TAG, "Start activity failed: " + uri, ex);
            Toast.makeText(mActivity, R.string.error_starting_intent, Toast.LENGTH_LONG).show();
        }
    }

    public void install(@NonNull File file) {
        exec(file);
    }

    public void addListener(DownloadListener callback) {
        callbackList.add(callback);
    }

    public void removeListener(DownloadListener callback) {
        callbackList.remove(callback);
    }

    private void progressStart() {
        mInProgress = true;
        for (DownloadListener callback : callbackList) {
            callback.onProgressStarted();
        }
    }

    private void progressStop() {
        mInProgress = false;
        for (DownloadListener callback : callbackList) {
            callback.onProgressEnded();
        }
    }

    public void deleteFile(File file) {
        new DeleteFile().execute(file);
    }

    private synchronized void handleDownloadCompleted(String uriString) {
        // Synchronized method allows only one thread to run this
        synchronized (mToDownloadUris) {
            Log.d(TAG, mDownloadedUris.toString());
            if (!mDownloadedUris.contains(uriString)) {
                mDownloadedUris.add(uriString);
                onDownloadCompleted(uriString);
            }
        }
    }

    private synchronized void onDownloadCompleted(String uriString) {
        Log.d(TAG, "Now let's query " + uriString);
        if (uriString != null && uriString.endsWith(".apk")) {
            Toast.makeText(mActivity, R.string.info_download_complete, Toast.LENGTH_LONG).show();
            if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                Log.d(TAG, "Downloaded " + uriString + " on a < 24 device.");
                for (DownloadListener callback : callbackList) {
                    callback.onApkDownloaded(new File(Uri.parse(uriString).getPath()));
                }
            } else {
                // TODO: Nougat install upon download yields "There was a problem parsing the package"
                Log.d(TAG, "Downloaded " + uriString + " on a Nougat device.");
                for (DownloadListener callback : callbackList) {
                    Log.d(TAG, mDownloadedUris.toString());
                    callback.onApkDownloadedNougat(new File(Uri.parse(uriString).getPath()));
                }
            }
        } else if (uriString != null) {
            File fileToDelete = new File(Uri.parse(uriString).getPath());
            boolean success = fileToDelete.delete();
            Log.i(TAG, "Deletion file. Successful? " + success);
            Toast.makeText(mActivity, mActivity.getString(R.string.warning_invalid_tag) + ": " +
                    (success ? "Removed" : "Unremoved"), Toast.LENGTH_LONG).show();
            for (DownloadListener callback : callbackList) {
                callback.onFileDeleted(fileToDelete,
                        success);
            }
        }
    }

    private class DownloadFile extends AsyncTask<String, Void, Integer> {

        @Override
        protected Integer doInBackground(String... urls) {
            String downloadUri = null;
            try {
                URLConnection con = new URL(urls[0]).openConnection();
                con.getHeaderFields();
                downloadUri = con.getURL().toString();
            } catch (Exception ex) {
                Log.e(TAG, "Download connection error", ex);
            }
            if (downloadUri == null) {
                return R.string.error_download_failed;
            }
            // TODO Make more descriptive download names
            String downloadedFileName;
            if (urls.length >= 2) {
                downloadedFileName = urls[1] + ".apk";
            } else {
                downloadedFileName =
                        urls[0].substring(urls[0].lastIndexOf("/") + 1).trim().replaceAll("\\?", "")
                                + ".apk";
            }
            try {
                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUri));
                request.setTitle(mActivity.getString(R.string.app_name) + ": " + downloadedFileName);
                request.setDescription(mActivity.getString(R.string.directory) + ": " +
                        DOWNLOADS_DIRECTORY.toString());
                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
                        downloadedFileName);
                final DownloadManager manager =
                        (DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE);
                manager.enqueue(request);
            } catch (Exception e) {
                String errorMsg = e.getMessage() + ": " + downloadedFileName + ", " +
                        urls[0] + ", " + DOWNLOADS_DIRECTORY.toString();
                Toast.makeText(mActivity.getApplicationContext(), errorMsg,
                        Toast.LENGTH_SHORT).show();
                // Tell user about an error and throw with additional debug info.
                throw new RuntimeException(errorMsg);
            }
            Log.i(TAG, "Download request for " + urls[0] + " enqueued");
            Log.d(TAG, "Should be saved to " + downloadedFileName);
            return -1;
        }

        @Override
        protected void onPostExecute(Integer failureMessage) {
            if (failureMessage >= 0) {
                Log.e(TAG, "Download manager failed, error code " + failureMessage);
                progressStop();
                Toast.makeText(mActivity, failureMessage, Toast.LENGTH_LONG).show();
            }
        }
    }

    private class DeleteFile extends AsyncTask<File, Void, Boolean> {
        private File fileToDelete;
        @Override
        protected Boolean doInBackground(File... filenames) {
            fileToDelete = filenames[0];
            return fileToDelete.delete();
        }

        @Override
        protected void onPostExecute(Boolean deleted) {
            if (!deleted) {
                Toast.makeText(mActivity, R.string.error_deleting_file, Toast.LENGTH_LONG).show();
            }
            for (DownloadListener callback : callbackList) {
                callback.onFileDeleted(fileToDelete, deleted);
            }
        }
    }

    public interface DownloadListener {
        /**
         * This method is called when the file has finished downloading. In this
         * callback, the method {@link #install(File)} can be called to open the
         * package manager to install this file.
         *
         * @param downloadedApkFile The file that was just downloaded.
         */
        void onApkDownloaded(File downloadedApkFile);

        /**
         * Currently a bug prevents apps from being directly installed after
         * loading. This callback will be run on Nougat devices.
         *
         * @param downloadedApkFile The file that was just downloaded.
         */
        void onApkDownloadedNougat(File downloadedApkFile);

        /**
         * This method is called when a file deletion is completed.
         *
         * @param deletedApkFile The file that should be deleted
         * @param wasSuccessful A boolean that indicates whether the file
         *   operation was successful.
         */
        void onFileDeleted(File deletedApkFile, boolean wasSuccessful);

        /**
         * This method is called when the download begins.
         */
        void onProgressStarted();

        /**
         * This method is called when the download is finished.
         */
        void onProgressEnded();
    }
}
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.app.DetailsFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
import android.support.v17.leanback.widget.DetailsOverviewRow;
import android.support.v17.leanback.widget.DetailsOverviewRowPresenter;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.OnItemViewClickedListener;
import android.support.v17.leanback.widget.Presenter;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.google.firebase.database.DatabaseError;
import java.io.File;
import news.androidtv.tvapprepo.R;
import news.androidtv.tvapprepo.Utils;
import news.androidtv.tvapprepo.activities.DetailsActivity;
import news.androidtv.tvapprepo.activities.MainActivity;
import news.androidtv.tvapprepo.download.ApkDownloadHelper;
import news.androidtv.tvapprepo.model.Apk;
import news.androidtv.tvapprepo.model.LeanbackShortcut;
import news.androidtv.tvapprepo.model.RepoDatabase;
import news.androidtv.tvapprepo.presenters.DetailsDescriptionPresenter;
import news.androidtv.tvapprepo.utils.PackageInstallerUtils;
import tv.puppetmaster.tinydl.PackageInstaller;

        }

        @Override
        public void onProgressEnded() {

        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate DetailsFragment");
        super.onCreate(savedInstanceState);

        prepareBackgroundManager();
        mSelectedApk = new Apk.Builder(getActivity().getIntent()
                .getStringExtra(DetailsActivity.APPLICATION)).build();
        if (mSelectedApk != null) {
            setupAdapter();
            setupDetailsOverviewRow();
            setupDetailsOverviewRowPresenter();
            setupMovieListRow();
            setupMovieListRowPresenter();
            updateBackground(mSelectedApk.getBanner());
            setOnItemViewClickedListener(new ItemViewClickedListener());
            mApkDownloadHelper = new ApkDownloadHelper(getActivity());
            RepoDatabase.getInstance().incrementApkViews(mSelectedApk);

            mApkDownloadHelper.addListener(mDownloadListener);
        } else {
            Intent intent = new Intent(getActivity(), MainActivity.class);
            startActivity(intent);
            Toast.makeText(getActivity(), R.string.no_app_selected, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "App activity stopped");
        mApkDownloadHelper.destroy();
        mApkDownloadHelper.removeListener(mDownloadListener);
    }

    private void prepareBackgroundManager() {
        mBackgroundManager = BackgroundManager.getInstance(getActivity());
        mBackgroundManager.attach(getActivity().getWindow());
        mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
        mMetrics = new DisplayMetrics();
        getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
    }

    protected void updateBackground(String uri) {
        Glide.with(getActivity())
                .load(uri)
                .centerCrop()
                .error(mDefaultBackground)
                .into(new SimpleTarget<GlideDrawable>(mMetrics.widthPixels, mMetrics.heightPixels) {
                    @Override
                    public void onResourceReady(GlideDrawable resource,
                                                GlideAnimation<? super GlideDrawable> glideAnimation) {
                        mBackgroundManager.setDrawable(resource);
                    }
                });
    }

    private void setupAdapter() {
        mPresenterSelector = new ClassPresenterSelector();
        mAdapter = new ArrayObjectAdapter(mPresenterSelector);
        setAdapter(mAdapter);
    }

    private void setupDetailsOverviewRow() {
        Log.d(TAG, "doInBackground: " + mSelectedApk.toString());
        final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedApk);
        row.setImageDrawable(getResources().getDrawable(R.drawable.default_background));
        int width = Utils.convertDpToPixel(getActivity()
                .getApplicationContext(), DETAIL_THUMB_WIDTH);
        int height = Utils.convertDpToPixel(getActivity()
                .getApplicationContext(), DETAIL_THUMB_HEIGHT);
        Glide.with(getActivity())
                .load(mSelectedApk.getIcon())
                .centerCrop()
                .error(R.drawable.default_background)
                .into(new SimpleTarget<GlideDrawable>(width, height) {
                    @Override
                    public void onResourceReady(GlideDrawable resource,
                                                GlideAnimation<? super GlideDrawable>
                                                        glideAnimation) {
                        Log.d(TAG, "details overview card image url ready: " + resource);
                        row.setImageDrawable(resource);
                        mAdapter.notifyArrayItemRangeChanged(0, mAdapter.size());
                    }
                });

        // See if this is already installed. If so, check if it can be updated.
        // Offer an uninstall option.
        // Add an install button.
        SparseArrayObjectAdapter possibleActions = new SparseArrayObjectAdapter();
        PackageManager packageManager = getActivity().getPackageManager();
        try {
            PackageInfo info = packageManager.getPackageInfo(mSelectedApk.getPackageName(), 0);
            if (info.versionCode < mSelectedApk.getVersionCode()) {
                possibleActions.set(ACTION_UPDATE,
                        new Action(ACTION_UPDATE, getString(R.string.update)));
            }
            possibleActions.set(ACTION_UNINSTALL,
                    new Action(ACTION_UNINSTALL, getString(R.string.uninstall)));
        } catch (PackageManager.NameNotFoundException e) {
            // App is not installed
            possibleActions.set(ACTION_INSTALL,
                    new Action(ACTION_INSTALL, getString(R.string.install)));
        }
        row.setActionsAdapter(possibleActions);

        mAdapter.add(row);
    }

    private void setupDetailsOverviewRowPresenter() {
        // Set detail background and style.
        DetailsOverviewRowPresenter detailsPresenter =

                new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter(getActivity()));
