/* * Copyright 2011 Azwan Adli Abdullah * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gh4a.activities; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.support.v4.content.Loader; import android.support.v4.print.PrintHelper; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import com.gh4a.R; import com.gh4a.loader.ContentLoader; import com.gh4a.loader.LoaderCallbacks; import com.gh4a.loader.LoaderResult; import com.gh4a.utils.FileUtils; import com.gh4a.utils.IntentUtils; import org.eclipse.egit.github.core.FieldError; import org.eclipse.egit.github.core.RepositoryContents; import org.eclipse.egit.github.core.RequestError; import org.eclipse.egit.github.core.client.RequestException; import org.eclipse.egit.github.core.util.EncodingUtils; import java.util.List; import java.util.Locale; public class FileViewerActivity extends WebViewerActivity { public static Intent makeIntent(Context context, String repoOwner, String repoName, String ref, String fullPath) { return makeIntentWithHighlight(context, repoOwner, repoName, ref, fullPath, -1, -1); } public static Intent makeIntentWithHighlight(Context context, String repoOwner, String repoName, String ref, String fullPath, int highlightStart, int highlightEnd) { return new Intent(context, FileViewerActivity.class) .putExtra("owner", repoOwner) .putExtra("repo", repoName) .putExtra("path", fullPath) .putExtra("ref", ref) .putExtra("highlight_start", highlightStart) .putExtra("highlight_end", highlightEnd); } private String mRepoName; private String mRepoOwner; private String mPath; private String mRef; private int mHighlightStart; private int mHighlightEnd; private RepositoryContents mContent; private static final int MENU_ITEM_HISTORY = 10; private static final String RAW_URL_FORMAT = "https://raw.githubusercontent.com/%s/%s/%s/%s"; private final LoaderCallbacks<List<RepositoryContents>> mFileCallback = new LoaderCallbacks<List<RepositoryContents>>(this) { @Override protected Loader<LoaderResult<List<RepositoryContents>>> onCreateLoader() { return new ContentLoader(FileViewerActivity.this, mRepoOwner, mRepoName, mPath, mRef); } @Override protected void onResultReady(List<RepositoryContents> result) { if (result != null && !result.isEmpty()) { mContent = result.get(0); onDataReady(); setContentEmpty(false); } else { setContentEmpty(true); setContentShown(true); } } @Override protected boolean onError(Exception e) { if (e instanceof RequestException) { RequestError error = ((RequestException) e).getError(); List<FieldError> errors = error != null ? error.getErrors() : null; if (errors != null) { for (FieldError fe : errors) { if ("too_large".equals(fe.getCode())) { openUnsuitableFileAndFinish(); return true; } } } } return super.onError(e); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String filename = FileUtils.getFileName(mPath); if (FileUtils.isBinaryFormat(filename) && !FileUtils.isImage(filename)) { openUnsuitableFileAndFinish(); } else { getSupportLoaderManager().initLoader(0, null, mFileCallback); } ActionBar actionBar = getSupportActionBar(); actionBar.setTitle(filename); actionBar.setSubtitle(mRepoOwner + "/" + mRepoName); actionBar.setDisplayHomeAsUpEnabled(true); } @Override protected void onInitExtras(Bundle extras) { super.onInitExtras(extras); mRepoOwner = extras.getString("owner"); mRepoName = extras.getString("repo"); mPath = extras.getString("path"); mRef = extras.getString("ref"); mHighlightStart = extras.getInt("highlight_start", -1); mHighlightEnd = extras.getInt("highlight_end", -1); } @Override protected boolean canSwipeToRefresh() { return true; } @Override public void onRefresh() { forceLoaderReload(0); setContentShown(false); super.onRefresh(); } @Override protected String generateHtml(String cssTheme, boolean addTitleHeader) { String base64Data = mContent.getContent(); if (base64Data != null && FileUtils.isImage(mPath)) { String title = addTitleHeader ? getDocumentTitle() : null; String imageUrl = "data:image/" + FileUtils.getFileExtension(mPath) + ";base64," + base64Data; return highlightImage(imageUrl, cssTheme, title); } else if (base64Data != null && FileUtils.isMarkdown(mPath)) { return generateMarkdownHtml(base64Data, mRepoOwner, mRepoName, mRef, cssTheme, addTitleHeader); } else { String data = base64Data != null ? new String(EncodingUtils.fromBase64(base64Data)) : ""; return generateCodeHtml(data, mPath, mHighlightStart, mHighlightEnd, cssTheme, addTitleHeader); } } @Override protected String getDocumentTitle() { return getString(R.string.file_print_document_title, FileUtils.getFileName(mPath), mRepoOwner, mRepoName); } @Override protected boolean handlePrintRequest() { if (!FileUtils.isImage(mPath)) { return false; } String base64Data = mContent != null ? mContent.getContent() : null; if (base64Data == null) { return false; } byte[] decodedData = EncodingUtils.fromBase64(base64Data); Bitmap bitmap = BitmapFactory.decodeByteArray(decodedData, 0, decodedData.length); PrintHelper printHelper = new PrintHelper(this); printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT); printHelper.printBitmap(getDocumentTitle(), bitmap); return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.file_viewer_menu, menu); if (FileUtils.isImage(mPath) || FileUtils.isMarkdown(mPath)) { menu.removeItem(R.id.wrap); } MenuItem item = menu.add(0, MENU_ITEM_HISTORY, Menu.NONE, R.string.history); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_NEVER); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { String url = String.format(Locale.US, "https://github.com/%s/%s/blob/%s/%s", mRepoOwner, mRepoName, mRef, mPath); switch (item.getItemId()) { case R.id.browser: IntentUtils.launchBrowser(this, Uri.parse(url)); return true; case R.id.share: Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_file_subject, FileUtils.getFileName(mPath), mRepoOwner + "/" + mRepoName)); shareIntent.putExtra(Intent.EXTRA_TEXT, url); shareIntent = Intent.createChooser(shareIntent, getString(R.string.share_title)); startActivity(shareIntent); return true; case MENU_ITEM_HISTORY: startActivity(CommitHistoryActivity.makeIntent(this, mRepoOwner, mRepoName, mRef, mPath)); return true; } return super.onOptionsItemSelected(item); } @Override protected Intent navigateUp() { return RepositoryActivity.makeIntent(this, mRepoOwner, mRepoName); } private void openUnsuitableFileAndFinish() { String url = String.format(Locale.US, RAW_URL_FORMAT, mRepoOwner, mRepoName, mRef, mPath); String mime = FileUtils.getMimeTypeFor(FileUtils.getFileName(mPath)); Intent intent = IntentUtils.createViewerOrBrowserIntent(this, Uri.parse(url), mime); if (intent == null) { handleLoadFailure(new ActivityNotFoundException()); findViewById(R.id.retry_button).setVisibility(View.GONE); } else { startActivity(intent); finish(); } } private static String highlightImage(String imageUrl, String cssTheme, String title) { StringBuilder content = new StringBuilder(); content.append("<html><head>"); writeCssInclude(content, "text", cssTheme); content.append("</head><body>"); if (title != null) { content.append("<h2>").append(title).append("</h2>"); } content.append("<div class='image'>"); content.append("<img src='").append(imageUrl).append("' />"); content.append("</div></body></html>"); return content.toString(); } }