package com.alorma.github.ui.fragment.detail.repo; import android.Manifest; import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.widget.AppCompatDrawableManager; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.alorma.github.R; import com.alorma.github.sdk.bean.dto.response.Content; import com.alorma.github.sdk.bean.info.FileInfo; import com.alorma.github.sdk.bean.info.RepoInfo; import com.alorma.github.sdk.services.content.GetFileContentClient; import com.alorma.github.sdk.services.repo.GetRepoContentsClient; import com.alorma.github.ui.actions.ShareAction; import com.alorma.github.ui.actions.ViewInAction; import com.alorma.github.ui.activity.ContentCommitsActivity; import com.alorma.github.ui.activity.FileActivity; import com.alorma.github.ui.activity.NewContentActivity; import com.alorma.github.ui.adapter.detail.repo.RepoSourceAdapter; import com.alorma.github.ui.fragment.base.LoadingListFragment; import com.alorma.github.ui.view.LinearBreadcrumb; import com.alorma.gitskarios.core.Pair; import com.karumi.dexter.Dexter; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionDeniedResponse; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.single.EmptyPermissionListener; import com.karumi.dexter.listener.single.PermissionListener; import com.mikepenz.octicons_typeface_library.Octicons; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Collections; import java.util.List; import retrofit.RetrofitError; import rx.Observer; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import static com.alorma.github.sdk.bean.dto.response.ContentType.file; public class RepositorySourceListFragment extends LoadingListFragment<RepoSourceAdapter> implements BranchManager, LinearBreadcrumb.SelectionCallback, BackManager, RepoSourceAdapter.SourceAdapterListener { private static final String REPO_INFO = "REPO_INFO"; private static final int RESULT_NEW_FILE = 111; private RepoInfo repoInfo; private LinearBreadcrumb breadCrumbs; private String currentPath; private Observer<Pair<List<Content>, Integer>> subscriber; public static RepositorySourceListFragment newInstance(RepoInfo repoInfo) { Bundle bundle = new Bundle(); bundle.putParcelable(REPO_INFO, repoInfo); RepositorySourceListFragment f = new RepositorySourceListFragment(); f.setArguments(bundle); return f; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); subscriber = new Observer<Pair<List<Content>, Integer>>() { @Override public void onCompleted() { stopRefresh(); } @Override public void onError(Throwable e) { stopRefresh(); if (getActivity() != null) { if (getAdapter() == null || getAdapter().getItemCount() == 0) { if (e != null && e instanceof RetrofitError && ((RetrofitError) e).getResponse() != null) { setEmpty(true, ((RetrofitError) e).getResponse().getStatus()); } } } } @Override public void onNext(Pair<List<Content>, Integer> listIntegerPair) { onContentLoaded(listIntegerPair.first); } }; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.source_list_fragment, null, false); } @Override protected int getLightTheme() { return R.style.AppTheme_Repository; } @Override protected int getDarkTheme() { return R.style.AppTheme_Dark_Repository; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); breadCrumbs = (LinearBreadcrumb) view.findViewById(R.id.breadCrumbs); breadCrumbs.setCallback(this); if (getArguments() != null) { getContent(); } } private void navigateUp() { if (currentPath != null) { String[] paths = currentPath.split("/"); paths = Arrays.copyOf(paths, paths.length - 1); StringBuilder builder = new StringBuilder(); if (paths.length > 0) { for (String path : paths) { builder.append(path); builder.append("/"); } String path = builder.toString(); getPathContent(path.substring(0, path.length() - 1)); } else { getContent(); } } } @Override protected void loadArguments() { if (getArguments() != null) { repoInfo = getArguments().getParcelable(REPO_INFO); } } private void getContent() { currentPath = "/"; if (getAdapter() != null) { getAdapter().clear(); } setAdapter(null); breadCrumbs.initRootCrumb(); GetRepoContentsClient repoContentsClient = new GetRepoContentsClient(repoInfo); repoContentsClient.observable().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber); } @Override protected Octicons.Icon getNoDataIcon() { return Octicons.Icon.oct_file_text; } @Override protected int getNoDataText() { return R.string.no_content; } private void displayContent(List<Content> contents) { if (getActivity() != null) { stopRefresh(); if (currentPath != null) { breadCrumbs.addPath(currentPath, "/"); } RepoSourceAdapter contentAdapter = new RepoSourceAdapter(getActivity(), LayoutInflater.from(getActivity()), repoInfo); contentAdapter.setSourceAdapterListener(this); contentAdapter.addAll(contents); setAdapter(contentAdapter); } } @Override public void onContentClick(Content item) { if (item.isDir()) { getPathContent(item.path); } else if (item.isFile()) { FileInfo fileInfo = new FileInfo(); fileInfo.repoInfo = repoInfo; fileInfo.head = item.sha; fileInfo.name = item.name; fileInfo.path = item.path; Intent intent = FileActivity.createLauncherIntent(getActivity(), fileInfo); startActivity(intent); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.source_list_fragment, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem menuItem = menu.findItem(R.id.action_content_download); try { if (menuItem != null) { Drawable drawable = AppCompatDrawableManager.get().getDrawable(getActivity(), R.drawable.cloud_download); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { drawable = DrawableCompat.wrap(drawable); drawable.setTint(Color.WHITE); } menuItem.setIcon(drawable); } } catch (Exception e) { e.printStackTrace(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_content_download: downloadRepo(); return true; default: return super.onOptionsItemSelected(item); } } private void downloadRepo() { Toast.makeText(getContext(), "Not implemented", Toast.LENGTH_SHORT).show(); /* String archive_url = currentRepo.getArchiveUrl(); GitskariosSettings settings = new GitskariosSettings(getContext()); String zipBall = getString(R.string.download_zip_value); String fileType = settings.getDownloadFileType(zipBall); archive_url = archive_url.replace("{archive_format}", fileType); archive_url = archive_url.replace("{/ref}", "/" + currentRepo.getDefaultBranch()); GitskariosDownloadManager gitskariosDownloadManager = new GitskariosDownloadManager(); gitskariosDownloadManager.download(this, archive_url, currentRepo.name + "_" + currentRepo.getDefaultBranch() + "." + getExtensionFromFileType(fileType), text -> { Snackbar snackbar = Snackbar.make(recyclerView, getString(text), Snackbar.LENGTH_LONG); snackbar.setActionAndAuthor(getString(R.string.external_storage_permission_request_action), v -> gitskariosDownloadManager.openSettings(getContext())); snackbar.show(); }); */ } private String getExtensionFromFileType(String fileType) { if ("zipball".equals(fileType)) { return "zip"; } else if ("tarball".equals(fileType)) { return "tar.gz"; } return null; } @Override public void onContentMenuAction(Content content, MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.action_content_share: new ShareAction(getActivity(), repoInfo.owner + "/" + repoInfo.name, content._links.html).setType(getString(R.string.source_code)) .execute(); break; case R.id.action_content_open: new ViewInAction(getActivity(), content._links.html).setType(getString(R.string.source_code)).execute(); break; case R.id.action_copy_content_url: copy(content._links.html); Toast.makeText(getActivity(), getString(R.string.url_of_copied, content.name), Toast.LENGTH_SHORT).show(); break; case R.id.action_content_history: Intent intent = ContentCommitsActivity.createLauncherIntent(getActivity(), repoInfo, content.path, content.name); startActivity(intent); break; case R.id.action_content_download: if (file.equals(content.type)) { checkPermissionsAndDownload(content); } else { Toast.makeText(getActivity(), R.string.download_only_files, Toast.LENGTH_LONG).show(); } break; } } private void checkPermissionsAndDownload(Content content) { PermissionListener listener = new EmptyPermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse response) { super.onPermissionGranted(response); downloadFile(content); } @Override public void onPermissionDenied(PermissionDeniedResponse response) { super.onPermissionDenied(response); Toast.makeText(getActivity(), R.string.external_storage_permission_request, Toast.LENGTH_SHORT).show(); } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { token.continuePermissionRequest(); } }; Dexter.checkPermission(listener, Manifest.permission.WRITE_EXTERNAL_STORAGE); } private void downloadFile(Content content) { FileInfo info = new FileInfo(); info.repoInfo = repoInfo; info.path = content.path; info.name = content.name; new GetFileContentClient(info).observable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Content>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Content content) { File downloadFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/gitskarios"); if (!downloadFolder.exists()) { downloadFolder.mkdir(); } File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/gitskarios", content.name); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream outputStream; try { outputStream = new FileOutputStream(file); outputStream.write(decodeContent(content.content).getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } Toast.makeText(getContext(), content.name + " has been download at Downloads/gitskarios/" + content.name, Toast.LENGTH_SHORT) .show(); } }); } public void copy(String text) { ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Gitskarios", text); clipboard.setPrimaryClip(clip); } @Override public void setEmpty(boolean withError, int statusCode) { super.setEmpty(withError, statusCode); } @Override public void hideEmpty() { super.hideEmpty(); } @Override public void setCurrentBranch(String branch) { loadArguments(); repoInfo.branch = branch; getContent(); } private void getPathContent(String path) { currentPath = path; startRefresh(); GetRepoContentsClient repoContentsClient = new GetRepoContentsClient(repoInfo, path); repoContentsClient.observable().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber); } private void onContentLoaded(List<Content> contents) { if (getActivity() != null) { if (contents != null && contents.size() > 0) { hideEmpty(); Collections.sort(contents); Collections.reverse(contents); displayContent(contents); } else if (getAdapter() == null || getAdapter().getItemCount() == 0) { setEmpty(); } } } @Override protected boolean useFAB() { return repoInfo != null && repoInfo.permissions != null && repoInfo.permissions.push; } @Override protected Octicons.Icon getFABGithubIcon() { return Octicons.Icon.oct_plus; } @Override protected void fabClick() { super.fabClick(); createFile(); } private void createFile() { Intent launcherIntent = NewContentActivity.createLauncherIntent(getActivity(), repoInfo, currentPath); startActivityForResult(launcherIntent, RESULT_NEW_FILE); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK) { if (requestCode == RESULT_NEW_FILE) { onRefresh(); } } } @Override public void onRefresh() { if (currentPath == null || currentPath.equals("/")) { getContent(); } else { getPathContent(currentPath); } } @Override public void onCrumbSelection(LinearBreadcrumb.Crumb crumb, String absolutePath, int count, int index) { if (crumb.getPath() != null && crumb.getPath().equals("/")) { getContent(); } else { getPathContent(breadCrumbs.getAbsolutePath(crumb, "/")); } breadCrumbs.setActive(crumb); } @Override public void loadMoreItems() { } private void downloadContent(Content content) { if (file.equals(content.type)) { FileInfo info = new FileInfo(); info.repoInfo = repoInfo; info.path = content.path; info.name = content.name; new GetFileContentClient(info).observable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Content>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Content content) { File downloadFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/gitskarios"); if (!downloadFolder.exists()) { downloadFolder.mkdirs(); } File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/gitskarios", content.name); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream outputStream; try { outputStream = new FileOutputStream(file); outputStream.write(decodeContent(content.content).getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } Toast.makeText(getContext(), content.name + " has been download at Download/gitskarios/" + content.name, Toast.LENGTH_SHORT) .show(); } }); } else { Toast.makeText(getActivity(), R.string.download_only_files, Toast.LENGTH_LONG).show(); } } private String decodeContent(String encoded) { String decoded = encoded; byte[] data = android.util.Base64.decode(encoded, android.util.Base64.DEFAULT); try { decoded = new String(data, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return decoded; } @Override public boolean onBackPressed() { if (breadCrumbs != null && breadCrumbs.size() == 1) { return true; } else { navigateUp(); return false; } } }