package net.hockeyapp.android;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.hockeyapp.android.listeners.DownloadFileListener;
import net.hockeyapp.android.tasks.DownloadFileTask;
import net.hockeyapp.android.tasks.GetFileSizeTask;
import net.hockeyapp.android.utils.AsyncTaskUtils;
import net.hockeyapp.android.utils.HockeyLog;
import net.hockeyapp.android.utils.VersionHelper;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.Locale;
/**
* <h3>Description</h3>
*
* Fragment to show update information and start the download
* process if the user taps the corresponding button.
*
**/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class UpdateFragment extends DialogFragment implements OnClickListener, UpdateInfoListener {
/**
* The URL of the APK to offer as download
*/
public static final String FRAGMENT_URL = "url";
/**
* Metadata about the update
*/
public static final String FRAGMENT_VERSION_INFO = "versionInfo";
/**
* Task to download the .apk file.
*/
private DownloadFileTask mDownloadTask;
/**
* JSON array with a JSON object for each version.
*/
private JSONArray mVersionInfo;
/**
* HockeyApp URL as a string.
*/
private String mUrlString;
/**
* Helper for version management.
*/
private VersionHelper mVersionHelper;
/**
* Creates a new instance of the fragment.
*
* @param versionInfo JSON array with a JSON object for each version.
* @param urlString HockeyApp URL as a string.
* @return Instance of Fragment
*/
@SuppressWarnings("unused")
static public UpdateFragment newInstance(final JSONArray versionInfo, String urlString) {
Bundle arguments = new Bundle();
arguments.putString(FRAGMENT_URL, urlString);
arguments.putString(FRAGMENT_VERSION_INFO, versionInfo.toString());
UpdateFragment fragment = new UpdateFragment();
fragment.setArguments(arguments);
return fragment;
}
/**
* Called when the fragment is starting. Sets the instance arguments
* and the style of the fragment.
*
* @param savedInstanceState Data it most recently supplied in
* onSaveInstanceState(Bundle)
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
this.mUrlString = getArguments().getString(FRAGMENT_URL);
this.mVersionInfo = new JSONArray(getArguments().getString(FRAGMENT_VERSION_INFO));
} catch (JSONException e) {
dismiss();
return;
}
setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog);
}
/**
* Creates the root view of the fragement, set title, the version number and
* the listener for the download button.
*
* @return The fragment's root view.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = getLayoutView();
mVersionHelper = new VersionHelper(getActivity(), mVersionInfo.toString(), this);
TextView nameLabel = (TextView) view.findViewById(R.id.label_title);
nameLabel.setText(getAppName());
final TextView versionLabel = (TextView) view.findViewById(R.id.label_version);
final String versionString = "Version " + mVersionHelper.getVersionString();
final String fileDate = mVersionHelper.getFileDateString();
String appSizeString = "Unknown size";
long appSize = mVersionHelper.getFileSizeBytes();
if (appSize >= 0L) {
appSizeString = String.format(Locale.US, "%.2f", appSize / (1024.0f * 1024.0f)) + " MB";
} else {
GetFileSizeTask task = new GetFileSizeTask(getActivity(), mUrlString, new DownloadFileListener() {
@Override
public void downloadSuccessful(DownloadFileTask task) {
if (task instanceof GetFileSizeTask) {
long appSize = ((GetFileSizeTask) task).getSize();
String appSizeString = String.format(Locale.US, "%.2f", appSize / (1024.0f * 1024.0f)) + " MB";
versionLabel.setText(getString(R.string.hockeyapp_update_version_details_label, versionString, fileDate, appSizeString));
}
}
});
AsyncTaskUtils.execute(task);
}
versionLabel.setText(getString(R.string.hockeyapp_update_version_details_label, versionString, fileDate, appSizeString));
Button updateButton = (Button) view.findViewById(R.id.button_update);
updateButton.setOnClickListener(this);
WebView webView = (WebView) view.findViewById(R.id.web_update_details);
webView.clearCache(true);
webView.destroyDrawingCache();
webView.loadDataWithBaseURL(Constants.BASE_URL, mVersionHelper.getReleaseNotes(false), "text/html", "utf-8", null);
return view;
}
/**
* Called when the download button is tapped. Starts the download task and
* disables the button to avoid multiple taps.
*/
@Override
public void onClick(View view) {
prepareDownload();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (permissions.length == 0 || grantResults.length == 0) {
// User cancelled permissions dialog -> don't do anything.
return;
}
if (requestCode == Constants.UPDATE_PERMISSIONS_REQUEST) {
// Check for the grant result on write permission
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, start download
startDownloadTask(getActivity());
} else {
// Permission denied, show user alert
HockeyLog.warn("User denied write permission, can't continue with updater task.");
UpdateManagerListener listener = UpdateManager.getLastListener();
if (listener != null) {
listener.onUpdatePermissionsNotGranted();
} else {
final UpdateFragment updateFragment = this;
new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.hockeyapp_permission_update_title))
.setMessage(getString(R.string.hockeyapp_permission_update_message))
.setNegativeButton(getString(R.string.hockeyapp_permission_dialog_negative_button), null)
.setPositiveButton(getString(R.string.hockeyapp_permission_dialog_positive_button), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
updateFragment.prepareDownload();
}
})
.create()
.show();
}
}
}
}
/**
* Returns the current version of the app.
*
* @return The version code as integer.
*/
public int getCurrentVersionCode() {
int currentVersionCode = -1;
try {
currentVersionCode = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), PackageManager.GET_META_DATA).versionCode;
} catch (NameNotFoundException e) {
} catch (NullPointerException e) {
}
return currentVersionCode;
}
public void prepareDownload() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Only if we're running on Android M or later
if (getActivity().checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// We don't have the permission to write to external storage yet, so we have to request it asynchronously.
requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.UPDATE_PERMISSIONS_REQUEST);
return;
}
}
startDownloadTask(this.getActivity());
dismiss();
}
/**
* Starts the download task and sets the listener for a successful
* download, a failed download, and configuration strings.
*/
private void startDownloadTask(final Activity activity) {
mDownloadTask = new DownloadFileTask(activity, mUrlString, new DownloadFileListener() {
public void downloadFailed(DownloadFileTask task, Boolean userWantsRetry) {
if (userWantsRetry) {
startDownloadTask(activity);
}
}
public void downloadSuccessful(DownloadFileTask task) {
// Do nothing as the fragment is already dismissed
}
});
AsyncTaskUtils.execute(mDownloadTask);
}
/**
* Returns the app's name.
*
* @return The app's name as a String.
*/
public String getAppName() {
Activity activity = getActivity();
try {
PackageManager pm = activity.getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(activity.getPackageName(), 0);
return pm.getApplicationLabel(applicationInfo).toString();
} catch (NameNotFoundException exception) {
return "";
}
}
/**
* Creates and returns a new instance of the update view.
*
* @return Update view
*/
public View getLayoutView() {
LinearLayout layout = new LinearLayout(getActivity());
LayoutInflater.from(getActivity()).inflate(R.layout.hockeyapp_fragment_update, layout);
return layout;
}
}