diff --git a/README.md b/README.md index 356c9634..f3e3c340 100644 --- a/README.md +++ b/README.md @@ -69,26 +69,30 @@ We use [Crowdin](https://crowdin.com/project/gitnex) for translation. If your la Thanks to all the open source libraries, contributors and donators. #### Open source libraries -- Retrofit -- Gson -- Okhttp -- Picasso -- Markwon -- Prism4j -- Prettytime -- Amulyakhare/textdrawable -- Vdurmont/emoji-java -- Pes/materialcolorpicker -- HamidrezaAmz/BreadcrumbsView -- Chrisbanes/PhotoView -- Pddstudio/highlightjs-android -- Apache/commons-io -- Caverock/androidsvg -- Droidsonroids.gif/android-gif-drawable -- Barteksc/androidPdfViewer -- Ge0rg/memorizingTrustManager -- Dimezis/blurView -- Mikaelhg/urlbuilder +- [square/retrofit](https://github.com/square/retrofit) +- [google/gson](https://github.com/google/gson) +- [square/okhttp](https://github.com/square/okhttp) +- [square/picasso](https://github.com/square/picasso) +- [wasabeef/picasso-transformations](https://github.com/wasabeef/picasso-transformations) +- [cats-oss/android-gpuimage](https://github.com/cats-oss/android-gpuimage) +- [noties/Markwon](https://github.com/noties/Markwon) +- [noties/Prism4j](https://github.com/noties/Prism4j) +- [ocpsoft/prettytime](https://github.com/ocpsoft/prettytime) +- [amulyakhare/TextDrawable](https://github.com/amulyakhare/TextDrawable) +- [vdurmont/emoji-java](https://github.com/vdurmont/emoji-java) +- [Pes8/android-material-color-picker-dialog](https://github.com/Pes8/android-material-color-picker-dialog) +- [HamidrezaAmz/BreadcrumbsView](https://github.com/HamidrezaAmz/BreadcrumbsView) +- [Baseflow/PhotoView](https://github.com/Baseflow/PhotoView) +- [apache/commons](https://github.com/apache/commons-io) +- [barteksc/AndroidPdfViewer](https://github.com/barteksc/AndroidPdfViewer) +- [ge0rg/MemorizingTrustManager](https://github.com/ge0rg/MemorizingTrustManager) +- [mikaelhg/urlbuilder](https://github.com/mikaelhg/urlbuilder) +- [ACRA/acra](https://github.com/ACRA/acra) + +#### Icon sets +- [feathericons/feather](https://github.com/feathericons/feather) +- [primer/octicons](https://github.com/primer/octicons) +- [google/material-design-icons](https://github.com/google/material-design-icons) [Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif) diff --git a/app/build.gradle b/app/build.gradle index 3cc8fd32..ac7dbf5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,8 +6,8 @@ android { applicationId "org.mian.gitnex" minSdkVersion 21 targetSdkVersion 30 - versionCode 346 - versionName "3.5.0-dev" + versionCode 395 + versionName "4.0.0-dev" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -30,7 +30,7 @@ android { release { minifyEnabled false shrinkResources false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } lintOptions { @@ -54,28 +54,30 @@ configurations { } dependencies { - def lifecycle_version = '2.3.0' - def markwon_version = '4.6.1' + def lifecycle_version = '2.3.1' + def markwon_version = '4.6.2' def work_version = "2.5.0" def acra = "5.7.0" implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.3.0-beta01' + implementation 'androidx.appcompat:appcompat:1.3.0-rc01' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - implementation 'com.squareup.okhttp3:okhttp:4.9.0' + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2' implementation "com.google.code.gson:gson:2.8.6" implementation "com.squareup.picasso:picasso:2.71828" + implementation 'jp.wasabeef:picasso-transformations:2.4.0' + implementation 'jp.co.cyberagent.android:gpuimage:2.1.0' implementation "com.amulyakhare:com.amulyakhare.textdrawable:1.0.1" implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' - implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' + implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2' implementation 'org.ocpsoft.prettytime:prettytime:5.0.0.Final' implementation "com.pes.materialcolorpicker:library:1.2.5" implementation "io.noties.markwon:core:$markwon_version" @@ -94,11 +96,9 @@ dependencies { implementation "io.noties.markwon:image-picasso:$markwon_version" implementation "io.noties:prism4j:2.0.0" annotationProcessor "io.noties:prism4j-bundler:2.0.0" - implementation "com.caverock:androidsvg:1.4" - implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.21" implementation "com.github.HamidrezaAmz:BreadcrumbsView:0.2.9" implementation "commons-io:commons-io:20030203.000550" - implementation 'org.apache.commons:commons-lang3:3.11' + implementation 'org.apache.commons:commons-lang3:3.12.0' implementation "com.github.chrisbanes:PhotoView:2.3.0" implementation "com.github.barteksc:android-pdf-viewer:3.2.0-beta.1" implementation "ch.acra:acra-mail:$acra" @@ -107,11 +107,10 @@ dependencies { implementation 'androidx.room:room-runtime:2.2.6' annotationProcessor 'androidx.room:room-compiler:2.2.6' implementation "androidx.work:work-runtime:$work_version" - implementation "com.eightbitlab:blurview:1.6.4" implementation "io.mikael:urlbuilder:2.0.9" implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" - implementation "org.codeberg.gitnex:tea4j:1.0.1" - coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.1" + implementation "org.codeberg.gitnex:tea4j:1.0.5" + coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" implementation 'androidx.biometric:biometric:1.1.0' } diff --git a/app/src/main/assets/highlightjs/highlight.pack.js b/app/src/main/assets/highlightjs/highlight.pack.js deleted file mode 100644 index 7bd4b8d3..00000000 --- a/app/src/main/assets/highlightjs/highlight.pack.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - Highlight.js 10.0.1 (33af2ea5) - License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).hljs=n()}(this,(function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!n.hasOwnProperty(r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach((function(e){for(n in e)t[n]=e[n]})),t}function r(e){return e.nodeName.toLowerCase()}var a=Object.freeze({__proto__:null,escapeHTML:n,inherit:t,nodeStream:function(e){var n=[];return function e(t,a){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=e(i,a),r(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n},mergeStreams:function(e,t,a){var i=0,s="",o=[];function l(){return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||t.length;){var g=l();if(s+=n(a.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+n(a.substr(i))}});const i="",s=e=>!!e.kind;class o{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=n(e)}openNode(e){if(!s(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){s(e)&&(this.buffer+=i)}span(e){this.buffer+=``}value(){return this.buffer}}class l{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){let n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){e.children&&(e.children.every(e=>"string"==typeof e)?(e.text=e.children.join(""),delete e.children):e.children.forEach(e=>{"string"!=typeof e&&l._collapse(e)}))}}class c extends l{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){let t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new o(this,this.options).value()}finalize(){}}function u(e){return e&&e.source||e}const d="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",g={begin:"\\\\[\\s\\S]",relevance:0},h={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[g]},f={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[g]},p={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,r){var a=t({className:"comment",begin:e,end:n,contains:[]},r||{});return a.contains.push(p),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|XXX):",relevance:0}),a},b=m("//","$"),v=m("/\\*","\\*/"),x=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:d,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",BACKSLASH_ESCAPE:g,APOS_STRING_MODE:h,QUOTE_STRING_MODE:f,PHRASAL_WORDS_MODE:p,COMMENT:m,C_LINE_COMMENT_MODE:b,C_BLOCK_COMMENT_MODE:v,HASH_COMMENT_MODE:x,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:d,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^\/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[g,{begin:/\[/,end:/\]/,relevance:0,contains:[g]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0}}),E="of and for in not or if then".split(" ");function R(e,n){return n?+n:(t=e,E.includes(t.toLowerCase())?0:1);var t}const N=n,w=t,{nodeStream:y,mergeStreams:O}=a;return function(n){var r=[],a={},i={},s=[],o=!0,l=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,d="Could not find the language '{}', did you forget to load/include a language module?",g={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0,__emitter:c};function h(e){return g.noHighlightRe.test(e)}function f(e,n,t,r){var a={code:n,language:e};T("before:highlight",a);var i=a.result?a.result:p(a.language,a.code,t,r);return i.code=a.code,T("after:highlight",i),i}function p(e,n,r,i){var s=n;function l(e,n){var t=v.case_insensitive?n[0].toLowerCase():n[0];return e.keywords.hasOwnProperty(t)&&e.keywords[t]}function c(){null!=_.subLanguage?function(){if(""!==k){var e="string"==typeof _.subLanguage;if(!e||a[_.subLanguage]){var n=e?p(_.subLanguage,k,!0,E[_.subLanguage]):m(k,_.subLanguage.length?_.subLanguage:void 0);_.relevance>0&&(T+=n.relevance),e&&(E[_.subLanguage]=n.top),w.addSublanguage(n.emitter,n.language)}else w.addText(k)}}():function(){var e,n,t,r;if(_.keywords){for(n=0,_.lexemesRe.lastIndex=0,t=_.lexemesRe.exec(k),r="";t;){r+=k.substring(n,t.index);var a=null;(e=l(_,t))?(w.addText(r),r="",T+=e[1],a=e[0],w.addKeyword(t[0],a)):r+=t[0],n=_.lexemesRe.lastIndex,t=_.lexemesRe.exec(k)}r+=k.substr(n),w.addText(r)}else w.addText(k)}(),k=""}function h(e){e.className&&w.openNode(e.className),_=Object.create(e,{parent:{value:_}})}var f={};function b(n,t){var a,i=t&&t[0];if(k+=n,null==i)return c(),0;if("begin"==f.type&&"end"==t.type&&f.index==t.index&&""===i){if(k+=s.slice(t.index,t.index+1),!o)throw(a=Error("0 width match regex")).languageName=e,a.badRule=f.rule,a;return 1}if(f=t,"begin"===t.type)return function(e){var n=e[0],t=e.rule;return t.__onBegin&&(t.__onBegin(e)||{}).ignoreMatch?function(e){return 0===_.matcher.regexIndex?(k+=e[0],1):(A=!0,0)}(n):(t&&t.endSameAsBegin&&(t.endRe=RegExp(n.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),t.skip?k+=n:(t.excludeBegin&&(k+=n),c(),t.returnBegin||t.excludeBegin||(k=n)),h(t),t.returnBegin?0:n.length)}(t);if("illegal"===t.type&&!r)throw(a=Error('Illegal lexeme "'+i+'" for mode "'+(_.className||"")+'"')).mode=_,a;if("end"===t.type){var l=function(e){var n=e[0],t=s.substr(e.index),r=function e(n,t){if(function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(n.endRe,t)){for(;n.endsParent&&n.parent;)n=n.parent;return n}if(n.endsWithParent)return e(n.parent,t)}(_,t);if(r){var a=_;a.skip?k+=n:(a.returnEnd||a.excludeEnd||(k+=n),c(),a.excludeEnd&&(k=n));do{_.className&&w.closeNode(),_.skip||_.subLanguage||(T+=_.relevance),_=_.parent}while(_!==r.parent);return r.starts&&(r.endSameAsBegin&&(r.starts.endRe=r.endRe),h(r.starts)),a.returnEnd?0:n.length}}(t);if(null!=l)return l}return k+=i,i.length}var v=M(e);if(!v)throw console.error(d.replace("{}",e)),Error('Unknown language: "'+e+'"');!function(e){function n(n,t){return RegExp(u(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class r{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);let e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+="|"),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"==l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("==l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;let n=this.matcherRe.exec(e);if(!n)return null;let t=n.findIndex((e,n)=>n>0&&null!=e),r=this.matchIndexes[t];return Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];let n=new r;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){let n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;let t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e){let n=e.input[e.index-1],t=e.input[e.index+e[0].length];if("."===n||"."===t)return{ignoreMatch:!0}}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");!function r(s,o){s.compiled||(s.compiled=!0,s.__onBegin=null,s.keywords=s.keywords||s.beginKeywords,s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,R(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemesRe=n(s.lexemes||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__onBegin=i),s.begin||(s.begin=/\B|\b/),s.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(s.endRe=n(s.end)),s.terminator_end=u(s.end)||"",s.endsWithParent&&o.terminator_end&&(s.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(s.illegalRe=n(s.illegal)),null==s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return t(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?t(e,{starts:e.starts?t(e.starts):null}):Object.isFrozen(e)?t(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){r(e,s)})),s.starts&&r(s.starts,o),s.matcher=function(e){let n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(s))}(e)}(v);var x,_=i||v,E={},w=new g.__emitter(g);!function(){for(var e=[],n=_;n!==v;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>w.openNode(e))}();var y,O,k="",T=0,L=0;try{var A=!1;for(_.matcher.considerAll();A?A=!1:(_.matcher.lastIndex=L,_.matcher.considerAll()),y=_.matcher.exec(s);)O=b(s.substring(L,y.index),y),L=y.index+O;return b(s.substr(L)),w.closeAllNodes(),w.finalize(),x=w.toHTML(),{relevance:T,value:x,language:e,illegal:!1,emitter:w,top:_}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:s.slice(L-100,L+100),mode:n.mode},sofar:x,relevance:0,value:N(s),emitter:w};if(o)return{relevance:0,value:N(s),emitter:w,language:e,top:_,errorRaised:n};throw n}}function m(e,n){n=n||g.languages||Object.keys(a);var t=function(e){const n={relevance:0,emitter:new g.__emitter(g),value:N(e),illegal:!1,top:E};return n.emitter.addText(e),n}(e),r=t;return n.filter(M).filter(k).forEach((function(n){var a=p(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function b(e){return g.tabReplace||g.useBR?e.replace(l,(function(e,n){return g.useBR&&"\n"===e?"
":g.tabReplace?n.replace(/\t/g,g.tabReplace):""})):e}function v(e){var n,t,r,a,s,o=function(e){var n,t=e.className+" ";if(t+=e.parentNode?e.parentNode.className:"",n=g.languageDetectRe.exec(t)){var r=M(n[1]);return r||(console.warn(d.replace("{}",n[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?n[1]:"no-highlight"}return t.split(/\s+/).find(e=>h(e)||M(e))}(e);h(o)||(T("before:highlightBlock",{block:e,language:o}),g.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e,s=n.textContent,r=o?f(o,s,!0):m(s),(t=y(n)).length&&((a=document.createElement("div")).innerHTML=r.value,r.value=O(t,y(a),s)),r.value=b(r.value),T("after:highlightBlock",{block:e,result:r}),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?i[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.relevance},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.relevance}))}function x(){if(!x.called){x.called=!0;var e=document.querySelectorAll("pre code");r.forEach.call(e,v)}}const E={disableAutodetect:!0,name:"Plain text"};function M(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]}function k(e){var n=M(e);return n&&!n.disableAutodetect}function T(e,n){var t=e;s.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(n,{highlight:f,highlightAuto:m,fixMarkup:b,highlightBlock:v,configure:function(e){g=w(g,e)},initHighlighting:x,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",x,!1)},registerLanguage:function(e,t){var r;try{r=t(n)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!o)throw n;console.error(n),r=E}r.name||(r.name=e),a[e]=r,r.rawDefinition=t.bind(null,n),r.aliases&&r.aliases.forEach((function(n){i[n]=e}))},listLanguages:function(){return Object.keys(a)},getLanguage:M,requireLanguage:function(e){var n=M(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:k,inherit:w,addPlugin:function(e,n){s.push(e)}}),n.debugMode=function(){o=!1},n.safeMode=function(){o=!0},n.versionString="10.0.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(n,_),n}({})}));hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("java",function(){"use strict";return function(e){var a="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]};return{name:"Java",aliases:["jsp"],keywords:a,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:a,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:a,relevance:0,contains:[n,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0},n]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,lexemes:/[\w\.]+/,keywords:{keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("ini",function(){"use strict";return function(e){var n={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var s={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},i={className:"literal",begin:/\bon|off|true|false|yes|no\b/},t={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]};return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{begin:/^[a-z0-9\[\]_\.-]+(?=\s*=\s*)/,className:"attr",starts:{end:/$/,contains:[a,{begin:/\[/,end:/\]/,contains:[a,i,s,t,n,"self"],relevance:0},i,s,t,n]}}]}}}());hljs.registerLanguage("xml",function(){"use strict";return function(e){var n={className:"symbol",begin:"&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;"},a={begin:"\\s",contains:[{className:"meta-keyword",begin:"#?[a-z_][a-z1-9_-]+",illegal:"\\n"}]},s=e.inherit(a,{begin:"\\(",end:"\\)"}),t=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),i=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),c={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%/},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("coffeescript",function(){"use strict";return function(e){var n={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:n},a=[e.BINARY_NUMBER_MODE,e.inherit(e.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[e.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,e.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=a;var t=e.inherit(e.TITLE_MODE,{begin:i}),r={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:n,contains:["self"].concat(a)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:n,illegal:/\/\*/,contains:a.concat([e.COMMENT("###","###"),e.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[t,r]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[r]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[t]},t]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},{begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\((?:.|\n)*?\)\1"/}]},s={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},i={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},o=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+o,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:o,returnBegin:!0,contains:[c],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:i,strings:a,keywords:l}}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},lexemes:e.IDENT_RE+"!?",illegal:""}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("python",function(){"use strict";return function(e){var n={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10",built_in:"Ellipsis NotImplemented",literal:"False None True"},a={className:"meta",begin:/^(>>>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,lexemes:"[a-z/_]+",keywords:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler",lexemes:e.UNDERSCORE_IDENT_RE}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},{begin:/\w+/,endSameAsBegin:!0,contains:[e.BACKSLASH_ESCAPE,c]}]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("javascript",function(){"use strict";return function(e){var n={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},a="[A-Za-z$_][0-9A-Za-z$_]*",s={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},r={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:e.C_NUMBER_RE+"n?"}],relevance:0},i={className:"subst",begin:"\\$\\{",end:"\\}",keywords:s,contains:[]},t={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,i],subLanguage:"xml"}},c={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,i],subLanguage:"css"}},o={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,i]};i.contains=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,t,c,o,r,e.REGEXP_MODE];var l=i.contains.concat([e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]),d={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:l};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:s,contains:[{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},{className:"meta",begin:/^#!/,end:/$/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,t,c,o,e.C_LINE_COMMENT_MODE,e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:a+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,r,{begin:/[{,\n]\s*/,relevance:0,contains:[{begin:a+"\\s*:",returnBegin:!0,relevance:0,contains:[{className:"attr",begin:a,relevance:0}]}]},{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.REGEXP_MODE,{className:"function",begin:"(\\(.*?\\)|"+a+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:a},{begin:/\(\s*\)/},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:l}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:n.begin,end:n.end}],subLanguage:"xml",contains:[{begin:n.begin,end:n.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:a}),d],illegal:/\[|%/},{begin:/\$[(.]/},e.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s*(?="+a+"\\()",end:/{/,keywords:"get set",contains:[e.inherit(e.TITLE_MODE,{begin:a}),{begin:/\(\)/},d]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("diff",function(){"use strict";return function(e){return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{begin:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{begin:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{className:"comment",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\-{3}/,end:/$/},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/}]},{className:"addition",begin:"^\\+",end:"$"},{className:"deletion",begin:"^\\-",end:"$"},{className:"addition",begin:"^\\!",end:"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",_={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,{begin:"<",end:">",keywords:"in out"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+E+"\\s+)+"+e.IDENT_RE+"\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},_]}}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],lexemes:/[\w\.]+/,keywords:n,contains:a}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const n={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},t={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,n]};n.contains.push(t);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]};return{name:"Bash",aliases:["sh","zsh"],lexemes:/\b-?[a-z\._]+\b/,keywords:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[{className:"meta",begin:/^#![^\n]+sh\s*$/,relevance:10},{className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0},a,e.HASH_COMMENT_MODE,t,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_="@interface @class @protocol @implementation";return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},lexemes:n,illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,lexemes:n,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("typescript",function(){"use strict";return function(e){var n={keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class public private protected get set super static implements enum export import declare type namespace abstract as from extends async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document any number boolean string void Promise"},r={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},a={begin:"\\(",end:/\)/,keywords:n,contains:["self",e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.NUMBER_MODE]},t={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,r,a]},s={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:e.C_NUMBER_RE+"n?"}],relevance:0},i={className:"subst",begin:"\\$\\{",end:"\\}",keywords:n,contains:[]},o={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,i],subLanguage:"xml"}},c={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,i],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,i]};return i.contains=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,o,c,E,s,e.REGEXP_MODE],{name:"TypeScript",aliases:["ts"],keywords:n,contains:[{className:"meta",begin:/^\s*['"]use strict['"]/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,o,c,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.REGEXP_MODE,{className:"function",begin:"(\\(.*?\\)|"+e.IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.IDENT_RE},{begin:/\(\s*\)/},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:n,contains:["self",e.inherit(e.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),t],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",t]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+e.IDENT_RE,relevance:0},r,a]}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]};return{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!"+e.UNDERSCORE_IDENT_RE},{className:"type",begin:"!!"+e.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:"true false yes no null",keywords:{literal:"true false yes no null"}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},n]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}()); \ No newline at end of file diff --git a/app/src/main/assets/highlightjs/highlightjs-line-numbers.min.js b/app/src/main/assets/highlightjs/highlightjs-line-numbers.min.js deleted file mode 100644 index 6d0a3cbe..00000000 --- a/app/src/main/assets/highlightjs/highlightjs-line-numbers.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){"use strict";function t(){"complete"===document.readyState?n():e.addEventListener("DOMContentLoaded",n)}function n(){try{var e=document.querySelectorAll("code.hljs");for(var t in e)e.hasOwnProperty(t)&&r(e[t])}catch(n){console.error("LineNumbers error: ",n)}}function r(e){if("object"==typeof e){var t=e.parentNode,n=o(t.textContent);if(n>1){for(var r="",c=0;n>c;c++)r+=c+1+"\n";var l=document.createElement("code");l.className="hljs hljs-line-numbers",l.style["float"]="left",l.textContent=r,t.insertBefore(l,e)}}}function o(e){if(0===e.length)return 0;var t=/\r\n|\r|\n/g,n=e.match(t);return n=n?n.length:0,e[e.length-1].match(t)||(n+=1),n}"undefined"==typeof e.hljs?console.error("highlight.js not detected!"):(e.hljs.initLineNumbersOnLoad=t,e.hljs.lineNumbersBlock=r)}(window); \ No newline at end of file diff --git a/app/src/main/assets/highlightjs/themes/androidstudio.css b/app/src/main/assets/highlightjs/themes/androidstudio.css deleted file mode 100644 index bc8e473b..00000000 --- a/app/src/main/assets/highlightjs/themes/androidstudio.css +++ /dev/null @@ -1,66 +0,0 @@ -/* -Date: 24 Fev 2015 -Author: Pedro Oliveira -*/ - -.hljs { - color: #a9b7c6; - background: #282b2e; - display: block; - overflow-x: auto; - padding: 0.5em; -} - -.hljs-number, -.hljs-literal, -.hljs-symbol, -.hljs-bullet { - color: #6897BB; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-deletion { - color: #cc7832; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-link { - color: #629755; -} - -.hljs-comment, -.hljs-quote { - color: #808080; -} - -.hljs-meta { - color: #bbb529; -} - -.hljs-string, -.hljs-attribute, -.hljs-addition { - color: #6A8759; -} - -.hljs-section, -.hljs-title, -.hljs-type { - color: #ffc66d; -} - -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #e8bf6a; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/app/src/main/assets/highlightjs/themes/arduino-light.css b/app/src/main/assets/highlightjs/themes/arduino-light.css deleted file mode 100644 index e6039c36..00000000 --- a/app/src/main/assets/highlightjs/themes/arduino-light.css +++ /dev/null @@ -1,87 +0,0 @@ -/* - -Arduino® Light Theme - Stefania Mellai - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #FFFFFF; -} - -.hljs, -.hljs-subst { - color: #434f54; -} - -.hljs-keyword, -.hljs-attribute, -.hljs-selector-tag, -.hljs-doctag, -.hljs-name { - color: #00979D; -} - -.hljs-built_in, -.hljs-literal, -.hljs-bullet, -.hljs-code, -.hljs-addition { - color: #D35400; -} - -.hljs-regexp, -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #00979D; -} - -.hljs-type, -.hljs-string, -.hljs-selector-id, -.hljs-selector-class, -.hljs-quote, -.hljs-template-tag, -.hljs-deletion { - color: #005C5F; -} - -.hljs-title, -.hljs-section { - color: #880000; - font-weight: bold; -} - -.hljs-comment { - color: rgba(149,165,166,.8); -} - -.hljs-meta-keyword { - color: #728E00; -} - -.hljs-meta { - color: #434f54; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-function { - color: #728E00; -} - -.hljs-number { - color: #8A7B52; -} diff --git a/app/src/main/assets/highlightjs/themes/far.css b/app/src/main/assets/highlightjs/themes/far.css deleted file mode 100644 index 2b3f87b5..00000000 --- a/app/src/main/assets/highlightjs/themes/far.css +++ /dev/null @@ -1,71 +0,0 @@ -/* - -FAR Style (c) MajestiC - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000080; -} - -.hljs, -.hljs-subst { - color: #0ff; -} - -.hljs-string, -.hljs-attribute, -.hljs-symbol, -.hljs-bullet, -.hljs-built_in, -.hljs-builtin-name, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition { - color: #ff0; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-type, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-variable { - color: #fff; -} - -.hljs-comment, -.hljs-quote, -.hljs-doctag, -.hljs-deletion { - color: #888; -} - -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-link { - color: #0f0; -} - -.hljs-meta { - color: #008080; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-title, -.hljs-section, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/app/src/main/assets/highlightjs/themes/github.css b/app/src/main/assets/highlightjs/themes/github.css deleted file mode 100644 index 791932b8..00000000 --- a/app/src/main/assets/highlightjs/themes/github.css +++ /dev/null @@ -1,99 +0,0 @@ -/* - -github.com style (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #998; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal, -.hljs-variable, -.hljs-template-variable, -.hljs-tag .hljs-attr { - color: #008080; -} - -.hljs-string, -.hljs-doctag { - color: #d14; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #900; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-type, -.hljs-class .hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-regexp, -.hljs-link { - color: #009926; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/app/src/main/assets/highlightjs/themes/ir-black.css b/app/src/main/assets/highlightjs/themes/ir-black.css deleted file mode 100644 index bd4c755e..00000000 --- a/app/src/main/assets/highlightjs/themes/ir-black.css +++ /dev/null @@ -1,73 +0,0 @@ -/* - IR_Black style (c) Vasily Mikhailitchenko -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000; - color: #f8f8f8; -} - -.hljs-comment, -.hljs-quote, -.hljs-meta { - color: #7c7c7c; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-tag, -.hljs-name { - color: #96cbfe; -} - -.hljs-attribute, -.hljs-selector-id { - color: #ffffb6; -} - -.hljs-string, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition { - color: #a8ff60; -} - -.hljs-subst { - color: #daefa3; -} - -.hljs-regexp, -.hljs-link { - color: #e9c062; -} - -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-doctag { - color: #ffffb6; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-variable, -.hljs-template-variable, -.hljs-literal { - color: #c6c5fe; -} - -.hljs-number, -.hljs-deletion { - color:#ff73fd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/app/src/main/assets/highlightjs/themes/monokai-sublime.css b/app/src/main/assets/highlightjs/themes/monokai-sublime.css deleted file mode 100644 index 2864170d..00000000 --- a/app/src/main/assets/highlightjs/themes/monokai-sublime.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #23241f; -} - -.hljs, -.hljs-tag, -.hljs-subst { - color: #f8f8f2; -} - -.hljs-strong, -.hljs-emphasis { - color: #a8a8a2; -} - -.hljs-bullet, -.hljs-quote, -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-link { - color: #ae81ff; -} - -.hljs-code, -.hljs-title, -.hljs-section, -.hljs-selector-class { - color: #a6e22e; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-name, -.hljs-attr { - color: #f92672; -} - -.hljs-symbol, -.hljs-attribute { - color: #66d9ef; -} - -.hljs-params, -.hljs-class .hljs-title { - color: #f8f8f2; -} - -.hljs-string, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition, -.hljs-variable, -.hljs-template-variable { - color: #e6db74; -} - -.hljs-comment, -.hljs-deletion, -.hljs-meta { - color: #75715e; -} diff --git a/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java b/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java index 308a3f40..8c5e2946 100644 --- a/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java +++ b/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java @@ -7,7 +7,6 @@ import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.TinyDB; import java.io.IOException; import java.util.Date; -import okhttp3.ResponseBody; import retrofit2.Call; /** @@ -35,7 +34,7 @@ public class NotificationsActions { public void setNotificationStatus(NotificationThread notificationThread, NotificationStatus notificationStatus) throws IOException { - Call call = RetrofitClient.getApiInterface(context) + Call call = RetrofitClient.getApiInterface(context) .markNotificationThreadAsRead(instanceToken, notificationThread.getId(), notificationStatus.name()); if(!call.execute().isSuccessful()) { @@ -46,7 +45,7 @@ public class NotificationsActions { public boolean setAllNotificationsRead(Date date) throws IOException { - Call call = RetrofitClient.getApiInterface(context) + Call call = RetrofitClient.getApiInterface(context) .markNotificationThreadsAsRead(instanceToken, AppUtil.getTimestampFromDate(context, date), true, new String[]{"unread", "pinned"}, "read"); diff --git a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java index fcec209f..c5642a6c 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java @@ -7,6 +7,7 @@ import org.mian.gitnex.R; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.TimeHelper; import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.notifications.Notifications; /** * Author M M Arif @@ -77,6 +78,8 @@ public abstract class BaseActivity extends AppCompatActivity { } AppUtil.setAppLocale(getResources(), tinyDB.getString("locale")); + + Notifications.startWorker(appCtx); } } diff --git a/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java b/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java index db4a661d..75c27cda 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CommitsActivity.java @@ -24,7 +24,7 @@ import org.mian.gitnex.adapters.CommitsAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityCommitsBinding; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; @@ -43,7 +43,7 @@ public class CommitsActivity extends BaseActivity { private TextView noData; private ProgressBar progressBar; private String TAG = "CommitsActivity"; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private int pageSize = 1; private RecyclerView recyclerView; @@ -85,7 +85,7 @@ public class CommitsActivity extends BaseActivity { // if gitea is 1.12 or higher use the new limit (resultLimitNewGiteaInstances) if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.12")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } recyclerView = activityCommitsBinding.recyclerView; diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java index f8cf0c8f..3ce413a5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java @@ -8,10 +8,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import com.google.gson.JsonElement; @@ -38,16 +34,7 @@ import retrofit2.Callback; public class CreateFileActivity extends BaseActivity { - public ImageView closeActivity; - private View.OnClickListener onClickListener; - private Button newFileCreate; - - private EditText newFileName; - private EditText newFileContent; - private EditText newFileCommitMessage; - private AutoCompleteTextView newFileBranches; - private String filePath; - private String fileSha; + private ActivityCreateFileBinding binding; public static final int FILE_ACTION_CREATE = 0; public static final int FILE_ACTION_DELETE = 1; @@ -55,6 +42,9 @@ public class CreateFileActivity extends BaseActivity { private int fileAction = FILE_ACTION_CREATE; + private String filePath; + private String fileSha; + private final List branches = new ArrayList<>(); private String repoOwner; @@ -66,154 +56,127 @@ public class CreateFileActivity extends BaseActivity { super.onCreate(savedInstanceState); - ActivityCreateFileBinding activityCreateFileBinding = ActivityCreateFileBinding.inflate(getLayoutInflater()); - setContentView(activityCreateFileBinding.getRoot()); - - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + binding = ActivityCreateFileBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); String repoFullName = tinyDB.getString("repoFullName"); String[] parts = repoFullName.split("/"); repoOwner = parts[0]; repoName = parts[1]; - closeActivity = activityCreateFileBinding.close; - newFileName = activityCreateFileBinding.newFileName; - newFileContent = activityCreateFileBinding.newFileContent; - newFileCommitMessage = activityCreateFileBinding.newFileCommitMessage; - TextView toolbarTitle = activityCreateFileBinding.toolbarTitle; + TextView toolbarTitle = binding.toolbarTitle; - newFileName.requestFocus(); - assert imm != null; - imm.showSoftInput(newFileName, InputMethodManager.SHOW_IMPLICIT); + binding.newFileName.requestFocus(); - initCloseListener(); + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + assert inputMethodManager != null; + inputMethodManager.showSoftInput(binding.newFileName, InputMethodManager.SHOW_IMPLICIT); - closeActivity.setOnClickListener(onClickListener); - - newFileCreate = activityCreateFileBinding.newFileCreate; - newFileContent.setOnTouchListener((touchView, motionEvent) -> { + binding.close.setOnClickListener(view -> finish()); + binding.newFileContent.setOnTouchListener((touchView, motionEvent) -> { touchView.getParent().requestDisallowInterceptTouchEvent(true); - if ((motionEvent.getAction() & MotionEvent.ACTION_UP) != 0 && (motionEvent.getActionMasked() & MotionEvent.ACTION_UP) != 0) { + if ((motionEvent.getAction() & MotionEvent.ACTION_UP) != 0 && + (motionEvent.getActionMasked() & MotionEvent.ACTION_UP) != 0) { touchView.getParent().requestDisallowInterceptTouchEvent(false); } + return false; + }); if(getIntent().getStringExtra("filePath") != null && getIntent().getIntExtra("fileAction", FILE_ACTION_DELETE) == FILE_ACTION_DELETE) { fileAction = getIntent().getIntExtra("fileAction", FILE_ACTION_DELETE); - filePath = getIntent().getStringExtra("filePath"); - String fileContents = getIntent().getStringExtra("fileContents"); fileSha = getIntent().getStringExtra("fileSha"); toolbarTitle.setText(getString(R.string.deleteFileText, filePath)); - newFileCreate.setText(R.string.deleteFile); - newFileName.setText(filePath); - newFileName.setEnabled(false); - newFileName.setFocusable(false); + binding.newFileCreate.setText(R.string.deleteFile); - newFileContent.setText(fileContents); - newFileContent.setEnabled(false); - newFileContent.setFocusable(false); + binding.newFileNameLayout.setVisibility(View.GONE); + binding.newFileContentLayout.setVisibility(View.GONE); } if(getIntent().getStringExtra("filePath") != null && getIntent().getIntExtra("fileAction", FILE_ACTION_EDIT) == FILE_ACTION_EDIT) { fileAction = getIntent().getIntExtra("fileAction", FILE_ACTION_EDIT); - filePath = getIntent().getStringExtra("filePath"); - String fileContents = getIntent().getStringExtra("fileContents"); fileSha = getIntent().getStringExtra("fileSha"); toolbarTitle.setText(getString(R.string.editFileText, filePath)); - newFileCreate.setText(R.string.editFile); - newFileName.setText(filePath); - newFileName.setEnabled(false); - newFileName.setFocusable(false); + binding.newFileCreate.setText(R.string.editFile); + binding.newFileName.setText(filePath); + binding.newFileName.setEnabled(false); + binding.newFileName.setFocusable(false); - newFileContent.setText(fileContents); + binding.newFileContent.setText(getIntent().getStringExtra("fileContents")); } - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); - - newFileBranches = activityCreateFileBinding.newFileBranches; getBranches(repoOwner, repoName); disableProcessButton(); NetworkStatusObserver networkStatusObserver = NetworkStatusObserver.getInstance(ctx); - networkStatusObserver.registerNetworkStatusListener(hasNetworkConnection -> newFileCreate.setEnabled(hasNetworkConnection)); + networkStatusObserver.registerNetworkStatusListener(binding.newFileCreate::setEnabled); - newFileCreate.setOnClickListener(createFileListener); + binding.newFileCreate.setOnClickListener(v -> processNewFile()); } - private final View.OnClickListener createFileListener = v -> processNewFile(); - private void processNewFile() { - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); - - String newFileName_ = newFileName.getText().toString(); - String newFileContent_ = newFileContent.getText().toString(); - String newFileBranchName_ = newFileBranches.getText().toString(); - String newFileCommitMessage_ = newFileCommitMessage.getText().toString(); - - if(!connToInternet) { + String newFileName = binding.newFileName.getText() != null ? binding.newFileName.getText().toString() : ""; + String newFileContent = binding.newFileContent.getText() != null ? binding.newFileContent.getText().toString() : ""; + String newFileBranchName = binding.newFileBranches.getText() != null ? binding.newFileBranches.getText().toString() : ""; + String newFileCommitMessage = binding.newFileCommitMessage.getText() != null ? binding.newFileCommitMessage.getText().toString() : ""; + if(!AppUtil.hasNetworkConnection(appCtx)) { Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); return; } - if(newFileName_.equals("") || newFileContent_.equals("") || newFileCommitMessage_.equals("")) { - + if(((newFileName.isEmpty() || newFileContent.isEmpty()) && fileAction != FILE_ACTION_DELETE) || newFileCommitMessage.isEmpty()) { Toasty.error(ctx, getString(R.string.newFileRequiredFields)); return; } - if(!AppUtil.checkStringsWithDash(newFileBranchName_)) { - + if(!AppUtil.checkStringsWithDash(newFileBranchName)) { Toasty.error(ctx, getString(R.string.newFileInvalidBranchName)); return; } - if(newFileCommitMessage_.length() > 255) { - + if(newFileCommitMessage.length() > 255) { Toasty.warning(ctx, getString(R.string.newFileCommitMessageError)); + return; } - else { - disableProcessButton(); + disableProcessButton(); - switch(fileAction) { + switch(fileAction) { - case FILE_ACTION_CREATE: - createNewFile(Authorization.get(ctx), repoOwner, repoName, newFileName_, AppUtil.encodeBase64(newFileContent_), newFileCommitMessage_, newFileBranchName_); - break; + case FILE_ACTION_CREATE: + createNewFile(repoOwner, repoName, newFileName, AppUtil.encodeBase64(newFileContent), newFileCommitMessage, newFileBranchName); + break; - case FILE_ACTION_DELETE: - deleteFile(Authorization.get(ctx), repoOwner, repoName, filePath, newFileCommitMessage_, newFileBranchName_, fileSha); - break; + case FILE_ACTION_DELETE: + deleteFile(repoOwner, repoName, filePath, newFileCommitMessage, newFileBranchName, fileSha); + break; - case FILE_ACTION_EDIT: - editFile(Authorization.get(ctx), repoOwner, repoName, filePath, - AppUtil.encodeBase64(newFileContent_), newFileCommitMessage_, newFileBranchName_, fileSha); - break; + case FILE_ACTION_EDIT: + editFile(repoOwner, repoName, filePath, AppUtil.encodeBase64(newFileContent), newFileCommitMessage, newFileBranchName, fileSha); + break; - } } } - private void createNewFile(final String token, String repoOwner, String repoName, String fileName, String fileContent, String fileCommitMessage, String branchName) { + private void createNewFile(String repoOwner, String repoName, String fileName, String fileContent, String fileCommitMessage, String branchName) { NewFile createNewFileJsonStr = branches.contains(branchName) ? new NewFile(branchName, fileContent, fileCommitMessage, "") : @@ -221,7 +184,7 @@ public class CreateFileActivity extends BaseActivity { Call call = RetrofitClient .getApiInterface(ctx) - .createNewFile(token, repoOwner, repoName, fileName, createNewFileJsonStr); + .createNewFile(Authorization.get(ctx), repoOwner, repoName, fileName, createNewFileJsonStr); call.enqueue(new Callback() { @@ -268,7 +231,7 @@ public class CreateFileActivity extends BaseActivity { } - private void deleteFile(final String token, String repoOwner, String repoName, String fileName, String fileCommitMessage, String branchName, String fileSha) { + private void deleteFile(String repoOwner, String repoName, String fileName, String fileCommitMessage, String branchName, String fileSha) { DeleteFile deleteFileJsonStr = branches.contains(branchName) ? new DeleteFile(branchName, fileCommitMessage, "", fileSha) : @@ -276,42 +239,42 @@ public class CreateFileActivity extends BaseActivity { Call call = RetrofitClient .getApiInterface(ctx) - .deleteFile(token, repoOwner, repoName, fileName, deleteFileJsonStr); + .deleteFile(Authorization.get(ctx), repoOwner, repoName, fileName, deleteFileJsonStr); call.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { - if(response.code() == 200) { + switch(response.code()) { - enableProcessButton(); - Toasty.info(ctx, getString(R.string.deleteFileMessage, tinyDB.getString("repoBranch"))); - getIntent().removeExtra("filePath"); - getIntent().removeExtra("fileSha"); - getIntent().removeExtra("fileContents"); - finish(); - } - else if(response.code() == 401) { + case 200: + enableProcessButton(); + Toasty.info(ctx, getString(R.string.deleteFileMessage, tinyDB.getString("repoBranch"))); + getIntent().removeExtra("filePath"); + getIntent().removeExtra("fileSha"); + getIntent().removeExtra("fileContents"); + finish(); + break; - enableProcessButton(); - AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), - getResources().getString(R.string.alertDialogTokenRevokedMessage), - getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), - getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); - } - else { - - if(response.code() == 404) { + case 401: + enableProcessButton(); + AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), + getResources().getString(R.string.alertDialogTokenRevokedMessage), + getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), + getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + break; + case 404: enableProcessButton(); Toasty.info(ctx, getString(R.string.apiNotFound)); - } - else { + break; + default: enableProcessButton(); Toasty.info(ctx, getString(R.string.genericError)); - } + break; + } } @@ -325,7 +288,7 @@ public class CreateFileActivity extends BaseActivity { } - private void editFile(final String token, String repoOwner, String repoName, String fileName, String fileContent, String fileCommitMessage, String branchName, String fileSha) { + private void editFile(String repoOwner, String repoName, String fileName, String fileContent, String fileCommitMessage, String branchName, String fileSha) { EditFile editFileJsonStr = branches.contains(branchName) ? new EditFile(branchName, fileCommitMessage, "", fileSha, fileContent) : @@ -333,7 +296,7 @@ public class CreateFileActivity extends BaseActivity { Call call = RetrofitClient .getApiInterface(ctx) - .editFile(token, repoOwner, repoName, fileName, editFileJsonStr); + .editFile(Authorization.get(ctx), repoOwner, repoName, fileName, editFileJsonStr); call.enqueue(new Callback() { @@ -402,8 +365,8 @@ public class CreateFileActivity extends BaseActivity { ArrayAdapter adapter = new ArrayAdapter<>(CreateFileActivity.this, R.layout.list_spinner_items, branches); - newFileBranches.setAdapter(adapter); - newFileBranches.setText(tinyDB.getString("repoBranch"), false); + binding.newFileBranches.setAdapter(adapter); + binding.newFileBranches.setText(tinyDB.getString("repoBranch"), false); enableProcessButton(); @@ -419,19 +382,7 @@ public class CreateFileActivity extends BaseActivity { } - private void initCloseListener() { - - onClickListener = view -> finish(); - } - - private void disableProcessButton() { - - newFileCreate.setEnabled(false); - } - - private void enableProcessButton() { - - newFileCreate.setEnabled(true); - } + private void disableProcessButton() { binding.newFileCreate.setEnabled(false); } + private void enableProcessButton() { binding.newFileCreate.setEnabled(true); } } diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java index 12b0f449..aa1e9d46 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java @@ -30,7 +30,7 @@ import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; @@ -51,7 +51,7 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis private CustomLabelsSelectionDialogBinding labelsBinding; private CustomAssigneesSelectionDialogBinding assigneesBinding; private View.OnClickListener onClickListener; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private Dialog dialogLabels; private Dialog dialogAssignees; private String labelsSetter; @@ -93,7 +93,7 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis // require gitea 1.12 or higher if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } viewBinding.newIssueTitle.requestFocus(); diff --git a/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java index a274d2ff..5c7256ed 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java @@ -24,13 +24,12 @@ import org.mian.gitnex.databinding.ActivityCreatePrBinding; import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; @@ -43,7 +42,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis private View.OnClickListener onClickListener; private ActivityCreatePrBinding viewBinding; private CustomLabelsSelectionDialogBinding labelsBinding; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private Dialog dialogLabels; private String labelsSetter; private List labelsIds = new ArrayList<>(); @@ -80,7 +79,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis // require gitea 1.12 or higher if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } viewBinding.prBody.setOnTouchListener((touchView, motionEvent) -> { @@ -165,14 +164,14 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis CreatePullRequest createPullRequest = new CreatePullRequest(prTitle, prDescription, loginUid, mergeInto, pullFrom, milestoneId, dueDate, assignees, labelsIds); - Call transferCall = RetrofitClient + Call transferCall = RetrofitClient .getApiInterface(appCtx) .createPullRequest(instanceToken, repoOwner, repoName, createPullRequest); - transferCall.enqueue(new Callback() { + transferCall.enqueue(new Callback() { @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { disableProcessButton(); @@ -199,7 +198,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis } @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { + public void onFailure(@NonNull Call call, @NonNull Throwable t) { enableProcessButton(); Toasty.error(ctx, getString(R.string.genericServerResponseError)); diff --git a/app/src/main/java/org/mian/gitnex/activities/EditIssueActivity.java b/app/src/main/java/org/mian/gitnex/activities/EditIssueActivity.java index 9e813b5e..5ec7dea8 100644 --- a/app/src/main/java/org/mian/gitnex/activities/EditIssueActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/EditIssueActivity.java @@ -27,7 +27,7 @@ import org.mian.gitnex.databinding.ActivityEditIssueBinding; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.text.DateFormat; @@ -45,7 +45,7 @@ import retrofit2.Callback; public class EditIssueActivity extends BaseActivity implements View.OnClickListener { private View.OnClickListener onClickListener; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private EditText editIssueTitle; private EditText editIssueDescription; @@ -93,7 +93,7 @@ public class EditIssueActivity extends BaseActivity implements View.OnClickListe // if gitea is 1.12 or higher use the new limit if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } editIssueTitle.requestFocus(); diff --git a/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java b/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java index b24ded72..a186dc06 100644 --- a/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java @@ -1,13 +1,11 @@ package org.mian.gitnex.activities; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.NonNull; import androidx.appcompat.widget.Toolbar; import org.gitnex.tea4j.models.FileDiffView; import org.mian.gitnex.R; @@ -15,6 +13,7 @@ import org.mian.gitnex.adapters.FilesDiffAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityFileDiffBinding; import org.mian.gitnex.helpers.AlertDialogs; +import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.ParseDiff; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; @@ -23,7 +22,7 @@ import java.io.IOException; import java.util.List; import okhttp3.ResponseBody; import retrofit2.Call; -import retrofit2.Callback; +import retrofit2.Response; /** * Author M M Arif @@ -52,8 +51,6 @@ public class FileDiffActivity extends BaseActivity { String[] parts = repoFullName.split("/"); final String repoOwner = parts[0]; final String repoName = parts[1]; - final String loginUid = tinyDb.getString("loginUid"); - final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); ImageView closeActivity = activityFileDiffBinding.close; toolbarTitle = activityFileDiffBinding.toolbarTitle; @@ -71,80 +68,69 @@ public class FileDiffActivity extends BaseActivity { String pullIndex = tinyDb.getString("issueNumber"); boolean apiCall = !new Version(tinyDb.getString("giteaVersion")).less("1.13.0"); - getPullDiffContent(repoOwner, repoName, pullIndex, instanceToken, apiCall); + getPullDiffContent(repoOwner, repoName, pullIndex, apiCall); } - private void getPullDiffContent(String owner, String repo, String pullIndex, String token, boolean apiCall) { + private void getPullDiffContent(String owner, String repo, String pullIndex, boolean apiCall) { - Call call; - if(apiCall) { + Thread thread = new Thread(() -> { - call = RetrofitClient.getApiInterface(ctx).getPullDiffContent(token, owner, repo, pullIndex); - } - else { + Call call = apiCall ? + RetrofitClient.getApiInterface(ctx).getPullDiffContent(Authorization.get(ctx), owner, repo, pullIndex) : + RetrofitClient.getWebInterface(ctx).getPullDiffContent(Authorization.getWeb(ctx), owner, repo, pullIndex); - call = RetrofitClient.getWebInterface(ctx).getPullDiffContent(owner, repo, pullIndex); - } + try { - call.enqueue(new Callback() { + Response response = call.execute(); + assert response.body() != null; - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + switch(response.code()) { - if(response.code() == 200) { + case 200: + List fileDiffViews = ParseDiff.getFileDiffViewArray(response.body().string()); - try { + int filesCount = fileDiffViews.size(); - assert response.body() != null; + String toolbarTitleText = (filesCount > 1) ? + getResources().getString(R.string.fileDiffViewHeader, Integer.toString(filesCount)) : + getResources().getString(R.string.fileDiffViewHeaderSingle, Integer.toString(filesCount)); - List fileContentsArray = ParseDiff.getFileDiffViewArray(response.body().string()); + FilesDiffAdapter adapter = new FilesDiffAdapter(ctx, getSupportFragmentManager(), fileDiffViews); - int filesCount = fileContentsArray.size(); - if(filesCount > 1) { + runOnUiThread(() -> { + toolbarTitle.setText(toolbarTitleText); + mListView.setAdapter(adapter); + mProgressBar.setVisibility(View.GONE); + }); + break; - toolbarTitle.setText(getResources().getString(R.string.fileDiffViewHeader, Integer.toString(filesCount))); - } - else { + case 401: + AlertDialogs.authorizationTokenRevokedDialog(ctx, + getString(R.string.alertDialogTokenRevokedTitle), + getString(R.string.alertDialogTokenRevokedMessage), + getString(R.string.alertDialogTokenRevokedCopyNegativeButton), + getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + break; - toolbarTitle.setText(getResources().getString(R.string.fileDiffViewHeaderSingle, Integer.toString(filesCount))); - } + case 403: + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + break; - FilesDiffAdapter adapter = new FilesDiffAdapter(ctx, getSupportFragmentManager(), fileContentsArray); - mListView.setAdapter(adapter); + case 404: + Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + break; - mProgressBar.setVisibility(View.GONE); - } - catch(IOException e) { + default: + Toasty.error(ctx, getString(R.string.labelGeneralError)); - e.printStackTrace(); - } } - else if(response.code() == 401) { + } catch(IOException ignored) {} - AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), getResources().getString(R.string.alertDialogTokenRevokedMessage), getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); - } - else if(response.code() == 403) { - - Toasty.error(ctx, ctx.getString(R.string.authorizeError)); - } - else if(response.code() == 404) { - - Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); - } - else { - - Toasty.error(ctx, getString(R.string.labelGeneralError)); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - } }); + thread.start(); + } private void initCloseListener() { @@ -153,6 +139,7 @@ public class FileDiffActivity extends BaseActivity { getIntent().removeExtra("singleFileName"); finish(); + }; } diff --git a/app/src/main/java/org/mian/gitnex/activities/FileViewActivity.java b/app/src/main/java/org/mian/gitnex/activities/FileViewActivity.java index e4e5c35d..02a7d140 100644 --- a/app/src/main/java/org/mian/gitnex/activities/FileViewActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/FileViewActivity.java @@ -1,29 +1,21 @@ package org.mian.gitnex.activities; import android.app.Activity; +import android.app.NotificationManager; +import android.content.Context; import android.content.Intent; import android.graphics.Typeface; -import android.net.Uri; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; -import android.util.Base64; -import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; -import com.github.barteksc.pdfviewer.PDFView; +import androidx.core.app.NotificationCompat; import com.github.barteksc.pdfviewer.util.FitPolicy; -import com.github.chrisbanes.photoview.PhotoView; import com.vdurmont.emoji.EmojiParser; import org.apache.commons.io.FileUtils; import org.gitnex.tea4j.models.Files; @@ -33,19 +25,18 @@ import org.mian.gitnex.databinding.ActivityFileViewBinding; import org.mian.gitnex.fragments.BottomSheetFileViewerFragment; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Images; import org.mian.gitnex.helpers.Markdown; import org.mian.gitnex.helpers.Toasty; -import org.mian.gitnex.helpers.highlightjs.HighlightJsView; -import org.mian.gitnex.helpers.highlightjs.models.Theme; -import java.io.File; +import org.mian.gitnex.notifications.Notifications; import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Objects; +import java.util.Arrays; +import okhttp3.ResponseBody; import retrofit2.Call; -import retrofit2.Callback; +import retrofit2.Response; /** * Author M M Arif @@ -53,74 +44,38 @@ import retrofit2.Callback; public class FileViewActivity extends BaseActivity implements BottomSheetFileViewerFragment.BottomSheetListener { - private View.OnClickListener onClickListener; - private TextView singleFileContents; - private LinearLayout singleFileContentsFrame; - private HighlightJsView singleCodeContents; - private PhotoView imageView; - private ProgressBar mProgressBar; - private byte[] imageData; - private PDFView pdfView; - private LinearLayout pdfViewFrame; - private byte[] decodedPdf; + private ActivityFileViewBinding binding; private Boolean pdfNightMode; - private String singleFileName; - private String fileSha; + + private Files file; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityFileViewBinding activityFileViewBinding = ActivityFileViewBinding.inflate(getLayoutInflater()); - setContentView(activityFileViewBinding.getRoot()); + binding = ActivityFileViewBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); - Toolbar toolbar = activityFileViewBinding.toolbar; - setSupportActionBar(toolbar); + setSupportActionBar(binding.toolbar); + + tinyDB.putBoolean("enableMarkdownInFileView", false); + + file = (Files) getIntent().getSerializableExtra("file"); + + binding.close.setOnClickListener(view -> finish()); + + binding.toolbarTitle.setMovementMethod(new ScrollingMovementMethod()); + binding.toolbarTitle.setText(file.getPath()); String repoFullName = tinyDB.getString("repoFullName"); String repoBranch = tinyDB.getString("repoBranch"); String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - final String loginUid = tinyDB.getString("loginUid"); - final String instanceToken = "token " + tinyDB.getString(loginUid + "-token"); + String repoOwner = parts[0]; + String repoName = parts[1]; - tinyDB.putBoolean("enableMarkdownInFileView", false); + getSingleFileContents(repoOwner, repoName, file.getPath(), repoBranch); - ImageView closeActivity = activityFileViewBinding.close; - singleFileContents = activityFileViewBinding.singleFileContents; - singleCodeContents = activityFileViewBinding.singleCodeContents; - imageView = activityFileViewBinding.imageView; - mProgressBar = activityFileViewBinding.progressBar; - pdfView = activityFileViewBinding.pdfView; - pdfViewFrame = activityFileViewBinding.pdfViewFrame; - singleFileContentsFrame = activityFileViewBinding.singleFileContentsFrame; - - singleFileName = getIntent().getStringExtra("singleFileName"); - - TextView toolbar_title = activityFileViewBinding.toolbarTitle; - toolbar_title.setMovementMethod(new ScrollingMovementMethod()); - - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); - - tinyDB.putString("downloadFileContents", ""); - - try { - - singleFileName = URLDecoder.decode(singleFileName, "UTF-8"); - singleFileName = singleFileName.replaceAll("//", "/"); - singleFileName = singleFileName.startsWith("/") ? singleFileName.substring(1) : singleFileName; - } - catch(UnsupportedEncodingException e) { - - Log.i("singleFileName", singleFileName); - } - - toolbar_title.setText(singleFileName); - - getSingleFileContents(instanceToken, repoOwner, repoName, singleFileName, repoBranch); } @Override @@ -128,166 +83,189 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie super.onResume(); - String repoFullName = tinyDB.getString("repoFullName"); - String repoBranch = tinyDB.getString("repoBranch"); - String[] parts = repoFullName.split("/"); - String repoOwner = parts[0]; - String repoName = parts[1]; - String loginUid = tinyDB.getString("loginUid"); - String instanceToken = "token " + tinyDB.getString(loginUid + "-token"); - if(tinyDB.getBoolean("fileModified")) { - getSingleFileContents(instanceToken, repoOwner, repoName, singleFileName, repoBranch); + String repoFullName = tinyDB.getString("repoFullName"); + String repoBranch = tinyDB.getString("repoBranch"); + String[] parts = repoFullName.split("/"); + String repoOwner = parts[0]; + String repoName = parts[1]; + + getSingleFileContents(repoOwner, repoName, file.getPath(), repoBranch); tinyDB.putBoolean("fileModified", false); + } } + private void getSingleFileContents(final String owner, String repo, final String filename, String ref) { - private void getSingleFileContents(String token, final String owner, String repo, final String filename, String ref) { + Thread thread = new Thread(() -> { - Call call = RetrofitClient.getApiInterface(ctx).getSingleFileContents(token, owner, repo, filename, ref); + Call call = RetrofitClient + .getWebInterface(ctx) + .getFileContents(Authorization.getWeb(ctx), owner, repo, ref, filename); - call.enqueue(new Callback() { + try { - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + Response response = call.execute(); if(response.code() == 200) { - assert response.body() != null; + ResponseBody responseBody = response.body(); - if(!response.body().getContent().equals("")) { + if(responseBody != null) { + + runOnUiThread(() -> binding.progressBar.setVisibility(View.GONE)); String fileExtension = FileUtils.getExtension(filename); - mProgressBar.setVisibility(View.GONE); - fileSha = response.body().getSha(); - - // download file meta - tinyDB.putString("downloadFileName", filename); - tinyDB.putString("downloadFileContents", response.body().getContent()); - - boolean unknown = false; + boolean processable = false; switch(AppUtil.getFileType(fileExtension)) { case IMAGE: - singleFileContentsFrame.setVisibility(View.GONE); - singleCodeContents.setVisibility(View.GONE); - pdfViewFrame.setVisibility(View.GONE); - imageView.setVisibility(View.VISIBLE); + // See https://developer.android.com/guide/topics/media/media-formats#core + if(Arrays.asList("bmp", "gif", "jpg", "jpeg", "png", "webp", "heic", "heif").contains(fileExtension.toLowerCase())) { - imageData = Base64.decode(response.body().getContent(), Base64.DEFAULT); - imageView.setImageBitmap(Images.scaleImage(imageData, 1920)); + processable = true; + + byte[] pictureBytes = responseBody.bytes(); + + runOnUiThread(() -> { + + binding.contents.setVisibility(View.GONE); + binding.pdfViewFrame.setVisibility(View.GONE); + binding.markdownFrame.setVisibility(View.GONE); + + binding.photoView.setVisibility(View.VISIBLE); + binding.photoView.setImageBitmap(Images.scaleImage(pictureBytes, 1920)); + + }); + } break; + + case UNKNOWN: case TEXT: - imageView.setVisibility(View.GONE); - singleFileContentsFrame.setVisibility(View.GONE); - pdfViewFrame.setVisibility(View.GONE); - singleCodeContents.setVisibility(View.VISIBLE); - - switch(tinyDB.getInt("fileviewerSourceCodeThemeId")) { - - case 1: singleCodeContents.setTheme(Theme.ARDUINO_LIGHT); break; - case 2: singleCodeContents.setTheme(Theme.GITHUB); break; - case 3: singleCodeContents.setTheme(Theme.FAR); break; - case 4: singleCodeContents.setTheme(Theme.IR_BLACK); break; - case 5: singleCodeContents.setTheme(Theme.ANDROID_STUDIO); break; - - default: singleCodeContents.setTheme(Theme.MONOKAI_SUBLIME); - + if(file.getSize() > Constants.maximumFileViewerSize) { + break; } - singleCodeContents.setSource(AppUtil.decodeBase64(response.body().getContent())); + processable = true; + + String text = responseBody.string(); + + runOnUiThread(() -> { + + binding.photoView.setVisibility(View.GONE); + binding.markdownFrame.setVisibility(View.GONE); + binding.pdfViewFrame.setVisibility(View.GONE); + + binding.contents.setVisibility(View.VISIBLE); + binding.contents.setContent(text, fileExtension); + + }); break; + case DOCUMENT: if(fileExtension.equalsIgnoreCase("pdf")) { - imageView.setVisibility(View.GONE); - singleFileContentsFrame.setVisibility(View.GONE); - singleCodeContents.setVisibility(View.GONE); - pdfViewFrame.setVisibility(View.VISIBLE); + processable = true; - pdfNightMode = tinyDB.getBoolean("enablePdfMode"); - decodedPdf = Base64.decode(response.body().getContent(), Base64.DEFAULT); + byte[] documentBytes = responseBody.bytes(); - pdfView.fromBytes(decodedPdf) - .enableSwipe(true) - .swipeHorizontal(false) - .enableDoubletap(true) - .defaultPage(0) - .enableAnnotationRendering(false) - .password(null) - .scrollHandle(null) - .enableAntialiasing(true) - .spacing(0) - .autoSpacing(true) - .pageFitPolicy(FitPolicy.WIDTH) - .fitEachPage(true) - .pageSnap(false) - .pageFling(true) - .nightMode(pdfNightMode).load(); + runOnUiThread(() -> { + + binding.photoView.setVisibility(View.GONE); + binding.markdownFrame.setVisibility(View.GONE); + binding.contents.setVisibility(View.GONE); + + pdfNightMode = tinyDB.getBoolean("enablePdfMode"); + + binding.pdfViewFrame.setVisibility(View.VISIBLE); + binding.pdfView.fromBytes(documentBytes) + .enableSwipe(true) + .swipeHorizontal(false) + .enableDoubletap(true) + .defaultPage(0) + .enableAnnotationRendering(false) + .password(null) + .scrollHandle(null) + .enableAntialiasing(true) + .spacing(0) + .autoSpacing(true) + .pageFitPolicy(FitPolicy.WIDTH) + .fitEachPage(true) + .pageSnap(false) + .pageFling(true) + .nightMode(pdfNightMode).load(); + + }); } - else { - unknown = true; - } break; - case UNKNOWN: - default: - unknown = true; - break; } - if(unknown) { // While the file could still be non-binary, + if(!processable) { // While the file could still be non-binary, // it's better we don't show it (to prevent any crashes and/or unwanted behavior) and let the user download it instead. - imageView.setVisibility(View.GONE); - singleCodeContents.setVisibility(View.GONE); - pdfViewFrame.setVisibility(View.GONE); - singleFileContentsFrame.setVisibility(View.VISIBLE); + responseBody.close(); - singleFileContents.setText(getString(R.string.excludeFilesInFileViewer)); - singleFileContents.setGravity(Gravity.CENTER); - singleFileContents.setTypeface(null, Typeface.BOLD); + runOnUiThread(() -> { + + binding.photoView.setVisibility(View.GONE); + binding.contents.setVisibility(View.GONE); + binding.pdfViewFrame.setVisibility(View.GONE); + + binding.markdownFrame.setVisibility(View.VISIBLE); + binding.markdown.setText(getString(R.string.excludeFilesInFileViewer)); + binding.markdown.setGravity(Gravity.CENTER); + binding.markdown.setTypeface(null, Typeface.BOLD); + + }); } - } - else { + } else { + + runOnUiThread(() -> { + binding.markdown.setText(""); + binding.progressBar.setVisibility(View.GONE); + }); + + } + } else { + + switch(response.code()) { + + case 401: + AlertDialogs.authorizationTokenRevokedDialog(ctx, + getResources().getString(R.string.alertDialogTokenRevokedTitle), + getResources().getString(R.string.alertDialogTokenRevokedMessage), + getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), + getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + break; + + case 403: + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + break; + + case 404: + Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + break; + + default: + Toasty.error(ctx, getString(R.string.labelGeneralError)); - singleFileContents.setText(""); - mProgressBar.setVisibility(View.GONE); } } - else if(response.code() == 401) { + } catch(IOException ignored) {} - AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), getResources().getString(R.string.alertDialogTokenRevokedMessage), getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); - } - else if(response.code() == 403) { - - Toasty.error(ctx, ctx.getString(R.string.authorizeError)); - } - else if(response.code() == 404) { - - Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); - } - else { - - Toasty.error(ctx, getString(R.string.labelGeneralError)); - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - } }); + thread.start(); + } @Override @@ -297,11 +275,11 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie inflater.inflate(R.menu.generic_nav_dotted_menu, menu); inflater.inflate(R.menu.files_view_menu, menu); - String fileExtension = FileUtils.getExtension(singleFileName); + if(!FileUtils.getExtension(file.getName()) + .equalsIgnoreCase("md")) { - if(!fileExtension.equalsIgnoreCase("md")) { - - menu.getItem(0).setVisible(false); + menu.getItem(0) + .setVisible(false); } return true; @@ -316,35 +294,36 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie finish(); return true; - } - else if(id == R.id.genericMenu) { + + } else if(id == R.id.genericMenu) { BottomSheetFileViewerFragment bottomSheet = new BottomSheetFileViewerFragment(); bottomSheet.show(getSupportFragmentManager(), "fileViewerBottomSheet"); return true; - } - else if(id == R.id.markdown) { - new Markdown(ctx, EmojiParser.parseToUnicode(AppUtil.decodeBase64(tinyDB.getString("downloadFileContents"))), singleFileContents); + } else if(id == R.id.markdown) { if(!tinyDB.getBoolean("enableMarkdownInFileView")) { - singleCodeContents.setVisibility(View.GONE); - singleFileContentsFrame.setVisibility(View.VISIBLE); - singleFileContents.setVisibility(View.VISIBLE); - tinyDB.putBoolean("enableMarkdownInFileView", true); - } - else { + new Markdown(ctx, EmojiParser.parseToUnicode(binding.contents.getContent()), binding.markdown); + + binding.contents.setVisibility(View.GONE); + binding.markdownFrame.setVisibility(View.VISIBLE); + + tinyDB.putBoolean("enableMarkdownInFileView", true); + + } else { + + binding.markdownFrame.setVisibility(View.GONE); + binding.contents.setVisibility(View.VISIBLE); - singleCodeContents.setVisibility(View.VISIBLE); - singleFileContentsFrame.setVisibility(View.GONE); - singleFileContents.setVisibility(View.GONE); - singleCodeContents.setSource(AppUtil.decodeBase64(tinyDB.getString("downloadFileContents"))); tinyDB.putBoolean("enableMarkdownInFileView", false); + } + return true; - } - else { + + } else { return super.onOptionsItemSelected(item); } @@ -360,102 +339,118 @@ public class FileViewActivity extends BaseActivity implements BottomSheetFileVie if("deleteFile".equals(text)) { - String fileExtension = FileUtils.getExtension(singleFileName); - - String data = AppUtil.getFileType(fileExtension) == AppUtil.FileType.TEXT ? - AppUtil.decodeBase64(tinyDB.getString("downloadFileContents")) : ""; - Intent intent = new Intent(ctx, CreateFileActivity.class); intent.putExtra("fileAction", CreateFileActivity.FILE_ACTION_DELETE); - intent.putExtra("filePath", singleFileName); - intent.putExtra("fileSha", fileSha); - intent.putExtra("fileContents", data); + intent.putExtra("filePath", file.getPath()); + intent.putExtra("fileSha", file.getSha()); ctx.startActivity(intent); + } if("editFile".equals(text)) { - String fileExtension = FileUtils.getExtension(singleFileName); + if(binding.contents.getContent() != null && + !binding.contents.getContent().isEmpty()) { - switch(AppUtil.getFileType(fileExtension)) { + Intent intent = new Intent(ctx, CreateFileActivity.class); - case TEXT: + intent.putExtra("fileAction", CreateFileActivity.FILE_ACTION_EDIT); + intent.putExtra("filePath", file.getPath()); + intent.putExtra("fileSha", file.getSha()); + intent.putExtra("fileContents", binding.contents.getContent()); - Intent intent = new Intent(ctx, CreateFileActivity.class); + ctx.startActivity(intent); - intent.putExtra("fileAction", CreateFileActivity.FILE_ACTION_EDIT); - intent.putExtra("filePath", singleFileName); - intent.putExtra("fileSha", fileSha); - intent.putExtra("fileContents", AppUtil.decodeBase64(tinyDB.getString("downloadFileContents"))); + } else { - ctx.startActivity(intent); - break; - default: - - Toasty.error(ctx, getString(R.string.fileTypeCannotBeEdited)); + Toasty.error(ctx, getString(R.string.fileTypeCannotBeEdited)); } } } private void requestFileDownload() { - if(!tinyDB.getString("downloadFileContents").isEmpty()) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - File outputFileName = new File(tinyDB.getString("downloadFileName")); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.putExtra(Intent.EXTRA_TITLE, file.getName()); + intent.setType("*/*"); - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + activityResultLauncher.launch(intent); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - intent.putExtra(Intent.EXTRA_TITLE, outputFileName.getName()); - - fileDownloadActivityResultLauncher.launch(intent); - } - else { - - Toasty.warning(ctx, getString(R.string.waitLoadingDownloadFile)); - } } - ActivityResultLauncher fileDownloadActivityResultLauncher = - registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - if (result.getResultCode() == Activity.RESULT_OK) { + if (result.getResultCode() == Activity.RESULT_OK) { - Intent data = result.getData(); + assert result.getData() != null; - try { + try { - assert data != null; - Uri uri = data.getData(); + OutputStream outputStream = getContentResolver().openOutputStream(result.getData().getData()); - assert uri != null; - OutputStream outputStream = getContentResolver().openOutputStream(uri); + NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx, ctx.getPackageName()) + .setContentTitle(getString(R.string.fileViewerNotificationTitleStarted)) + .setContentText(getString(R.string.fileViewerNotificationDescriptionStarted, file.getName())) + .setSmallIcon(R.drawable.gitnex_transparent) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setChannelId(Constants.downloadNotificationChannelId) + .setProgress(100, 0, false) + .setOngoing(true); - byte[] dataAsBytes = Base64.decode(tinyDB.getString("downloadFileContents"), 0); + int notificationId = Notifications.uniqueNotificationId(ctx); - assert outputStream != null; - outputStream.write(dataAsBytes); - outputStream.close(); + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);; + notificationManager.notify(notificationId, builder.build()); - Toasty.success(ctx, getString(R.string.downloadFileSaved)); - } - catch(IOException e) { + String repoFullName = tinyDB.getString("repoFullName"); + String repoBranch = tinyDB.getString("repoBranch"); + String[] parts = repoFullName.split("/"); + String repoOwner = parts[0]; + String repoName = parts[1]; - Log.e("errorFileDownloading", Objects.requireNonNull(e.getMessage())); - } - } + Thread thread = new Thread(() -> { - }); + try { - private void initCloseListener() { + Call call = RetrofitClient + .getWebInterface(ctx) + .getFileContents(Authorization.getWeb(ctx), repoOwner, repoName, repoBranch, file.getPath()); - onClickListener = view -> { + Response response = call.execute(); - getIntent().removeExtra("singleFileName"); - finish(); - }; - } + assert response.body() != null; + + AppUtil.copyProgress(response.body().byteStream(), outputStream, file.getSize(), progress -> { + builder.setProgress(100, progress, false); + notificationManager.notify(notificationId, builder.build()); + }); + + builder.setContentTitle(getString(R.string.fileViewerNotificationTitleFinished)) + .setContentText(getString(R.string.fileViewerNotificationDescriptionFinished, file.getName())); + + } catch(IOException ignored) { + + builder.setContentTitle(getString(R.string.fileViewerNotificationTitleFailed)) + .setContentText(getString(R.string.fileViewerNotificationDescriptionFailed, file.getName())); + + } finally { + + builder.setProgress(0,0,false) + .setOngoing(false); + + notificationManager.notify(notificationId, builder.build()); + + } + }); + + thread.start(); + + } catch(IOException ignored) {} + } + + }); } diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index b02a2740..e2ddc88e 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -10,7 +10,6 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -61,8 +60,7 @@ import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; -import eightbitlab.com.blurview.BlurView; -import eightbitlab.com.blurview.RenderScriptBlur; +import jp.wasabeef.picasso.transformations.BlurTransformation; import retrofit2.Call; import retrofit2.Callback; @@ -73,12 +71,10 @@ import retrofit2.Callback; public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener, BottomSheetDraftsFragment.BottomSheetListener { private DrawerLayout drawer; - private BlurView blurView; private TextView userFullName; private TextView userEmail; private ImageView userAvatar; private ImageView userAvatarBackground; - private ViewGroup navHeaderFrame; private TextView toolbarTitle; private Typeface myTypeface; @@ -273,12 +269,10 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig String userFullNameNav = tinyDB.getString("userFullname"); String userAvatarNav = tinyDB.getString("userAvatar"); - blurView = hView.findViewById(R.id.blurView); userEmail = hView.findViewById(R.id.userEmail); userFullName = hView.findViewById(R.id.userFullname); userAvatar = hView.findViewById(R.id.userAvatar); userAvatarBackground = hView.findViewById(R.id.userAvatarBackground); - navHeaderFrame = hView.findViewById(R.id.navHeaderFrame); List userAccountsList; userAccountsList = new ArrayList<>(); @@ -323,24 +317,18 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig PicassoService.getInstance(ctx).get() .load(userAvatarNav) + .transform(new BlurTransformation(ctx)) .into(userAvatarBackground, new com.squareup.picasso.Callback() { @Override public void onSuccess() { - int textColor = new ColorInverter().getImageViewContrastColor(userAvatarBackground); userFullName.setTextColor(textColor); userEmail.setTextColor(textColor); - - blurView.setupWith(navHeaderFrame) - .setBlurAlgorithm(new RenderScriptBlur(ctx)) - .setBlurRadius(5) - .setHasFixedTransformationMatrix(false); } - @Override - public void onError(Exception e) {} + @Override public void onError(Exception e) {} }); } @@ -350,6 +338,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ProfileFragment()).commit(); navigationView.setCheckedItem(R.id.nav_profile); drawer.closeDrawers(); + }); getNotificationsCount(instanceToken); @@ -403,22 +392,21 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig switch(launchFragmentByHandler) { case "repos": - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new RepositoriesFragment()).commit(); navigationView.setCheckedItem(R.id.nav_repositories); return; - case "org": + case "org": getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new OrganizationsFragment()).commit(); navigationView.setCheckedItem(R.id.nav_organizations); return; - case "notification": + case "notification": getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NotificationsFragment()).commit(); navigationView.setCheckedItem(R.id.nav_notifications); return; - case "explore": + case "explore": getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreFragment()).commit(); navigationView.setCheckedItem(R.id.nav_explore); return; @@ -438,49 +426,48 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig switch(tinyDB.getInt("homeScreenId")) { case 1: - toolbarTitle.setText(getResources().getString(R.string.pageTitleStarredRepos)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new StarredRepositoriesFragment()).commit(); navigationView.setCheckedItem(R.id.nav_starred_repos); break; - case 2: + case 2: toolbarTitle.setText(getResources().getString(R.string.pageTitleOrganizations)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new OrganizationsFragment()).commit(); navigationView.setCheckedItem(R.id.nav_organizations); break; - case 3: + case 3: toolbarTitle.setText(getResources().getString(R.string.pageTitleRepositories)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new RepositoriesFragment()).commit(); navigationView.setCheckedItem(R.id.nav_repositories); break; - case 4: + case 4: toolbarTitle.setText(getResources().getString(R.string.pageTitleProfile)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ProfileFragment()).commit(); navigationView.setCheckedItem(R.id.nav_profile); break; - case 5: + case 5: toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreFragment()).commit(); navigationView.setCheckedItem(R.id.nav_explore); break; - case 6: + case 6: toolbarTitle.setText(getResources().getString(R.string.titleDrafts)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new DraftsFragment()).commit(); navigationView.setCheckedItem(R.id.nav_comments_draft); break; - case 7: + case 7: toolbarTitle.setText(getResources().getString(R.string.pageTitleNotifications)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NotificationsFragment()).commit(); navigationView.setCheckedItem(R.id.nav_notifications); break; - default: + default: toolbarTitle.setText(getResources().getString(R.string.pageTitleMyRepos)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new MyRepositoriesFragment()).commit(); navigationView.setCheckedItem(R.id.nav_home); diff --git a/app/src/main/java/org/mian/gitnex/activities/MergePullRequestActivity.java b/app/src/main/java/org/mian/gitnex/activities/MergePullRequestActivity.java index 7098dc4b..12b5d48a 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MergePullRequestActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MergePullRequestActivity.java @@ -21,7 +21,6 @@ import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.Objects; -import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; @@ -170,12 +169,12 @@ public class MergePullRequestActivity extends BaseActivity { MergePullRequest mergePR = new MergePullRequest(Do, mergePRDT, mergeTitle); - Call call = RetrofitClient.getApiInterface(ctx).mergePullRequest(Authorization.get(ctx), repoOwner, repoName, prIndex, mergePR); + Call call = RetrofitClient.getApiInterface(ctx).mergePullRequest(Authorization.get(ctx), repoOwner, repoName, prIndex, mergePR); - call.enqueue(new Callback() { + call.enqueue(new Callback() { @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { if(response.code() == 200) { @@ -244,7 +243,7 @@ public class MergePullRequestActivity extends BaseActivity { } @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { + public void onFailure(@NonNull Call call, @NonNull Throwable t) { Log.e("onFailure", t.toString()); enableProcessButton(); diff --git a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java index 6c3e6f63..f16cee4c 100644 --- a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java @@ -27,7 +27,7 @@ import org.mian.gitnex.adapters.RepoForksAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityRepoForksBinding; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; @@ -46,7 +46,7 @@ public class RepoForksActivity extends BaseActivity { private TextView noData; private ProgressBar progressBar; private String TAG = "RepositoryForks"; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private int pageSize = 1; private RecyclerView recyclerView; @@ -90,7 +90,7 @@ public class RepoForksActivity extends BaseActivity { // if gitea is 1.12 or higher use the new limit (resultLimitNewGiteaInstances) if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } recyclerView = activityRepoForksBinding.recyclerView; diff --git a/app/src/main/java/org/mian/gitnex/activities/SettingsFileViewerActivity.java b/app/src/main/java/org/mian/gitnex/activities/SettingsFileViewerActivity.java deleted file mode 100644 index d3e86f63..00000000 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsFileViewerActivity.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.mian.gitnex.activities; - -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import androidx.appcompat.app.AlertDialog; -import com.google.android.material.switchmaterial.SwitchMaterial; -import org.mian.gitnex.R; -import org.mian.gitnex.databinding.ActivitySettingsFileviewerBinding; -import org.mian.gitnex.helpers.Toasty; - -/** - * Author M M Arif - */ - -public class SettingsFileViewerActivity extends BaseActivity { - - private View.OnClickListener onClickListener; - - private static final String[] fileViewerSourceCodeThemesList = {"Sublime", "Arduino Light", "Github", "Far ", "Ir Black", "Android Studio"}; - private static int fileViewerSourceCodeThemesSelectedChoice = 0; - - @Override - public void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - - ActivitySettingsFileviewerBinding activitySettingsFileviewerBinding = ActivitySettingsFileviewerBinding.inflate(getLayoutInflater()); - setContentView(activitySettingsFileviewerBinding.getRoot()); - - ImageView closeActivity = activitySettingsFileviewerBinding.close; - - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); - - TextView fileViewerSourceCodeThemesSelected = activitySettingsFileviewerBinding.sourceCodeThemeSelected; // setter for fileviewer theme - LinearLayout sourceCodeThemeFrame = activitySettingsFileviewerBinding.sourceCodeThemeFrame; - SwitchMaterial pdfModeSwitch = activitySettingsFileviewerBinding.switchPdfMode; - - if(!tinyDB.getString("fileviewerSourceCodeThemeStr").isEmpty()) { - fileViewerSourceCodeThemesSelected.setText(tinyDB.getString("fileviewerSourceCodeThemeStr")); - } - - if(fileViewerSourceCodeThemesSelectedChoice == 0) { - fileViewerSourceCodeThemesSelectedChoice = tinyDB.getInt("fileviewerThemeId"); - } - - pdfModeSwitch.setChecked(tinyDB.getBoolean("enablePdfMode")); - - // fileviewer srouce code theme selection dialog - sourceCodeThemeFrame.setOnClickListener(view -> { - - AlertDialog.Builder fvtsBuilder = new AlertDialog.Builder(SettingsFileViewerActivity.this); - - fvtsBuilder.setTitle(R.string.fileViewerSourceCodeThemeSelectorDialogTitle); - fvtsBuilder.setCancelable(fileViewerSourceCodeThemesSelectedChoice != -1); - - fvtsBuilder.setSingleChoiceItems(fileViewerSourceCodeThemesList, fileViewerSourceCodeThemesSelectedChoice, (dialogInterfaceTheme, i) -> { - - fileViewerSourceCodeThemesSelectedChoice = i; - fileViewerSourceCodeThemesSelected.setText(fileViewerSourceCodeThemesList[i]); - tinyDB.putString("fileviewerSourceCodeThemeStr", fileViewerSourceCodeThemesList[i]); - tinyDB.putInt("fileviewerSourceCodeThemeId", i); - - dialogInterfaceTheme.dismiss(); - Toasty.success(appCtx, getResources().getString(R.string.settingsSave)); - - }); - - AlertDialog cfDialog = fvtsBuilder.create(); - cfDialog.show(); - }); - - // pdf night mode switcher - pdfModeSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - - tinyDB.putBoolean("enablePdfMode", isChecked); - tinyDB.putString("enablePdfModeInit", "yes"); - Toasty.success(appCtx, getResources().getString(R.string.settingsSave)); - }); - } - - private void initCloseListener() { - onClickListener = view -> finish(); - } - -} diff --git a/app/src/main/java/org/mian/gitnex/activities/SettingsNotificationsActivity.java b/app/src/main/java/org/mian/gitnex/activities/SettingsNotificationsActivity.java index b668625b..1c378fd5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsNotificationsActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/SettingsNotificationsActivity.java @@ -8,7 +8,7 @@ import androidx.appcompat.app.AlertDialog; import com.pes.androidmaterialcolorpickerdialog.ColorPicker; import org.mian.gitnex.R; import org.mian.gitnex.databinding.ActivitySettingsNotificationsBinding; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.notifications.Notifications; @@ -33,7 +33,7 @@ public class SettingsNotificationsActivity extends BaseActivity { viewBinding.close.setOnClickListener(onClickListener); - viewBinding.pollingDelaySelected.setText(String.format(getString(R.string.pollingDelaySelectedText), tinyDB.getInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay))); + viewBinding.pollingDelaySelected.setText(String.format(getString(R.string.pollingDelaySelectedText), tinyDB.getInt("pollingDelayMinutes", Constants.defaultPollingDelay))); viewBinding.chooseColorState.setCardBackgroundColor(tinyDB.getInt("notificationsLightColor", Color.GREEN)); viewBinding.enableNotificationsMode.setChecked(tinyDB.getBoolean("notificationsEnabled", true)); @@ -52,9 +52,9 @@ public class SettingsNotificationsActivity extends BaseActivity { viewBinding.pollingDelayFrame.setOnClickListener(v -> { NumberPicker numberPicker = new NumberPicker(ctx); - numberPicker.setMinValue(StaticGlobalVariables.minimumPollingDelay); - numberPicker.setMaxValue(StaticGlobalVariables.maximumPollingDelay); - numberPicker.setValue(tinyDB.getInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay)); + numberPicker.setMinValue(Constants.minimumPollingDelay); + numberPicker.setMaxValue(Constants.maximumPollingDelay); + numberPicker.setValue(tinyDB.getInt("pollingDelayMinutes", Constants.defaultPollingDelay)); numberPicker.setWrapSelectorWheel(true); AlertDialog.Builder builder = new AlertDialog.Builder(ctx); @@ -76,6 +76,7 @@ public class SettingsNotificationsActivity extends BaseActivity { builder.setNeutralButton(R.string.cancelButton, (dialog, which) -> dialog.dismiss()); builder.setView(numberPicker); builder.create().show(); + }); // lights switcher diff --git a/app/src/main/java/org/mian/gitnex/adapters/FilesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/FilesAdapter.java index 5724dd44..25ef21d6 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/FilesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/FilesAdapter.java @@ -7,6 +7,7 @@ import android.view.ViewGroup; import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; @@ -14,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView; import org.apache.commons.io.FileUtils; import org.gitnex.tea4j.models.Files; import org.mian.gitnex.R; -import org.mian.gitnex.helpers.Toasty; import java.util.ArrayList; import java.util.List; @@ -33,14 +33,14 @@ public class FilesAdapter extends RecyclerView.Adapter filesListener.onClickFile(file)); + //ImageView filesDropdownMenu = itemView.findViewById(R.id.filesDropdownMenu); - fileName.setOnClickListener(v -> { - - Context context = v.getContext(); - - if(fileType.equals("file")) { - filesListener.onClickFile(fileName.getText().toString()); - } - else if(fileType.equals("dir")) { - filesListener.onClickDir(fileName.getText().toString()); - } - else { - Toasty.warning(context, context.getString(R.string.filesGenericError)); - } - - }); - - /*filesDropdownMenu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -170,25 +156,36 @@ public class FilesAdapter extends RecyclerView.Adapter class IssuesHolder extends RecyclerView.ViewHolder { - private String userLoginId; + private Issues issue; - private final TextView issueNumber; private final ImageView issueAssigneeAvatar; private final TextView issueTitle; private final TextView issueCreatedTime; @@ -114,7 +113,6 @@ public class IssuesAdapter extends RecyclerView.Adapter super(itemView); - issueNumber = itemView.findViewById(R.id.issueNumber); issueAssigneeAvatar = itemView.findViewById(R.id.assigneeAvatar); issueTitle = itemView.findViewById(R.id.issueTitle); issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount); @@ -126,10 +124,10 @@ public class IssuesAdapter extends RecyclerView.Adapter Context context = title.getContext(); Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); + intent.putExtra("issueNumber", issue.getNumber()); TinyDB tinyDb = TinyDB.getInstance(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); + tinyDb.putString("issueNumber", String.valueOf(issue.getNumber())); tinyDb.putString("issueType", "Issue"); context.startActivity(intent); @@ -140,18 +138,18 @@ public class IssuesAdapter extends RecyclerView.Adapter Context context = commentsCount.getContext(); Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); + intent.putExtra("issueNumber", issue.getNumber()); TinyDB tinyDb = TinyDB.getInstance(context); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); + tinyDb.putString("issueNumber", String.valueOf(issue.getNumber())); tinyDb.putString("issueType", "Issue"); context.startActivity(intent); }); - issueAssigneeAvatar.setOnClickListener(loginId -> { - - Context context = loginId.getContext(); + issueAssigneeAvatar.setOnClickListener(v -> { + Context context = v.getContext(); + String userLoginId = issue.getUser().getLogin(); AppUtil.copyToClipboard(context, userLoginId, context.getString(R.string.copyLoginIdToClipBoard, userLoginId)); }); @@ -159,40 +157,44 @@ public class IssuesAdapter extends RecyclerView.Adapter } @SuppressLint("SetTextI18n") - void bindData(Issues issuesModel) { + void bindData(Issues issue) { - final TinyDB tinyDb = TinyDB.getInstance(context); - final String locale = tinyDb.getString("locale"); - final String timeFormat = tinyDb.getString("dateFormat"); + TinyDB tinyDb = TinyDB.getInstance(context); + String locale = tinyDb.getString("locale"); + String timeFormat = tinyDb.getString("dateFormat"); - userLoginId = issuesModel.getUser().getLogin(); + PicassoService.getInstance(context).get() + .load(issue.getUser().getAvatar_url()) + .placeholder(R.drawable.loader_animated) + .transform(new RoundedTransformation(8, 0)) + .resize(120, 120) + .centerCrop() + .into(issueAssigneeAvatar); - PicassoService.getInstance(context).get().load(issuesModel.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar); + String issueNumber_ = "" + context.getResources().getString(R.string.hash) + issue.getNumber() + ""; + issueTitle.setText(HtmlCompat.fromHtml(issueNumber_ + " " + EmojiParser.parseToUnicode(issue.getTitle()), HtmlCompat.FROM_HTML_MODE_LEGACY)); - String issueNumber_ = "" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + ""; - issueTitle.setText(HtmlCompat.fromHtml(issueNumber_ + " " + EmojiParser.parseToUnicode(issuesModel.getTitle()), HtmlCompat.FROM_HTML_MODE_LEGACY)); - - issueNumber.setText(String.valueOf(issuesModel.getNumber())); - issueCommentsCount.setText(String.valueOf(issuesModel.getComments())); + this.issue = issue; + this.issueCommentsCount.setText(String.valueOf(issue.getComments())); switch(timeFormat) { case "pretty": { PrettyTime prettyTime = new PrettyTime(new Locale(locale)); - String createdTime = prettyTime.format(issuesModel.getCreated_at()); - issueCreatedTime.setText(createdTime); - issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(issuesModel.getCreated_at()), context)); + String createdTime = prettyTime.format(issue.getCreated_at()); + this.issueCreatedTime.setText(createdTime); + this.issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(issue.getCreated_at()), context)); break; } case "normal": { DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); - String createdTime = formatter.format(issuesModel.getCreated_at()); - issueCreatedTime.setText(createdTime); + String createdTime = formatter.format(issue.getCreated_at()); + this.issueCreatedTime.setText(createdTime); break; } case "normal1": { DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale)); - String createdTime = formatter.format(issuesModel.getCreated_at()); - issueCreatedTime.setText(createdTime); + String createdTime = formatter.format(issue.getCreated_at()); + this.issueCreatedTime.setText(createdTime); break; } } diff --git a/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java index 46966259..48a28c44 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java @@ -18,8 +18,8 @@ import org.gitnex.tea4j.models.Milestones; import org.mian.gitnex.R; import org.mian.gitnex.actions.MilestoneActions; import org.mian.gitnex.helpers.ClickListener; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Markdown; -import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.TimeHelper; import org.mian.gitnex.helpers.TinyDB; import java.text.DateFormat; @@ -41,7 +41,7 @@ public class MilestonesAdapter extends RecyclerView.Adapter dataListMain) { diff --git a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java index 944760ad..64e284f8 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java @@ -292,7 +292,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter { - - Context context = loginId.getContext(); + assigneeAvatar.setOnClickListener(v -> { + Context context = v.getContext(); + String userLoginId = pullRequest.getUser().getLogin(); AppUtil.copyToClipboard(context, userLoginId, context.getString(R.string.copyLoginIdToClipBoard, userLoginId)); }); @@ -175,43 +179,32 @@ public class PullRequestsAdapter extends RecyclerView.Adapter" + context.getResources().getString(R.string.hash) + prModel.getNumber() + ""; - prTitle.setText(HtmlCompat.fromHtml(prNumber_ + " " + EmojiParser.parseToUnicode(prModel.getTitle()), HtmlCompat.FROM_HTML_MODE_LEGACY)); + String prNumber_ = "" + context.getResources().getString(R.string.hash) + pullRequest.getNumber() + ""; - prNumber.setText(String.valueOf(prModel.getNumber())); - prMergeable.setText(String.valueOf(prModel.isMergeable())); - if(prModel.getHead() != null) { - prHeadBranch.setText(prModel.getHead().getRef()); - if(prModel.getHead().getRepo() != null) { - prIsFork.setText(String.valueOf(prModel.getHead().getRepo().isFork())); - prForkFullName.setText(prModel.getHead().getRepo().getFull_name()); - } - else { - // pull was done from a deleted fork - prIsFork.setText("true"); - prForkFullName.setText(context.getString(R.string.prDeletedFork)); - } - } - prCommentsCount.setText(String.valueOf(prModel.getComments())); - - prCreatedTime.setText(TimeHelper.formatTime(prModel.getCreated_at(), new Locale(locale), timeFormat, context)); + this.prTitle.setText(HtmlCompat.fromHtml(prNumber_ + " " + EmojiParser.parseToUnicode(pullRequest.getTitle()), HtmlCompat.FROM_HTML_MODE_LEGACY)); + this.prCommentsCount.setText(String.valueOf(pullRequest.getComments())); + this.prCreatedTime.setText(TimeHelper.formatTime(pullRequest.getCreated_at(), new Locale(locale), timeFormat, context)); if(timeFormat.equals("pretty")) { - prCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(prModel.getCreated_at()), context)); + this.prCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(pullRequest.getCreated_at()), context)); } - } - } static class LoadHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java index fd497e23..0069e224 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java @@ -293,7 +293,7 @@ public class ReposListAdapter extends RecyclerView.Adapter { Context context = v.getContext(); Intent intent = new Intent(context, IssueDetailActivity.class); - intent.putExtra("issueNumber", issueNumber.getText()); + intent.putExtra("issueNumber", issue.getNumber()); - tinyDb.putString("issueNumber", issueNumber.getText().toString()); + tinyDb.putString("issueNumber", String.valueOf(issue.getNumber())); tinyDb.putString("issueType", "Issue"); - tinyDb.putString("repoFullName", repoFullName.getText().toString()); + tinyDb.putString("repoFullName", issue.getRepository().getFull_name()); - String[] parts = repoFullName.getText().toString().split("/"); + String[] parts = issue.getRepository().getFull_name().split("/"); final String repoOwner = parts[0]; final String repoName = parts[1]; @@ -104,14 +100,13 @@ public class SearchIssuesAdapter extends RecyclerView.Adapter { - - Context context = loginId.getContext(); + issueAssigneeAvatar.setOnClickListener(v -> { + Context context = v.getContext(); + String userLoginId = issue.getUser().getLogin(); AppUtil.copyToClipboard(context, userLoginId, context.getString(R.string.copyLoginIdToClipBoard, userLoginId)); }); } - } @NonNull @@ -130,17 +125,19 @@ public class SearchIssuesAdapter extends RecyclerView.Adapter" + currentItem.getRepository().getFull_name() + mCtx.getResources().getString(R.string.hash) + currentItem.getNumber() + ""; - holder.issueTitle.setText(HtmlCompat.fromHtml(issueNumber_ + " " + currentItem.getTitle(), HtmlCompat.FROM_HTML_MODE_LEGACY)); - holder.issueNumber.setText(String.valueOf(currentItem.getNumber())); + holder.issue = currentItem; + holder.issueTitle.setText(HtmlCompat.fromHtml(issueNumber_ + " " + currentItem.getTitle(), HtmlCompat.FROM_HTML_MODE_LEGACY)); holder.issueCommentsCount.setText(String.valueOf(currentItem.getComments())); - holder.repoFullName.setText(currentItem.getRepository().getFull_name()); switch(timeFormat) { case "pretty": { diff --git a/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java index 343932dc..947f3d01 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java @@ -296,7 +296,7 @@ public class StarredReposListAdapter extends RecyclerView.Adapter apiInterfaces = new ConcurrentHashMap<>(); private static final Map webInterfaces = new ConcurrentHashMap<>(); - private static Retrofit createRetrofit(Context context, String instanceUrl) { + private static Retrofit createRetrofit(Context context, String instanceUrl, boolean cacheEnabled) { TinyDB tinyDB = TinyDB.getInstance(context); - int cacheSize = FilesData.returnOnlyNumber(tinyDB.getString("cacheSizeStr")) * 1024 * 1024; - Cache cache = new Cache(new File(context.getCacheDir(), "responses"), cacheSize); - - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - logging.setLevel(HttpLoggingInterceptor.Level.BODY); +// HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); +// logging.setLevel(HttpLoggingInterceptor.Level.BODY); try { @@ -49,11 +45,17 @@ public class RetrofitClient { MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context); sslContext.init(null, new X509TrustManager[]{ memorizingTrustManager }, new SecureRandom()); - OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder().cache(cache) - //.addInterceptor(logging) + OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder() +// .addInterceptor(logging) .sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager) - .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())) - .addInterceptor(chain -> { + .hostnameVerifier(memorizingTrustManager.wrapHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())); + + if(cacheEnabled) { + + int cacheSize = FilesData.returnOnlyNumber(tinyDB.getString("cacheSizeStr")) * 1024 * 1024; + Cache cache = new Cache(new File(context.getCacheDir(), "responses"), cacheSize); + + okHttpClient.cache(cache).addInterceptor(chain -> { Request request = chain.request(); @@ -64,6 +66,7 @@ public class RetrofitClient { return chain.proceed(request); }); + } return new Retrofit.Builder() .baseUrl(instanceUrl) @@ -101,7 +104,7 @@ public class RetrofitClient { synchronized(RetrofitClient.class) { if(!apiInterfaces.containsKey(url)) { - ApiInterface apiInterface = createRetrofit(context, url).create(ApiInterface.class); + ApiInterface apiInterface = createRetrofit(context, url, true).create(ApiInterface.class); apiInterfaces.put(url, apiInterface); return apiInterface; @@ -119,7 +122,7 @@ public class RetrofitClient { synchronized(RetrofitClient.class) { if(!webInterfaces.containsKey(url)) { - WebInterface webInterface = createRetrofit(context, url).create(WebInterface.class); + WebInterface webInterface = createRetrofit(context, url, false).create(WebInterface.class); webInterfaces.put(url, webInterface); return webInterface; diff --git a/app/src/main/java/org/mian/gitnex/core/MainApplication.java b/app/src/main/java/org/mian/gitnex/core/MainApplication.java index ceed209a..5fd91d2c 100644 --- a/app/src/main/java/org/mian/gitnex/core/MainApplication.java +++ b/app/src/main/java/org/mian/gitnex/core/MainApplication.java @@ -14,8 +14,8 @@ import org.acra.config.MailSenderConfigurationBuilder; import org.acra.data.StringFormat; import org.mian.gitnex.R; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.FontsOverride; -import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.notifications.Notifications; @@ -28,7 +28,9 @@ import org.mian.gitnex.notifications.Notifications; resTitle = R.string.crashTitle, resChannelName = R.string.setCrashReports, resText = R.string.crashMessage) -@AcraCore(reportContent = { ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.STACK_TRACE }) +@AcraCore(reportContent = { + ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, + ReportField.STACK_TRACE, ReportField.AVAILABLE_MEM_SIZE, ReportField.BRAND }) public class MainApplication extends Application { @@ -69,20 +71,26 @@ public class MainApplication extends Application { } + Notifications.createChannels(appCtx); + } + + @Override + protected void attachBaseContext(Context context) { + super.attachBaseContext(context); + + tinyDB = TinyDB.getInstance(context); + if(tinyDB.getBoolean("crashReportingEnabled")) { CoreConfigurationBuilder ACRABuilder = new CoreConfigurationBuilder(this); ACRABuilder.setBuildConfigClass(BuildConfig.class).setReportFormat(StringFormat.KEY_VALUE_LIST); - ACRABuilder.getPluginConfigurationBuilder(MailSenderConfigurationBuilder.class).setReportAsFile(true).setMailTo(getResources().getString(R.string.appEmail)).setSubject(getResources().getString(R.string.crashReportEmailSubject, AppUtil.getAppBuildNo(getApplicationContext()))).setEnabled(true); + ACRABuilder.getPluginConfigurationBuilder(MailSenderConfigurationBuilder.class).setReportAsFile(true).setMailTo(getResources().getString(R.string.appEmail)).setSubject(getResources().getString(R.string.crashReportEmailSubject, AppUtil + .getAppBuildNo(context))).setEnabled(true); ACRABuilder.getPluginConfigurationBuilder(LimiterConfigurationBuilder.class).setEnabled(true); ACRA.init(this, ACRABuilder); - } - - Notifications.startWorker(appCtx); - } private void setDefaults() { @@ -121,7 +129,7 @@ public class MainApplication extends Application { // setting default polling delay if(tinyDB.getInt("pollingDelayMinutes", 0) <= 0) { - tinyDB.putInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay); + tinyDB.putInt("pollingDelayMinutes", Constants.defaultPollingDelay); } // disable biometric by default diff --git a/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java b/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java new file mode 100644 index 00000000..aab0bfda --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/core/MainGrammarLocator.java @@ -0,0 +1,140 @@ +package org.mian.gitnex.core; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Set; +import io.noties.prism4j.GrammarLocator; +import io.noties.prism4j.Prism4j; +import io.noties.prism4j.annotations.PrismBundle; + +/** + * @author opyale + */ + +@PrismBundle( + includeAll = true, + grammarLocatorClassName = ".DefaultGrammarLocator" +) +public class MainGrammarLocator implements GrammarLocator { + + public static final String DEFAULT_FALLBACK_LANGUAGE = "clike"; + + private static final DefaultGrammarLocator defaultGrammarLocator = new DefaultGrammarLocator(); + private static volatile MainGrammarLocator instance; + + private MainGrammarLocator() {} + + public String fromExtension(String extension) { + + switch(extension.toLowerCase()) { + + case "b": + case "bf": + return "brainfuck"; + + case "c": + case "h": + case "hdl": + return "c"; + + case "clj": + case "cljs": + case "cljc": + case "edn": + return "clojure"; + + case "cc": + case "cpp": + case "cxx": + case "c++": + case "hh": + case "hpp": + case "hxx": + case "h++": + return "cpp"; + + case "cs": + case "csx": + return "csharp"; + + case "groovy": + case "gradle": + case "gvy": + case "gy": + case "gsh": + return "groovy"; + + case "js": + case "cjs": + case "mjs": + return "javascript"; + + case "kt": + case "kts": + case "ktm": + return "kotlin"; + + case "md": + return "markdown"; + + case "xml": + case "html": + case "htm": + case "mathml": + case "svg": + return "markup"; + + case "py": + case "pyi": + case "pyc": + case "pyd": + case "pyo": + case "pyw": + case "pyz": + return "python"; + + case "scala": + case "sc": + return "scala"; + + case "yaml": + case "yml": + case "properties": // This extension doesn't correspond to YAML, but it's the next best option + case "ini": // This extension doesn't correspond to YAML, but it's the next best option + return "yaml"; + + } + + return extension; + + } + + @Nullable + @Override + public Prism4j.Grammar grammar(@NotNull Prism4j prism4j, @NotNull String language) { + + return defaultGrammarLocator.grammar(prism4j, language); + } + + @NotNull + @Override + public Set languages() { + + return defaultGrammarLocator.languages(); + } + + public static MainGrammarLocator getInstance() { + + if(instance == null) { + synchronized(MainGrammarLocator.class) { + if(instance == null) { + instance = new MainGrammarLocator(); + } + } + } + + return instance; + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/api/DraftsApi.java b/app/src/main/java/org/mian/gitnex/database/api/DraftsApi.java index fd3fd691..8a4307f0 100644 --- a/app/src/main/java/org/mian/gitnex/database/api/DraftsApi.java +++ b/app/src/main/java/org/mian/gitnex/database/api/DraftsApi.java @@ -7,7 +7,7 @@ import org.mian.gitnex.database.dao.DraftsDao; import org.mian.gitnex.database.db.GitnexDatabase; import org.mian.gitnex.database.models.Draft; import org.mian.gitnex.database.models.DraftWithRepository; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import java.util.List; /** @@ -51,7 +51,7 @@ public class DraftsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.draftsApi, e.toString()); + Log.e(Constants.draftsApi, e.toString()); } return draftId; @@ -67,7 +67,7 @@ public class DraftsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.draftsApi, e.toString()); + Log.e(Constants.draftsApi, e.toString()); } return draftId; @@ -83,7 +83,7 @@ public class DraftsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.draftsApi, e.toString()); + Log.e(Constants.draftsApi, e.toString()); } return checkDraftFlag; diff --git a/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java b/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java index 55fd537c..ef5e1f5d 100644 --- a/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java +++ b/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java @@ -6,7 +6,7 @@ import androidx.lifecycle.LiveData; import org.mian.gitnex.database.dao.RepositoriesDao; import org.mian.gitnex.database.db.GitnexDatabase; import org.mian.gitnex.database.models.Repository; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import java.util.List; /** @@ -47,7 +47,7 @@ public class RepositoriesApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.repositoriesApi, e.toString()); + Log.e(Constants.repositoriesApi, e.toString()); } return repositoryId; @@ -63,7 +63,7 @@ public class RepositoriesApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.repositoriesApi, e.toString()); + Log.e(Constants.repositoriesApi, e.toString()); } return repository; @@ -89,7 +89,7 @@ public class RepositoriesApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.repositoriesApi, e.toString()); + Log.e(Constants.repositoriesApi, e.toString()); } return checkRepository; @@ -105,7 +105,7 @@ public class RepositoriesApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.repositoriesApi, e.toString()); + Log.e(Constants.repositoriesApi, e.toString()); } return repository; @@ -121,7 +121,7 @@ public class RepositoriesApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.repositoriesApi, e.toString()); + Log.e(Constants.repositoriesApi, e.toString()); } return repository; diff --git a/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java b/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java index 92e7f183..482da27b 100644 --- a/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java +++ b/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java @@ -6,7 +6,7 @@ import androidx.lifecycle.LiveData; import org.mian.gitnex.database.dao.UserAccountsDao; import org.mian.gitnex.database.db.GitnexDatabase; import org.mian.gitnex.database.models.UserAccount; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import java.util.List; /** @@ -50,7 +50,7 @@ public class UserAccountsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.userAccountsApi, e.toString()); + Log.e(Constants.userAccountsApi, e.toString()); } return accountId; @@ -81,7 +81,7 @@ public class UserAccountsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.userAccountsApi, e.toString()); + Log.e(Constants.userAccountsApi, e.toString()); } return userAccount; @@ -97,7 +97,7 @@ public class UserAccountsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.userAccountsApi, e.toString()); + Log.e(Constants.userAccountsApi, e.toString()); } return checkAccount; @@ -118,7 +118,7 @@ public class UserAccountsApi { } catch(InterruptedException e) { - Log.e(StaticGlobalVariables.userAccountsApi, e.toString()); + Log.e(Constants.userAccountsApi, e.toString()); } return userAccounts; diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java index 5e3501f4..ef3b8048 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java @@ -10,7 +10,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import org.mian.gitnex.databinding.BottomSheetDraftsBinding; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; /** * Author M M Arif @@ -18,7 +18,7 @@ import org.mian.gitnex.helpers.StaticGlobalVariables; public class BottomSheetDraftsFragment extends BottomSheetDialogFragment { - private String TAG = StaticGlobalVariables.tagDraftsBottomSheet; + private String TAG = Constants.tagDraftsBottomSheet; private BottomSheetDraftsFragment.BottomSheetListener bmListener; @Nullable diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetReplyFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetReplyFragment.java index a138c8c9..df04d66e 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetReplyFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetReplyFragment.java @@ -25,7 +25,7 @@ import org.mian.gitnex.actions.IssueActions; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.database.api.DraftsApi; import org.mian.gitnex.databinding.BottomSheetReplyLayoutBinding; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import java.util.Objects; @@ -266,11 +266,11 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment { String draftType; if(tinyDB.getString("issueType").equalsIgnoreCase("Issue")) { - draftType = StaticGlobalVariables.draftTypeIssue; + draftType = Constants.draftTypeIssue; } else if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { - draftType = StaticGlobalVariables.draftTypePull; + draftType = Constants.draftTypePull; } else { diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExploreRepositoriesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreRepositoriesFragment.java index db32f168..e09cbb05 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ExploreRepositoriesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ExploreRepositoriesFragment.java @@ -28,8 +28,8 @@ import org.mian.gitnex.databinding.CustomExploreRepositoriesDialogBinding; import org.mian.gitnex.databinding.FragmentExploreRepoBinding; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.InfiniteScrollListener; -import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; @@ -80,7 +80,7 @@ public class ExploreRepositoriesFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - limit = StaticGlobalVariables.resultLimitNewGiteaInstances; + limit = Constants.resultLimitNewGiteaInstances; } LinearLayoutManager linearLayoutManager = new LinearLayoutManager(ctx); @@ -104,7 +104,7 @@ public class ExploreRepositoriesFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - limit = StaticGlobalVariables.resultLimitNewGiteaInstances; + limit = Constants.resultLimitNewGiteaInstances; } else { limit = 10; @@ -134,7 +134,7 @@ public class ExploreRepositoriesFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - limit = StaticGlobalVariables.resultLimitNewGiteaInstances; + limit = Constants.resultLimitNewGiteaInstances; } else { limit = 10; @@ -156,7 +156,7 @@ public class ExploreRepositoriesFragment extends Fragment { int apiCallDefaultLimit = 10; // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - apiCallDefaultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + apiCallDefaultLimit = Constants.resultLimitNewGiteaInstances; } if(apiCallDefaultLimit > limit) { diff --git a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java index 5f986962..4d381550 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java @@ -11,15 +11,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; +import org.gitnex.tea4j.models.Files; import org.mian.gitnex.R; import org.mian.gitnex.activities.FileViewActivity; import org.mian.gitnex.activities.RepoDetailActivity; @@ -31,7 +28,6 @@ import org.mian.gitnex.helpers.Path; import org.mian.gitnex.viewmodels.FilesViewModel; import java.util.ArrayList; import java.util.Collections; -import moe.feng.common.view.breadcrumbs.BreadcrumbsView; import moe.feng.common.view.breadcrumbs.DefaultBreadcrumbsCallback; import moe.feng.common.view.breadcrumbs.model.BreadcrumbItem; @@ -41,15 +37,11 @@ import moe.feng.common.view.breadcrumbs.model.BreadcrumbItem; public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapterListener { - private ProgressBar mProgressBar; - private RecyclerView mRecyclerView; - private TextView noDataFiles; - private LinearLayout filesFrame; + private FragmentFilesBinding binding; private static final String repoNameF = "param2"; private static final String repoOwnerF = "param1"; private static final String repoRefF = "param3"; - private BreadcrumbsView mBreadcrumbsView; private String repoName; private String repoOwner; @@ -92,28 +84,21 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentFilesBinding fragmentFilesBinding = FragmentFilesBinding.inflate(inflater, container, false); + binding = FragmentFilesBinding.inflate(inflater, container, false); setHasOptionsMenu(true); - noDataFiles = fragmentFilesBinding.noDataFiles; - filesFrame = fragmentFilesBinding.filesFrame; - filesAdapter = new FilesAdapter(getContext(), this); - mRecyclerView = fragmentFilesBinding.recyclerView; - mRecyclerView.setHasFixedSize(true); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - mRecyclerView.setAdapter(filesAdapter); + binding.recyclerView.setHasFixedSize(true); + binding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerView.setAdapter(filesAdapter); - DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(), DividerItemDecoration.VERTICAL); - mRecyclerView.addItemDecoration(dividerItemDecoration); + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(binding.recyclerView.getContext(), DividerItemDecoration.VERTICAL); + binding.recyclerView.addItemDecoration(dividerItemDecoration); - mProgressBar = fragmentFilesBinding.progressBar; - - mBreadcrumbsView = fragmentFilesBinding.breadcrumbsView; - mBreadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); + binding.breadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); // noinspection unchecked - mBreadcrumbsView.setCallback(new DefaultBreadcrumbsCallback() { + binding.breadcrumbsView.setCallback(new DefaultBreadcrumbsCallback() { @SuppressLint("SetTextI18n") @Override @@ -140,14 +125,14 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter path.clear(); ref = repoBranch; - mBreadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); + binding.breadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, repoBranch); }); fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, ref); - return fragmentFilesBinding.getRoot(); + return binding.getRoot(); } @Override @@ -157,40 +142,35 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter } @Override - public void onClickDir(String dirName) { + public void onClickFile(Files file) { - path.add(dirName); - mBreadcrumbsView.addItem(new BreadcrumbItem(Collections.singletonList(dirName))); + switch(file.getType()) { - fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, path.toString(), ref); + case "dir": + path.add(file.getName()); + binding.breadcrumbsView.addItem(new BreadcrumbItem(Collections.singletonList(file.getName()))); - } + fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, path.toString(), ref); + break; - @Override - public void onClickFile(String fileName) { + case "file": + Intent intent = new Intent(getContext(), FileViewActivity.class); + intent.putExtra("file", file); - Intent intent = new Intent(getContext(), FileViewActivity.class); + requireContext().startActivity(intent); + break; - if(path.size() != 0) { - - intent.putExtra("singleFileName", path.toString() + "/" + fileName); } - else { - - intent.putExtra("singleFileName", fileName); - } - - requireContext().startActivity(intent); } private void fetchDataAsync(String instanceToken, String owner, String repo, String ref) { - mRecyclerView.setVisibility(View.GONE); - mProgressBar.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.VISIBLE); FilesViewModel filesModel = new ViewModelProvider(this).get(FilesViewModel.class); - filesModel.getFilesList(instanceToken, owner, repo, ref, getContext(), mProgressBar, noDataFiles).observe(getViewLifecycleOwner(), filesListMain -> { + filesModel.getFilesList(instanceToken, owner, repo, ref, getContext(), binding.progressBar, binding.noDataFiles).observe(getViewLifecycleOwner(), filesListMain -> { filesAdapter.getOriginalFiles().clear(); filesAdapter.getOriginalFiles().addAll(filesListMain); @@ -198,16 +178,16 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter if(filesListMain.size() > 0) { - AppUtil.setMultiVisibility(View.VISIBLE, mRecyclerView, filesFrame); - noDataFiles.setVisibility(View.GONE); + AppUtil.setMultiVisibility(View.VISIBLE, binding.recyclerView, binding.filesFrame); + binding.noDataFiles.setVisibility(View.GONE); } else { - AppUtil.setMultiVisibility(View.VISIBLE, mRecyclerView, filesFrame, noDataFiles); + AppUtil.setMultiVisibility(View.VISIBLE, binding.recyclerView, binding.filesFrame, binding.noDataFiles); } - filesFrame.setVisibility(View.VISIBLE); - mProgressBar.setVisibility(View.GONE); + binding.filesFrame.setVisibility(View.VISIBLE); + binding.progressBar.setVisibility(View.GONE); }); @@ -215,12 +195,12 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter private void fetchDataAsyncSub(String instanceToken, String owner, String repo, String filesDir, String ref) { - mRecyclerView.setVisibility(View.GONE); - mProgressBar.setVisibility(View.VISIBLE); + binding.recyclerView.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.VISIBLE); - FilesViewModel filesModel2 = new ViewModelProvider(this).get(FilesViewModel.class); + FilesViewModel filesModel = new ViewModelProvider(this).get(FilesViewModel.class); - filesModel2.getFilesList2(instanceToken, owner, repo, filesDir, ref, getContext(), mProgressBar, noDataFiles).observe(this, filesListMain2 -> { + filesModel.getFilesList2(instanceToken, owner, repo, filesDir, ref, getContext(), binding.progressBar, binding.noDataFiles).observe(this, filesListMain2 -> { filesAdapter.getOriginalFiles().clear(); filesAdapter.getOriginalFiles().addAll(filesListMain2); @@ -228,15 +208,15 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter if(filesListMain2.size() > 0) { - AppUtil.setMultiVisibility(View.VISIBLE, mRecyclerView, filesFrame); - noDataFiles.setVisibility(View.GONE); + AppUtil.setMultiVisibility(View.VISIBLE, binding.recyclerView, binding.filesFrame); + binding.noDataFiles.setVisibility(View.GONE); } else { - AppUtil.setMultiVisibility(View.VISIBLE, mRecyclerView, filesFrame, noDataFiles); + AppUtil.setMultiVisibility(View.VISIBLE, binding.recyclerView, binding.filesFrame, binding.noDataFiles); } - filesFrame.setVisibility(View.VISIBLE); - mProgressBar.setVisibility(View.GONE); + binding.filesFrame.setVisibility(View.VISIBLE); + binding.progressBar.setVisibility(View.GONE); }); @@ -259,7 +239,7 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter @Override public boolean onQueryTextChange(String newText) { - if(mRecyclerView.getAdapter() != null) { + if(binding.recyclerView.getAdapter() != null) { filesAdapter.getFilter().filter(newText); } diff --git a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java index c535bb58..29c853dd 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java @@ -28,7 +28,7 @@ import org.mian.gitnex.adapters.IssuesAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentIssuesBinding; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; @@ -49,12 +49,12 @@ public class IssuesFragment extends Fragment { private List issuesList; private IssuesAdapter adapter; private Context context; - private int pageSize = StaticGlobalVariables.issuesPageInit; + private int pageSize = Constants.issuesPageInit; private ProgressBar mProgressBar; - private String TAG = StaticGlobalVariables.tagIssuesList; + private String TAG = Constants.tagIssuesList; private TextView noDataIssues; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; - private String requestType = StaticGlobalVariables.issuesRequestType; + private int resultLimit = Constants.resultLimitOldGiteaInstances; + private String requestType = Constants.issuesRequestType; private ProgressBar progressLoadMore; @Nullable @@ -78,7 +78,7 @@ public class IssuesFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } recyclerView = fragmentIssuesBinding.recyclerView; diff --git a/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java index f8657f11..ca4512cc 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java @@ -23,7 +23,7 @@ import org.mian.gitnex.adapters.MilestonesAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentMilestonesBinding; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; @@ -44,9 +44,9 @@ public class MilestonesFragment extends Fragment { private List dataList; private MilestonesAdapter adapter; private Context ctx; - private int pageSize = StaticGlobalVariables.milestonesPageInit; - private String TAG = StaticGlobalVariables.tagMilestonesFragment; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int pageSize = Constants.milestonesPageInit; + private String TAG = Constants.tagMilestonesFragment; + private int resultLimit = Constants.resultLimitOldGiteaInstances; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -68,7 +68,7 @@ public class MilestonesFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } dataList = new ArrayList<>(); diff --git a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java index 91a5aba5..a62be27d 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java @@ -29,8 +29,8 @@ import org.mian.gitnex.adapters.NotificationsAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentNotificationsBinding; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.InfiniteScrollListener; -import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import java.io.IOException; @@ -83,7 +83,7 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap context = getContext(); tinyDB = TinyDB.getInstance(context); - pageResultLimit = StaticGlobalVariables.getCurrentResultLimit(context); + pageResultLimit = Constants.getCurrentResultLimit(context); tinyDB.putString("notificationsFilterState", currentFilterMode); markAllAsRead = fragmentNotificationsBinding.markAllAsRead; @@ -204,7 +204,7 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap Call> call = RetrofitClient .getApiInterface(context) .getNotificationThreads(instanceToken, false, filter, - StaticGlobalVariables.defaultOldestTimestamp, "", + Constants.defaultOldestTimestamp, "", pageCurrentIndex, pageResultLimit); call.enqueue(new Callback>() { diff --git a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java index 1282c19b..d6c45a00 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java @@ -30,8 +30,7 @@ import org.mian.gitnex.helpers.ColorInverter; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.TinyDB; import java.util.Locale; -import eightbitlab.com.blurview.BlurView; -import eightbitlab.com.blurview.RenderScriptBlur; +import jp.wasabeef.picasso.transformations.BlurTransformation; /** * Author M M Arif @@ -54,7 +53,6 @@ public class ProfileFragment extends Fragment { TinyDB tinyDb = TinyDB.getInstance(getContext()); - BlurView blurView = v.findViewById(R.id.blurView); TextView userFullName = v.findViewById(R.id.userFullName); ImageView userAvatarBackground = v.findViewById(R.id.userAvatarBackground); ImageView userAvatar = v.findViewById(R.id.userAvatar); @@ -77,10 +75,10 @@ public class ProfileFragment extends Fragment { userLanguage.setText(R.string.notSupported); } - userAvatar.setOnClickListener(loginId -> { - - AppUtil.copyToClipboard(ctx, tinyDb.getString("userLogin"), ctx.getString(R.string.copyLoginIdToClipBoard, tinyDb.getString("userLogin"))); - }); + userAvatar.setOnClickListener(loginId -> + AppUtil.copyToClipboard(ctx, + tinyDb.getString("userLogin"), + ctx.getString(R.string.copyLoginIdToClipBoard, tinyDb.getString("userLogin")))); userFullName.setText(Html.fromHtml(tinyDb.getString("userFullname"))); userLogin.setText(getString(R.string.usernameWithAt, tinyDb.getString("userLogin"))); @@ -94,11 +92,11 @@ public class ProfileFragment extends Fragment { PicassoService.getInstance(ctx).get() .load(tinyDb.getString("userAvatar")) + .transform(new BlurTransformation(ctx)) .into(userAvatarBackground, new Callback() { @Override public void onSuccess() { - int invertedColor = new ColorInverter().getImageViewContrastColor(userAvatarBackground); userFullName.setTextColor(invertedColor); @@ -107,20 +105,11 @@ public class ProfileFragment extends Fragment { userLanguage.setTextColor(invertedColor); ImageViewCompat.setImageTintList(userLanguageIcon, ColorStateList.valueOf(invertedColor)); - - blurView.setupWith(aboutFrame) - .setBlurAlgorithm(new RenderScriptBlur(ctx)) - .setBlurRadius(3) - .setHasFixedTransformationMatrix(true); - } - @Override - public void onError(Exception e) {} - + @Override public void onError(Exception e) {} }); - ProfileFragment.SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager()); ViewPager mViewPager = v.findViewById(R.id.container); @@ -181,8 +170,6 @@ public class ProfileFragment extends Fragment { @Override public Fragment getItem(int position) { - Fragment fragment = null; - switch (position) { case 0: // followers @@ -196,7 +183,7 @@ public class ProfileFragment extends Fragment { } - return fragment; + return null; } diff --git a/app/src/main/java/org/mian/gitnex/fragments/PullRequestsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/PullRequestsFragment.java index 793378a5..0e30006a 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/PullRequestsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/PullRequestsFragment.java @@ -28,7 +28,7 @@ import org.mian.gitnex.adapters.PullRequestsAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentPullRequestsBinding; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; @@ -49,11 +49,11 @@ public class PullRequestsFragment extends Fragment { private RecyclerView recyclerView; private List prList; private PullRequestsAdapter adapter; - private String TAG = StaticGlobalVariables.tagPullRequestsList; + private String TAG = Constants.tagPullRequestsList; private Context context; - private int pageSize = StaticGlobalVariables.prPageInit; + private int pageSize = Constants.prPageInit; private TextView noData; - private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; + private int resultLimit = Constants.resultLimitOldGiteaInstances; private ProgressBar progressLoadMore; @Nullable @@ -77,7 +77,7 @@ public class PullRequestsFragment extends Fragment { // if gitea is 1.12 or higher use the new limit if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { - resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; + resultLimit = Constants.resultLimitNewGiteaInstances; } recyclerView = fragmentPullRequestsBinding.recyclerView; diff --git a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java index 0a3962d8..f7c67749 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java @@ -8,10 +8,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; @@ -42,36 +39,18 @@ import retrofit2.Callback; public class RepoInfoFragment extends Fragment { private Context ctx; - private ProgressBar mProgressBar; private LinearLayout pageContent; private static String repoNameF = "param2"; private static String repoOwnerF = "param1"; + private FragmentRepoInfoBinding binding; + private String repoName; private String repoOwner; - private TextView repoMetaName; - private TextView repoMetaDescription; - private TextView repoMetaStars; - private TextView repoMetaPullRequests; - private LinearLayout repoMetaPullRequestsFrame; - private TextView repoMetaForks; - private TextView repoMetaSize; - private TextView repoMetaWatchers; - private TextView repoMetaCreatedAt; - private TextView repoMetaWebsite; - private Button repoAdditionalButton; - private TextView repoFileContents; - private LinearLayout repoMetaFrame; - private ImageView repoMetaDataExpandCollapse; - private ImageView repoFilenameExpandCollapse; - private LinearLayout fileContentsFrameHeader; - private LinearLayout fileContentsFrame; private OnFragmentInteractionListener mListener; - public RepoInfoFragment() { - - } + public RepoInfoFragment() {} public static RepoInfoFragment newInstance(String param1, String param2) { RepoInfoFragment fragment = new RepoInfoFragment(); @@ -94,44 +73,18 @@ public class RepoInfoFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentRepoInfoBinding fragmentRepoInfoBinding = FragmentRepoInfoBinding.inflate(inflater, container, false); + binding = FragmentRepoInfoBinding.inflate(inflater, container, false); TinyDB tinyDb = TinyDB.getInstance(getContext()); - final String locale = tinyDb.getString("locale"); - final String timeFormat = tinyDb.getString("dateFormat"); + ctx = getContext(); - ctx = getActivity(); - - pageContent = fragmentRepoInfoBinding.repoInfoLayout; + pageContent = binding.repoInfoLayout; pageContent.setVisibility(View.GONE); - mProgressBar = fragmentRepoInfoBinding.progressBar; - repoMetaName = fragmentRepoInfoBinding.repoMetaName; - repoMetaDescription = fragmentRepoInfoBinding.repoMetaDescription; - repoMetaStars = fragmentRepoInfoBinding.repoMetaStars; - repoMetaPullRequests = fragmentRepoInfoBinding.repoMetaPullRequests; - repoMetaPullRequestsFrame = fragmentRepoInfoBinding.repoMetaPullRequestsFrame; - repoMetaForks = fragmentRepoInfoBinding.repoMetaForks; - repoMetaSize = fragmentRepoInfoBinding.repoMetaSize; - repoMetaWatchers = fragmentRepoInfoBinding.repoMetaWatchers; - repoMetaCreatedAt = fragmentRepoInfoBinding.repoMetaCreatedAt; - repoMetaWebsite = fragmentRepoInfoBinding.repoMetaWebsite; - repoAdditionalButton = fragmentRepoInfoBinding.repoAdditionalButton; - repoFileContents = fragmentRepoInfoBinding.repoFileContents; - repoMetaFrame = fragmentRepoInfoBinding.repoMetaFrame; - LinearLayout repoMetaFrameHeader = fragmentRepoInfoBinding.repoMetaFrameHeader; - repoMetaDataExpandCollapse = fragmentRepoInfoBinding.repoMetaDataExpandCollapse; - repoFilenameExpandCollapse = fragmentRepoInfoBinding.repoFilenameExpandCollapse; - fileContentsFrameHeader = fragmentRepoInfoBinding.fileContentsFrameHeader; - fileContentsFrame = fragmentRepoInfoBinding.fileContentsFrame; - LinearLayout repoMetaStarsFrame = fragmentRepoInfoBinding.repoMetaStarsFrame; - LinearLayout repoMetaForksFrame = fragmentRepoInfoBinding.repoMetaForksFrame; - LinearLayout repoMetaWatchersFrame = fragmentRepoInfoBinding.repoMetaWatchersFrame; + binding.repoMetaFrame.setVisibility(View.GONE); - repoMetaFrame.setVisibility(View.GONE); - - getRepoInfo(Authorization.get(getContext()), repoOwner, repoName, locale, timeFormat); + getRepoInfo(Authorization.get(getContext()), repoOwner, repoName, tinyDb.getString("locale"), tinyDb.getString("dateFormat")); getFileContents(Authorization.get(getContext()), repoOwner, repoName, getResources().getString(R.string.defaultFilename)); if(isExpandViewVisible()) { @@ -142,27 +95,26 @@ public class RepoInfoFragment extends Fragment { toggleExpandViewMeta(); } - fileContentsFrameHeader.setOnClickListener(v1 -> toggleExpandView()); + binding.fileContentsFrameHeader.setOnClickListener(v1 -> toggleExpandView()); + binding.repoMetaFrameHeader.setOnClickListener(v12 -> toggleExpandViewMeta()); - repoMetaFrameHeader.setOnClickListener(v12 -> toggleExpandViewMeta()); - - repoMetaStarsFrame.setOnClickListener(metaStars -> { + binding.repoMetaStarsFrame.setOnClickListener(metaStars -> { Intent intent = new Intent(ctx, RepoStargazersActivity.class); intent.putExtra("repoFullNameForStars", repoOwner + "/" + repoName); ctx.startActivity(intent); }); - repoMetaWatchersFrame.setOnClickListener(metaWatchers -> { + binding.repoMetaWatchersFrame.setOnClickListener(metaWatchers -> { Intent intent = new Intent(ctx, RepoWatchersActivity.class); intent.putExtra("repoFullNameForWatchers", repoOwner + "/" + repoName); ctx.startActivity(intent); }); - repoMetaPullRequestsFrame.setOnClickListener(metaPR -> RepoDetailActivity.mViewPager.setCurrentItem(3)); + binding.repoMetaPullRequestsFrame.setOnClickListener(metaPR -> RepoDetailActivity.mViewPager.setCurrentItem(3)); - return fragmentRepoInfoBinding.getRoot(); + return binding.getRoot(); } public void onButtonPressed(Uri uri) { @@ -183,42 +135,42 @@ public class RepoInfoFragment extends Fragment { private void toggleExpandView() { - if (repoFileContents.getVisibility() == View.GONE) { - repoFilenameExpandCollapse.setImageResource(R.drawable.ic_chevron_up); - repoFileContents.setVisibility(View.VISIBLE); + if (binding.repoFileContents.getVisibility() == View.GONE) { + binding.repoFilenameExpandCollapse.setImageResource(R.drawable.ic_chevron_up); + binding.repoFileContents.setVisibility(View.VISIBLE); //Animation slide_down = AnimationUtils.loadAnimation(getContext(), R.anim.slide_down); - //fileContentsFrame.startAnimation(slide_down); + //binding.fileContentsFrame.startAnimation(slide_down); } else { - repoFilenameExpandCollapse.setImageResource(R.drawable.ic_chevron_down); - repoFileContents.setVisibility(View.GONE); + binding.repoFilenameExpandCollapse.setImageResource(R.drawable.ic_chevron_down); + binding.repoFileContents.setVisibility(View.GONE); //Animation slide_up = AnimationUtils.loadAnimation(getContext(), R.anim.slide_up); - //fileContentsFrame.startAnimation(slide_up); + //binding.fileContentsFrame.startAnimation(slide_up); } } private boolean isExpandViewVisible() { - return repoFileContents.getVisibility() == View.VISIBLE; + return binding.repoFileContents.getVisibility() == View.VISIBLE; } private void toggleExpandViewMeta() { - if (repoMetaFrame.getVisibility() == View.GONE) { - repoMetaDataExpandCollapse.setImageResource(R.drawable.ic_chevron_up); - repoMetaFrame.setVisibility(View.VISIBLE); + if (binding.repoMetaFrame.getVisibility() == View.GONE) { + binding.repoMetaDataExpandCollapse.setImageResource(R.drawable.ic_chevron_up); + binding.repoMetaFrame.setVisibility(View.VISIBLE); //Animation slide_down = AnimationUtils.loadAnimation(getContext(), R.anim.slide_down); - //repoMetaFrame.startAnimation(slide_down); + //binding.repoMetaFrame.startAnimation(slide_down); } else { - repoMetaDataExpandCollapse.setImageResource(R.drawable.ic_chevron_down); - repoMetaFrame.setVisibility(View.GONE); + binding.repoMetaDataExpandCollapse.setImageResource(R.drawable.ic_chevron_down); + binding.repoMetaFrame.setVisibility(View.GONE); //Animation slide_up = AnimationUtils.loadAnimation(getContext(), R.anim.slide_up); - //repoMetaFrame.startAnimation(slide_up); + //binding.repoMetaFrame.startAnimation(slide_up); } } private boolean isExpandViewMetaVisible() { - return repoMetaFrame.getVisibility() == View.VISIBLE; + return binding.repoMetaFrame.getVisibility() == View.VISIBLE; } private void getRepoInfo(String token, final String owner, String repo, final String locale, final String timeFormat) { @@ -243,39 +195,39 @@ public class RepoInfoFragment extends Fragment { if (response.code() == 200) { assert repoInfo != null; - repoMetaName.setText(repoInfo.getName()); + binding.repoMetaName.setText(repoInfo.getName()); if(!repoInfo.getDescription().isEmpty()) { - repoMetaDescription.setText(repoInfo.getDescription()); + binding.repoMetaDescription.setText(repoInfo.getDescription()); } else { - repoMetaDescription.setText(getString(R.string.noDataDescription)); + binding.repoMetaDescription.setText(getString(R.string.noDataDescription)); } - repoMetaStars.setText(repoInfo.getStars_count()); + binding.repoMetaStars.setText(repoInfo.getStars_count()); if(repoInfo.getOpen_pull_count() != null) { - repoMetaPullRequests.setText(repoInfo.getOpen_pull_count()); + binding.repoMetaPullRequests.setText(repoInfo.getOpen_pull_count()); } else { - repoMetaPullRequestsFrame.setVisibility(View.GONE); + binding.repoMetaPullRequestsFrame.setVisibility(View.GONE); } - repoMetaForks.setText(repoInfo.getForks_count()); - repoMetaWatchers.setText(repoInfo.getWatchers_count()); - repoMetaSize.setText(FileUtils.byteCountToDisplaySize((int) (repoInfo.getSize() * 1024))); + binding.repoMetaForks.setText(repoInfo.getForks_count()); + binding.repoMetaWatchers.setText(repoInfo.getWatchers_count()); + binding.repoMetaSize.setText(FileUtils.byteCountToDisplaySize((int) repoInfo.getSize() * 1024)); - repoMetaCreatedAt.setText(TimeHelper.formatTime(repoInfo.getCreated_at(), new Locale(locale), timeFormat, ctx)); + binding.repoMetaCreatedAt.setText(TimeHelper.formatTime(repoInfo.getCreated_at(), new Locale(locale), timeFormat, ctx)); if(timeFormat.equals("pretty")) { - repoMetaCreatedAt.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(repoInfo.getCreated_at()), ctx)); + binding.repoMetaCreatedAt.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(repoInfo.getCreated_at()), ctx)); } String repoMetaUpdatedAt = TimeHelper.formatTime(repoInfo.getUpdated_at(), new Locale(locale), timeFormat, ctx); String website = (repoInfo.getWebsite().isEmpty()) ? getResources().getString(R.string.noDataWebsite) : repoInfo.getWebsite(); - repoMetaWebsite.setText(website); + binding.repoMetaWebsite.setText(website); - repoAdditionalButton.setOnClickListener(v -> { + binding.repoAdditionalButton.setOnClickListener(v -> { View view = LayoutInflater.from(ctx).inflate(R.layout.layout_repo_more_info, null); @@ -334,7 +286,7 @@ public class RepoInfoFragment extends Fragment { tinyDb.putString("repoHtmlUrl", repoInfo.getHtml_url()); - mProgressBar.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.GONE); pageContent.setVisibility(View.VISIBLE); } @@ -369,39 +321,41 @@ public class RepoInfoFragment extends Fragment { if (isAdded()) { - if (response.code() == 200) { + switch(response.code()) { - new Markdown(ctx, response.body(), repoFileContents); + case 200: + new Markdown(ctx, response.body(), binding.repoFileContents); + break; - } else if (response.code() == 401) { - - AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), + case 401: + AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), getResources().getString(R.string.alertDialogTokenRevokedMessage), getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + break; - } else if (response.code() == 403) { + case 403: + Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + break; - Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + case 404: + binding.fileContentsFrameHeader.setVisibility(View.GONE); + binding.fileContentsFrame.setVisibility(View.GONE); + break; - } else if (response.code() == 404) { - - fileContentsFrameHeader.setVisibility(View.GONE); - fileContentsFrame.setVisibility(View.GONE); - - } else { - - Toasty.error(getContext(), getString(R.string.genericError)); + default: + Toasty.error(getContext(), getString(R.string.genericError)); + break; } } - } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { Log.e("onFailure", t.toString()); } + }); } diff --git a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java index 8e09ee97..68603d59 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java @@ -15,7 +15,6 @@ import org.mian.gitnex.R; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.activities.SettingsAppearanceActivity; import org.mian.gitnex.activities.SettingsDraftsActivity; -import org.mian.gitnex.activities.SettingsFileViewerActivity; import org.mian.gitnex.activities.SettingsGeneralActivity; import org.mian.gitnex.activities.SettingsNotificationsActivity; import org.mian.gitnex.activities.SettingsReportsActivity; @@ -54,8 +53,6 @@ public class SettingsFragment extends Fragment { fragmentSettingsBinding.appearanceFrame.setOnClickListener(v1 -> startActivity(new Intent(ctx, SettingsAppearanceActivity.class))); - fragmentSettingsBinding.fileViewerFrame.setOnClickListener(v1 -> startActivity(new Intent(ctx, SettingsFileViewerActivity.class))); - fragmentSettingsBinding.draftsFrame.setOnClickListener(v1 -> startActivity(new Intent(ctx, SettingsDraftsActivity.class))); fragmentSettingsBinding.securityFrame.setOnClickListener(v1 -> startActivity(new Intent(ctx, SettingsSecurityActivity.class))); @@ -71,6 +68,7 @@ public class SettingsFragment extends Fragment { fragmentSettingsBinding.aboutAppFrame.setOnClickListener(aboutApp -> requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new AboutFragment()).commit()); return fragmentSettingsBinding.getRoot(); + } public void rateThisApp() { diff --git a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java index a5fee3fd..0a539feb 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java @@ -13,17 +13,20 @@ import android.util.TypedValue; import android.view.View; import androidx.annotation.ColorInt; import androidx.core.content.pm.PackageInfoCompat; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Author M M Arif @@ -31,31 +34,38 @@ import java.util.Objects; public class AppUtil { - public enum FileType { IMAGE, DOCUMENT, TEXT, UNKNOWN } + public enum FileType { IMAGE, AUDIO, VIDEO, DOCUMENT, TEXT, EXECUTABLE, UNKNOWN } - private static final HashMap, FileType> extensions = new HashMap<>(); + private static final HashMap extensions = new HashMap<>(); // AppUtil should not be instantiated. private AppUtil() {} static { - extensions.put(Arrays.asList("jpg", "jpeg", "gif", "png", "ico"), FileType.IMAGE); - extensions.put(Arrays.asList("doc", "docx", "ppt", "pptx", "xls", "xlsx", "xlsm", "odt", "ott", "odf", "ods", "ots", "exe", "jar", "odg", "otg", "odp", "otp", "bin", "dmg", "psd", "xcf", "pdf"), FileType.DOCUMENT); - extensions.put(Arrays.asList("txt", "md", "json", "java", "go", "php", "c", "cc", "cpp", "h", "cxx", "cyc", "m", "cs", "bash", "sh", "bsh", "cv", "python", "perl", "pm", "rb", "ruby", "javascript", "coffee", "rc", "rs", "rust", "basic", "clj", "css", "dart", "lisp", "erl", "hs", "lsp", "rkt", "ss", "llvm", "ll", "lua", "matlab", "pascal", "r", "scala", "sql", "latex", "tex", "vb", "vbs", "vhd", "tcl", "wiki.meta", "yaml", "yml", "markdown", "xml", "proto", "regex", "py", "pl", "js", "html", "htm", "volt", "ini", "htaccess", "conf", "gitignore", "gradle", "txt", "properties", "bat", "twig", "cvs", "cmake", "in", "info", "spec", "m4", "am", "dist", "pam", "hx", "ts"), FileType.TEXT); + extensions.put(new String[]{"jpg", "jpeg", "gif", "png", "ico", "tif", "tiff", "bmp"}, FileType.IMAGE); + extensions.put(new String[]{"mp3", "wav", "opus", "flac", "wma", "aac", "m4a", "oga", "mpc", "ogg"}, FileType.AUDIO); + extensions.put(new String[]{"mp4", "mkv", "avi", "mov", "wmv", "qt", "mts", "m2ts", "webm", "flv", "ogv", "amv", "mpg", "mpeg", "mpv", "m4v", "3gp", "wmv"}, FileType.VIDEO); + extensions.put(new String[]{"doc", "docx", "ppt", "pptx", "xls", "xlsx", "xlsm", "odt", "ott", "odf", "ods", "ots", "odg", "otg", "odp", "otp", "bin", "psd", "xcf", "pdf"}, FileType.DOCUMENT); + extensions.put(new String[]{"exe", "msi", "jar", "dmg", "deb", "apk"}, FileType.EXECUTABLE); + extensions.put(new String[]{"txt", "md", "json", "java", "go", "php", "c", "cc", "cpp", "h", "cxx", "cyc", "m", "cs", "bash", "sh", "bsh", "cv", "python", "perl", "pm", "rb", "ruby", "javascript", "coffee", "rc", "rs", "rust", "basic", "clj", "css", "dart", "lisp", "erl", "hs", "lsp", "rkt", "ss", "llvm", "ll", "lua", "matlab", "pascal", "r", "scala", "sql", "latex", "tex", "vb", "vbs", "vhd", "tcl", "wiki.meta", "yaml", "yml", "markdown", "xml", "proto", "regex", "py", "pl", "js", "html", "htm", "volt", "ini", "htaccess", "conf", "gitignore", "gradle", "txt", "properties", "bat", "twig", "cvs", "cmake", "in", "info", "spec", "m4", "am", "dist", "pam", "hx", "ts", "kt", "kts"}, FileType.TEXT); + } public static FileType getFileType(String extension) { - for(List e : extensions.keySet()) { + if(extension != null && !extension.isEmpty()) { + for(String[] testExtensions : extensions.keySet()) { + for(String testExtension : testExtensions) { - if(e.contains(extension)) { - - return extensions.get(e); + if(testExtension.equalsIgnoreCase(extension)) + return extensions.get(testExtensions); + } } } return FileType.UNKNOWN; + } public static boolean hasNetworkConnection(Context context) { @@ -63,6 +73,45 @@ public class AppUtil { return NetworkStatusObserver.getInstance(context).hasNetworkConnection(); } + public static void copyProgress(InputStream inputStream, OutputStream outputStream, long totalSize, ProgressListener progressListener) throws IOException { + + byte[] buffer = new byte[4096]; + int read; + + long totalSteps = (long) Math.ceil((double) totalSize / buffer.length); + long stepsPerPercent = (long) Math.floor((double) totalSteps / 100); + + short percent = 0; + long stepCount = 0; + + progressListener.onActionStarted(); + + while((read = inputStream.read(buffer)) != -1) { + + outputStream.write(buffer, 0, read); + stepCount++; + + if(stepCount == stepsPerPercent) { + percent++; + if(percent <= 100) progressListener.onProgressChanged(percent); + stepCount = 0; + } + } + + if(percent < 100) { + progressListener.onProgressChanged((short) 100); + } + + progressListener.onActionFinished(); + } + + public interface ProgressListener { + default void onActionStarted() {} + default void onActionFinished() {} + + void onProgressChanged(short progress); + } + public static int getAppBuildNo(Context context) { try { @@ -248,6 +297,20 @@ public class AppUtil { return (int) (context.getResources().getDisplayMetrics().scaledDensity * sp); } + public static int getLineCount(String s) { + + int lines = 0; + + Pattern pattern = Pattern.compile("(\r\n|\r|\n)"); + Matcher matcher = pattern.matcher(s); + + while(matcher.find()) + lines++; + + return lines; + + } + public static void copyToClipboard(Context ctx, CharSequence data, String message) { ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(Context.CLIPBOARD_SERVICE); diff --git a/app/src/main/java/org/mian/gitnex/helpers/Authorization.java b/app/src/main/java/org/mian/gitnex/helpers/Authorization.java index 6a520d65..e97dd246 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/Authorization.java +++ b/app/src/main/java/org/mian/gitnex/helpers/Authorization.java @@ -23,4 +23,12 @@ public class Authorization { return "token " + tinyDb.getString(loginUid + "-token"); } + + public static String getWeb(Context context) { + + TinyDB tinyDb = TinyDB.getInstance(context); + return Credentials.basic("", tinyDb.getString(tinyDb.getString("loginUid") + "-token")); + + } + } diff --git a/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java b/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java index e1f16d79..00925b0e 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java @@ -5,6 +5,7 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.widget.ImageView; import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; /** * Author M M Arif @@ -17,45 +18,36 @@ public class ColorInverter { double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; - int d; - if (a < 0.5) { - d = 30; // almost black - } else { - d = 255; // white - } + int d = (a < 0.30) ? + 30 : // almost black + 255; // white return Color.rgb(d, d, d); } @ColorInt - public int getImageViewContrastColor(ImageView imageView) { + public int getBitmapContrastColor(@NonNull Bitmap bitmap) { - if(imageView != null) { + int colorSum = 0; + int divisionValue = 0; - Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); + for(int height=0; height 0) { + linesView.setLineCount(AppUtil.getLineCount(source)); + + try { + + MainGrammarLocator mainGrammarLocator = MainGrammarLocator.getInstance(); + Prism4j prism4j = new Prism4j(mainGrammarLocator); + + CharSequence highlightedSource = Prism4jSyntaxHighlight.create(prism4j, prism4jTheme, MainGrammarLocator.DEFAULT_FALLBACK_LANGUAGE) + .highlight(mainGrammarLocator.fromExtension(extension), source); + + if(highlightedSource.charAt(highlightedSource.length() - 1) == '\n') { + // Removes a line break which is probably added by Prism4j but not actually present in the source. + // This line should be altered in case this gets fixed. + sourceView.setText(highlightedSource.subSequence(0, highlightedSource.length() - 1)); + } + else { + sourceView.setText(highlightedSource); + } + + } catch(Throwable ignored) { + // Fall back to plaintext if something fails + sourceView.setText(source); + } + } + } + + public String getContent() { + + return sourceView.getText().toString(); + } + + private static class LinesView extends View { + + private final Paint paint = new Paint(); + private final Rect textBounds = new Rect(); + + @ColorInt private int backgroundColor; + @ColorInt private int textColor; + @ColorInt private int lineColor; + + private int lineCount; + + public LinesView(Context context) { + super(context); + } + + public void setLineCount(int lineCount) { + this.lineCount = lineCount; + } + + @Override + public void setBackgroundColor(@ColorInt int backgroundColor) { + this.backgroundColor = backgroundColor; + } + + public void setTextColor(@ColorInt int textColor) { + this.textColor = textColor; + } + + public void setLineColor(@ColorInt int lineColor) { + this.lineColor = lineColor; + } + + public Paint getPaint() { + return paint; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + String highestLineNumber = String.valueOf(lineCount); + + paint.getTextBounds(highestLineNumber, 0, highestLineNumber.length(), textBounds); + + setMeasuredDimension(getPaddingLeft() + textBounds.width() + getPaddingRight(), MeasureSpec.getSize(heightMeasureSpec)); + + } + + @Override + protected void onDraw(Canvas canvas) { + + paint.setColor(backgroundColor); + + canvas.drawRect(0, 0, getWidth(), getHeight(), paint); + + float marginTopBottom = (float) (getHeight() - (textBounds.height() / 2)) / lineCount; + + paint.setColor(textColor); + + canvas.save(); + canvas.translate(getPaddingLeft(), marginTopBottom); + + for(int currentLine = 1; currentLine <= lineCount; currentLine++) { + + canvas.drawText(String.valueOf(currentLine), 0, 0, paint); + canvas.translate(0, marginTopBottom); + + } + + paint.setColor(lineColor); + + int dividerX = getWidth() - 1; + int dividerY = getHeight(); + + canvas.restore(); + canvas.drawLine(dividerX,0, dividerX, dividerY, paint); + + } + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/HighlightJsView.java b/app/src/main/java/org/mian/gitnex/helpers/highlightjs/HighlightJsView.java index 7f9c7930..e69de29b 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/HighlightJsView.java +++ b/app/src/main/java/org/mian/gitnex/helpers/highlightjs/HighlightJsView.java @@ -1,112 +0,0 @@ -package org.mian.gitnex.helpers.highlightjs; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.webkit.WebSettings; -import android.webkit.WebView; - -import org.mian.gitnex.helpers.highlightjs.models.Language; -import org.mian.gitnex.helpers.highlightjs.models.Theme; -import org.mian.gitnex.helpers.highlightjs.utils.SourceUtils; - -/** - * This Class was created by Patrick J - * on 09.06.16. (modified by opyale) - */ - -public class HighlightJsView extends WebView { - - private Language language = Language.AUTO_DETECT; - private Theme theme = Theme.DEFAULT; - - private boolean zoomSupport = false; - private boolean showLineNumbers = true; - private TextWrap textWrap = TextWrap.NO_WRAP; - - public HighlightJsView(Context context) { - - super(context); - setup(); - } - - public HighlightJsView(Context context, AttributeSet attrs) { - - super(context, attrs); - setup(); - } - - public HighlightJsView(Context context, AttributeSet attrs, int defStyleAttr) { - - super(context, attrs, defStyleAttr); - setup(); - } - - @SuppressLint("SetJavaScriptEnabled") - private void setup() { - - WebSettings settings = getSettings(); - - settings.setJavaScriptEnabled(true); - settings.setBuiltInZoomControls(true); - settings.setSupportZoom(zoomSupport); - settings.setDisplayZoomControls(false); - - setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); - - } - - private void changeZoomSettings(boolean enable) { - - this.zoomSupport = enable; - getSettings().setSupportZoom(enable); - } - - public void setSource(String source) { - - source = (source == null) ? " " : source; - - String html_content = SourceUtils.generateContent(source, theme.getName(), language.getName(), zoomSupport, showLineNumbers, textWrap); - loadDataWithBaseURL("file:///android_asset/", html_content, "text/html", "utf-8", null); - - } - - public void refresh() { - - super.reload(); - } - - public void setHighlightLanguage(Language language) { - this.language = language; - } - - public void setTheme(Theme theme) { - this.theme = theme; - } - - public void setTextWrap(TextWrap textWrap) { - this.textWrap = textWrap; - } - - public Language getHighlightLanguage() { - return language; - } - - public Theme getTheme() { - return theme; - } - - public void setZoomSupportEnabled(boolean supportZoom) { - changeZoomSettings(supportZoom); - } - - public void setShowLineNumbers(boolean showLineNumbers) { - this.showLineNumbers = showLineNumbers; - } - - public enum TextWrap { - NO_WRAP, WORD_WRAP, BREAK_ALL - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Language.java b/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Language.java deleted file mode 100644 index eefea0a6..00000000 --- a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Language.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.mian.gitnex.helpers.highlightjs.models; - -/** - * This Class was created by Patrick J - * on 09.06.16. - */ - -public enum Language { - AUTO_DETECT(null), - DISABLE_HIGHLIGHT("nohighlight"), - _1C("1c"), - ABNF("abnf"), - ACCESS_LOGS("accesslog"), - ADA("ada"), - ARM_ASSEMBLER("arm"), - AVR_ASSEMBLER("avrasm"), - ACTION_SCRIPT("actionscript"), - APACHE("apache"), - APPLE_SCRIPT("applescript"), - ASCII_DOC("asciidoc"), - ASPECT_J("aspectj"), - AUTO_HOTKEY("autohotkey"), - AUTO_IT("autoit"), - AXAPTA("axapta"), - AWK("awk"), - BASH("bash"), - SHELL("sh"), - ZSH("zsh"), - BASIC("basic"), - BNF("bnf"), - BRAINFUCK("brainfuck"), - C("c"), - C_SHARP("csharp"), - C_PLUS_PLUS("cpp"), - CACHE_OBJECT_SCRIPT("cos"), - C_MAKE("cmake"), - COQ("coq"), - CSP("csp"), - CSS("css"), - CAPTAIN_PROTO("capnproto"), - CLEAN("clean"), - CLOJURE("clojure"), - COFFEE_SCRIPT("coffeescript"), - CRMSH("crmsh"), - CRYSTAL("crystal"), - D("d"), - DNS_ZONE_FILE("dns"), - DOS("dos"), - BATCH("bat"), - DART("dart"), - DELPHI("delphi"), - DIFF("diff"), - DJANGO("django"), - DOCKER_FILE("dockerfile"), - DSCONFIG("dsconfig"), - DTS("dts"), - DUST("dust"), - EBNF("ebnf"), - ELIXIR("elixir"), - ELM("elm"), - ERLANG("erlang"), - EXCEL("excel"), - F_SHARP("fsharp"), - FIX("fix"), - FLIX("flix"), - FORTRAN("fortran"), - G_CODE("gcode"), - GAMS("gams"), - GAUSS("gauss"), - GHERKIN("gherkin"), - GO("go"), - GOLO("golo"), - GRADLE("gradle"), - GROOVY("groovy"), - HTML("html"), - XML("xml"), - HTTP("http"), - HAML("haml"), - HANDLEBARS("hbs"), - HASKELL("hs"), - HAXE("hx"), - HY("hy"), - INI("ini"), - INFORM7("i7"), - IRPF90("irpf90"), - JSON("json"), - JAVA("java"), - JAVA_SCRIPT("javascript"), - LASSO("lasso"), - LEAF("leaf"), - LESS("less"), - LDIF("ldif"), - LISP("lisp"), - LIVE_CODE_SERVER("livecodeserver"), - LIVE_SCRIPT("livescript"), - LLVM("llvm"), - LUA("lua"), - MAKEFILE("makefile"), - MARKDOWN("md"), - MATHEMATICA("mma"), - MATLAB("matlab"), - MAXIMA("maxima"), - MAYA_EMBEDDED_LANGUAGE("mel"), - MERCURY("mercury"), - MIZAR("mizar"), - MOJOLICIOUS("mojolicious"), - MONKEY("monkey"), - MOONSCRIPT("moonscript"), - N1QL("n1ql"), - NSIS("nsis"), - NGINX("nginx"), - NIMROD("nimrod"), - NIX("nix"), - O_CAML("ocaml"), - OBJECTIVE_C("objectivec"), - OPENGL_SHADING_LANGUAGE("glsl"), - OPEN_SCAD("scad"), - ORACLE_RULES_LANGUAGE("ruleslanguage"), - OXYGENE("oxygene"), - PF("pf"), - PHP("php"), - PARSER3("parser3"), - PERL("perl"), - PONY("pony"), - POWER_SHELL("ps"), - PROCESSING("processing"), - PROLOG("prolog"), - PROTOCOL_BUFFERS("protobuf"), - PUPPET("pp"), - PYTHON("python"), - PYTHON_PROFILER_RESULTS("profile"), - Q("k"), - QML("qml"), - R("r"), - RENDER_MAN_RIB("rib"), - RENDER_MAN_RSL("rsl"), - ROBOCONF("roboconf"), - RUBY("ruby"), - RUST("rust"), - SCSS("scss"), - SQL("sql"), - STEP_PART_21("p21"), - SCALA("scala"), - SCHEME("scheme"), - SCILAB("sci"), - SMALI("smali"), - SMALLTALK("smalltalk"), - STAN("stan"), - STATA("stata"), - STYLUS("stylus"), - SUB_UNIT("subunit"), - SWIFT("swift"), - TEST_ANYTHING_PROTOCOL("tap"), - TCL("tcl"), - TEX("tex"), - THRIFT("thrift"), - TP("tp"), - TWIG("twig"), - TYPE_SCRIPT("typescript"), - VB_NET("vbnet"), - VB_SCRIPT("vbscript"), - VHDL("vhdl"), - VALA("vala"), - VERILOG("v"), - VIM("vim"), - X86_ASSEMBLY("x86asm"), - XL("xl"), - X_QUERY("xq"), - ZEPHIR("zep"); - - private final String className; - - Language(String name) { - this.className = name; - } - - public String getName() { - return className; - } - -} diff --git a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Theme.java b/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Theme.java deleted file mode 100644 index 5dff9a1e..00000000 --- a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/models/Theme.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.mian.gitnex.helpers.highlightjs.models; - -/** - * This Class was created by Patrick J - * on 09.06.16. - */ - -public enum Theme { - AGATE("agate"), - ANDROID_STUDIO("androidstudio"), - ARDUINO_LIGHT("arduino-light"), - ARTA("arta"), - ASCETIC("ascetic"), - ATELIER_CAVE_DARK("atelier-cave-dark"), - ATELIER_CAVE_LIGHT("atelier-cave-light"), - ATELIER_DUNE_DARK("atelier-dune-dark"), - ATELIER_DUNE_LIGHT("atelier-dune-light"), - ATELIER_ESTUARY_DARK("atelier-estuary-dark"), - ATELIER_ESTUARY_LIGHT("atelier-estuary-light"), - ATELIER_FOREST_DARK("atelier-forest-dark"), - ATELIER_FOREST_LIGHT("atelier-forest-light"), - ATELIER_HEATH_DARK("atelier-heath-dark"), - ATELIER_HEATH_LIGHT("atelier-heath-light"), - ATELIER_LAKESIDE_DARK("atelier-lakeside-dark"), - ATELIER_LAKESIDE_LIGHT("atelier-lakeside-light"), - ATELIER_PLATEAU_DARK("atelier-plateau-dark"), - ATELIER_PLATEAU_LIGHT("atelier-plateau-light"), - ATELIER_SAVANNA_DARK("atelier-savanna-dark"), - ATELIER_SAVANNA_LIGHT("atelier-savanna-light"), - ATELIER_SEASIDE_DARK("atelier-seaside-dark"), - ATELIER_SEASIDE_LIGHT("atelier-seaside-light"), - ATELIER_SULPHURPOOL_DARK("atelier-sulphurpool-dark"), - ATELIER_SULPHURPOOL_LIGHT("atelier-sulphurpool-light"), - ATOM_ONE_DARK("atom-one-dark"), - ATOM_ONE_LIGHT("atom-one-light"), - BROWN_PAPER("brown-paper"), - CODEPEN_EMBED("codepen-embed"), - COLOR_BREWER("color-brewer"), - DARK("dark"), - DARKULA("darkula"), - DEFAULT("default"), - DOCCO("docco"), - DRAKULA("drakula"), - FAR("far"), - FOUNDATION("foundaiton"), - GITHUB("github"), - GITHUB_GIST("github-gist"), - GOOGLECODE("googlecode"), - GRAYSCALE("grayscale"), - GRUVBOX_DARK("gruvbox-dark"), - GRUVBOX_LIGHT("gruvbox-light"), - HOPSCOTCH("hopscotch"), - HYBRID("hybrid"), - IDEA("idea"), - IR_BLACK("ir-black"), - KIMBIE_DARK("kimbie.dark"), - KIMBIE_LIGHT("kimbie.light"), - MAGULA("magula"), - MONO_BLUE("mono-blue"), - MONOKAI("monokai"), - MONOKAI_SUBLIME("monokai-sublime"), - OBSIDIAN("obsidian"), - OCEAN("ocean"), - PARAISO_DARK("paraiso-dark"), - PARAISO_LIGHT("paraiso-light"), - POJOAQUE("pojoaque"), - PURE_BASIC("purebasic"), - QT_CREATOR_DARK("qtcreator_dark"), - QT_CREATOR_LIGHT("qtcreator_light"), - RAILSCASTS("railscasts"), - RAINBOX("rainbow"), - SCHOOL_BOOK("school-book"), - SOLARIZED_DARK("solarized-dark"), - SOLARIZED_LIGHT("solarized-light"), - SUNBURST("sunburst"), - TOMORROW("tomorrow"), - TOMORROW_NIGHT_BLUE("tomorrow-night-blue"), - TOMORROW_NIGHT_BRIGHT("tomorrow-night-bright"), - TOMORROW_NIGHT("tomorrow-night"), - TOMORROW_NIGHT_EIGHTIES("tomorrow-night-eighties"), - VS("vs"), - X_CODE("xcode"), - XT256("xt256"), - ZENBURN("zenburn"); - - private final String themeName; - - Theme(String themeName) { - this.themeName = themeName; - } - - public String getName() { - return themeName; - } - -} diff --git a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/utils/SourceUtils.java b/app/src/main/java/org/mian/gitnex/helpers/highlightjs/utils/SourceUtils.java deleted file mode 100644 index 36589c92..00000000 --- a/app/src/main/java/org/mian/gitnex/helpers/highlightjs/utils/SourceUtils.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.mian.gitnex.helpers.highlightjs.utils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import org.mian.gitnex.helpers.highlightjs.HighlightJsView; - -/** - * This Class was created by Patrick J - * on 09.06.16. (modified by opyale) - */ - -public class SourceUtils { - - public static String generateContent(String source, @NonNull String theme, @Nullable String language, boolean supportZoom, boolean showLineNumbers, - HighlightJsView.TextWrap textWrap) { - - return getStylePageHeader(supportZoom, textWrap) + - getSourceForTheme(theme) + - (showLineNumbers ? getLineNumberStyling() : "") + - getScriptPageHeader(showLineNumbers) + - getSourceForLanguage(source, language) + - getTemplateFooter(); - } - - private static String getStylePageHeader(boolean enableZoom, HighlightJsView.TextWrap textWrap) { - - String preTag; - - switch (textWrap) { - - case WORD_WRAP: - preTag = "pre { white-space: pre-wrap; word-wrap: break-word; }"; - break; - - case BREAK_ALL: - preTag = "pre { white-space: pre-wrap; word-break: break-all; }"; - break; - - default: - preTag = "pre { }"; - break; - - } - - return "" + - "" + - "" + - "" + - (enableZoom ? "" : "") + - ""; - } - - private static String getScriptPageHeader(boolean showLineNumbers) { - - return "" + - (showLineNumbers ? "" : "") + - "" + - (showLineNumbers ? "" : "") + - ""; - } - - private static String getLineNumberStyling() { - - return ""; - } - - private static String getTemplateFooter() { - - return ""; - } - - private static String escapeCode(String code) { - - return code.replaceAll("<", "<") - .replaceAll(">", ">"); - } - - private static String getSourceForTheme(String theme) { - - return String.format("", theme); - } - - private static String getSourceForLanguage(String source, String language) { - - if (language != null) { - return String.format("
%s
", language, escapeCode(source)); - } else { - return String.format("
%s
", escapeCode(source)); - } - - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/mian/gitnex/notifications/Notifications.java b/app/src/main/java/org/mian/gitnex/notifications/Notifications.java index 37d90a5f..68cb399c 100644 --- a/app/src/main/java/org/mian/gitnex/notifications/Notifications.java +++ b/app/src/main/java/org/mian/gitnex/notifications/Notifications.java @@ -1,13 +1,17 @@ package org.mian.gitnex.notifications; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Context; +import android.graphics.Color; import android.os.Build; import androidx.work.Constraints; import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.NetworkType; import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.R; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Version; import java.util.concurrent.TimeUnit; @@ -20,13 +24,52 @@ public class Notifications { private static int notificationsSupported = -1; - private static void checkVersion(TinyDB tinyDB) { + public static int uniqueNotificationId(Context context) { - String currentVersion = tinyDB.getString("giteaVersion"); + TinyDB tinyDB = TinyDB.getInstance(context); - if(tinyDB.getBoolean("loggedInMode") && !currentVersion.isEmpty()) { + int previousNotificationId = tinyDB.getInt("previousNotificationId", 0); + int nextPreviousNotificationId = previousNotificationId == Integer.MAX_VALUE ? 0 : previousNotificationId + 1; + + tinyDB.putInt("previousNotificationId", nextPreviousNotificationId); + return previousNotificationId; + + } + + public static void createChannels(Context context) { + + TinyDB tinyDB = TinyDB.getInstance(context); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + + // Delete old notification channels + notificationManager.deleteNotificationChannel(context.getPackageName()); // TODO Can be removed in future versions + + // Create new notification channels + NotificationChannel mainChannel = new NotificationChannel(Constants.mainNotificationChannelId, context.getString(R.string.mainNotificationChannelName), NotificationManager.IMPORTANCE_DEFAULT); + mainChannel.setDescription(context.getString(R.string.mainNotificationChannelDescription)); + + if(tinyDB.getBoolean("notificationsEnableVibration", true)) { + mainChannel.setVibrationPattern(new long[] { 1000, 1000 }); + mainChannel.enableVibration(true); + } else { + mainChannel.enableVibration(false); + } + + if(tinyDB.getBoolean("notificationsEnableLights", true)) { + mainChannel.setLightColor(tinyDB.getInt("notificationsLightColor", Color.GREEN)); + mainChannel.enableLights(true); + } else { + mainChannel.enableLights(false); + } + + NotificationChannel downloadChannel = new NotificationChannel(Constants.downloadNotificationChannelId, context.getString(R.string.fileViewerNotificationChannelName), NotificationManager.IMPORTANCE_LOW); + downloadChannel.setDescription(context.getString(R.string.fileViewerNotificationChannelDescription)); + + notificationManager.createNotificationChannel(mainChannel); + notificationManager.createNotificationChannel(downloadChannel); - notificationsSupported = new Version(currentVersion).higherOrEqual("1.12.3") ? 1 : 0; } } @@ -56,7 +99,7 @@ public class Notifications { constraints.setRequiresDeviceIdle(false); } - int pollingDelayMinutes = Math.max(tinyDB.getInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay), 15); + int pollingDelayMinutes = Math.max(tinyDB.getInt("pollingDelayMinutes", Constants.defaultPollingDelay), 15); PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(NotificationsWorker.class, pollingDelayMinutes, TimeUnit.MINUTES) .setConstraints(constraints.build()) @@ -68,4 +111,15 @@ public class Notifications { } } } + + private static void checkVersion(TinyDB tinyDB) { + + String currentVersion = tinyDB.getString("giteaVersion"); + + if(tinyDB.getBoolean("loggedInMode") && !currentVersion.isEmpty()) { + + notificationsSupported = new Version(currentVersion).higherOrEqual("1.12.3") ? 1 : 0; + } + } + } diff --git a/app/src/main/java/org/mian/gitnex/notifications/NotificationsWorker.java b/app/src/main/java/org/mian/gitnex/notifications/NotificationsWorker.java index 1972f016..cdf40405 100644 --- a/app/src/main/java/org/mian/gitnex/notifications/NotificationsWorker.java +++ b/app/src/main/java/org/mian/gitnex/notifications/NotificationsWorker.java @@ -1,15 +1,11 @@ package org.mian.gitnex.notifications; import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.media.RingtoneManager; -import android.os.Build; -import android.util.Log; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -20,7 +16,8 @@ import org.mian.gitnex.R; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.helpers.StaticGlobalVariables; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import java.util.Date; import java.util.List; @@ -34,7 +31,6 @@ import retrofit2.Response; public class NotificationsWorker extends Worker { private static final int MAXIMUM_NOTIFICATIONS = 100; - private static final long[] VIBRATION_PATTERN = new long[]{ 1000, 1000 }; private final Context context; private final TinyDB tinyDB; @@ -45,17 +41,16 @@ public class NotificationsWorker extends Worker { this.context = context; this.tinyDB = TinyDB.getInstance(context); + } @NonNull @Override public Result doWork() { - String token = "token " + tinyDB.getString(tinyDB.getString("loginUid") + "-token"); + int notificationLoops = tinyDB.getInt("pollingDelayMinutes", Constants.defaultPollingDelay) >= 15 ? 1 : Math.min(15 - tinyDB.getInt("pollingDelayMinutes"), 10); - int notificationLoops = tinyDB.getInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay) >= 15 ? 1 : Math.min(15 - tinyDB.getInt("pollingDelayMinutes"), 10); - - for(int i=0; i> call = RetrofitClient .getApiInterface(context) - .getNotificationThreads(token, false, new String[]{"unread"}, previousRefreshTimestamp, + .getNotificationThreads(Authorization.get(context), false, new String[]{"unread"}, previousRefreshTimestamp, null, 1, MAXIMUM_NOTIFICATIONS); Response> response = call.execute(); @@ -76,34 +71,24 @@ public class NotificationsWorker extends Worker { List notificationThreads = response.body(); - if(!notificationThreads.isEmpty()) { - + if(!notificationThreads.isEmpty()) sendNotification(notificationThreads); - } tinyDB.putString("previousRefreshTimestamp", AppUtil.getTimestampFromDate(context, new Date())); } - else { - Log.e("onError", String.valueOf(response.code())); - } - } - catch(Exception e) { - - Log.e("onError", e.toString()); - } + } catch(Exception ignored) {} try { - if(notificationLoops > 1 && i < (notificationLoops - 1)) { Thread.sleep(60000 - (System.currentTimeMillis() - startPollingTime)); } - } - catch (InterruptedException ignored) {} + } catch (InterruptedException ignored) {} } return Result.success(); + } private void sendNotification(List notificationThreads) { @@ -112,7 +97,6 @@ public class NotificationsWorker extends Worker { PendingIntent pendingIntent = getPendingIntent(); NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); - attachNotificationChannel(notificationManagerCompat); Notification summaryNotification = new NotificationCompat.Builder(context, context.getPackageName()) .setContentTitle(context.getString(R.string.newMessages)) @@ -122,15 +106,13 @@ public class NotificationsWorker extends Worker { .setGroupSummary(true) .setAutoCancel(true) .setContentIntent(pendingIntent) + .setChannelId(Constants.mainNotificationChannelId) .build(); notificationManagerCompat.notify(summaryId, summaryNotification); for(NotificationThread notificationThread : notificationThreads) { - NotificationManagerCompat notificationManagerCompat1 = NotificationManagerCompat.from(context); - attachNotificationChannel(notificationManagerCompat1); - String subjectUrl = notificationThread.getSubject().getUrl(); String issueId = context.getResources().getString(R.string.hash) + subjectUrl.substring(subjectUrl.lastIndexOf("/") + 1); String notificationHeader = issueId + " " + notificationThread.getSubject().getTitle() + " " + String.format(context.getResources().getString(R.string.notificationExtraInfo), notificationThread.getRepository().getFull_name(), notificationThread.getSubject().getType()); @@ -140,49 +122,8 @@ public class NotificationsWorker extends Worker { .setGroup(context.getPackageName()) .setContentIntent(pendingIntent); - pushNotification(notificationManagerCompat1, builder1.build()); - } - } + notificationManagerCompat.notify(Notifications.uniqueNotificationId(context), builder1.build()); - private void pushNotification(NotificationManagerCompat notificationManagerCompat, Notification notification) { - - int previousNotificationId = tinyDB.getInt("previousNotificationId", 0); - int nextPreviousNotificationId = previousNotificationId > 71951418 ? 0 : previousNotificationId + 1; - - tinyDB.putInt("previousNotificationId", nextPreviousNotificationId); - notificationManagerCompat.notify(previousNotificationId, notification); - } - - private void attachNotificationChannel(NotificationManagerCompat notificationManagerCompat) { - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - - NotificationChannel notificationChannel = new NotificationChannel(context.getPackageName(), context.getString(R.string.appName), - NotificationManager.IMPORTANCE_DEFAULT); - - notificationChannel.setDescription(context.getString(R.string.notificationChannelDescription)); - - if(tinyDB.getBoolean("notificationsEnableVibration", true)) { - - notificationChannel.setVibrationPattern(VIBRATION_PATTERN); - notificationChannel.enableVibration(true); - } - else { - - notificationChannel.enableVibration(false); - } - - if(tinyDB.getBoolean("notificationsEnableLights", true)) { - - notificationChannel.setLightColor(tinyDB.getInt("notificationsLightColor", Color.GREEN)); - notificationChannel.enableLights(true); - } - else { - - notificationChannel.enableLights(false); - } - - notificationManagerCompat.createNotificationChannel(notificationChannel); } } @@ -193,6 +134,7 @@ public class NotificationsWorker extends Worker { .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setChannelId(Constants.mainNotificationChannelId) .setAutoCancel(true); if(tinyDB.getBoolean("notificationsEnableLights", true)) { @@ -201,11 +143,8 @@ public class NotificationsWorker extends Worker { } if(tinyDB.getBoolean("notificationsEnableVibration", true)) { - - builder.setVibrate(VIBRATION_PATTERN); - } - else { - + builder.setVibrate(new long[]{ 1000, 1000 }); + } else { builder.setVibrate(null); } @@ -219,5 +158,6 @@ public class NotificationsWorker extends Worker { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); return PendingIntent.getActivity(context, 0, intent, 0); + } } diff --git a/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java index a263f338..1e074180 100644 --- a/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java +++ b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java @@ -18,6 +18,7 @@ import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import java.io.IOException; import java.util.ArrayList; @@ -222,7 +223,7 @@ public class ReactionSpinner extends HorizontalScrollView { if(response.isSuccessful() && response.body() != null) { allowedReactions.addAll(Arrays.asList(response.body().getAllowed_reactions())); } else { - allowedReactions.addAll(Arrays.asList("+1", "-1", "laugh", "hooray", "confused", "heart", "rocket", "eyes")); + allowedReactions.addAll(Arrays.asList(Constants.fallbackReactions)); } return allowedReactions; diff --git a/app/src/main/res/drawable/ic_directory.xml b/app/src/main/res/drawable/ic_directory.xml index 3165bd97..0756ffd6 100644 --- a/app/src/main/res/drawable/ic_directory.xml +++ b/app/src/main/res/drawable/ic_directory.xml @@ -4,10 +4,7 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:fillColor="?attr/iconsColor" + android:pathData="M3.75,4.5a0.25,0.25 0,0 0,-0.25 0.25v14.5c0,0.138 0.112,0.25 0.25,0.25h16.5a0.25,0.25 0,0 0,0.25 -0.25V7.687a0.25,0.25 0,0 0,-0.25 -0.25h-8.471a1.75,1.75 0,0 1,-1.447 -0.765L8.928,4.61a0.25,0.25 0,0 0,-0.208 -0.11H3.75zM2,4.75C2,3.784 2.784,3 3.75,3h4.971c0.58,0 1.12,0.286 1.447,0.765l1.404,2.063a0.25,0.25 0,0 0,0.207 0.11h8.471c0.966,0 1.75,0.783 1.75,1.75V19.25A1.75,1.75 0,0 1,20.25 21H3.75A1.75,1.75 0,0 1,2 19.25V4.75z" + android:fillType="evenOdd"/> diff --git a/app/src/main/res/drawable/ic_file.xml b/app/src/main/res/drawable/ic_file.xml index 45577def..b2c86a3c 100644 --- a/app/src/main/res/drawable/ic_file.xml +++ b/app/src/main/res/drawable/ic_file.xml @@ -4,17 +4,7 @@ android:viewportWidth="24" android:viewportHeight="24"> - + android:fillColor="?attr/iconsColor" + android:pathData="M5,2.5a0.5,0.5 0,0 0,-0.5 0.5v18a0.5,0.5 0,0 0,0.5 0.5h14a0.5,0.5 0,0 0,0.5 -0.5L19.5,8.5h-4a2,2 0,0 1,-2 -2v-4L5,2.5zM15,2.5v4a0.5,0.5 0,0 0,0.5 0.5h4a0.5,0.5 0,0 0,-0.146 -0.336l-4.018,-4.018A0.5,0.5 0,0 0,15 2.5zM3,3a2,2 0,0 1,2 -2h9.982a2,2 0,0 1,1.414 0.586l4.018,4.018A2,2 0,0 1,21 7.018L21,21a2,2 0,0 1,-2 2L5,23a2,2 0,0 1,-2 -2L3,3z" + android:fillType="evenOdd"/> diff --git a/app/src/main/res/drawable/ic_question.xml b/app/src/main/res/drawable/ic_question.xml index cbe0237d..ca41c5a6 100644 --- a/app/src/main/res/drawable/ic_question.xml +++ b/app/src/main/res/drawable/ic_question.xml @@ -4,24 +4,10 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:fillColor="?attr/iconsColor" + android:pathData="M10.97,8.265a1.45,1.45 0,0 0,-0.487 0.57,0.75 0.75,0 0,1 -1.341,-0.67c0.2,-0.402 0.513,-0.826 0.997,-1.148C10.627,6.69 11.244,6.5 12,6.5c0.658,0 1.369,0.195 1.934,0.619a2.45,2.45 0,0 1,1.004 2.006c0,1.033 -0.513,1.72 -1.027,2.215 -0.19,0.183 -0.399,0.358 -0.579,0.508l-0.147,0.123a4.329,4.329 0,0 0,-0.435 0.409v1.37a0.75,0.75 0,1 1,-1.5 0v-1.473c0,-0.237 0.067,-0.504 0.247,-0.736 0.22,-0.28 0.486,-0.517 0.718,-0.714l0.183,-0.153 0.001,-0.001c0.172,-0.143 0.324,-0.27 0.47,-0.412 0.368,-0.355 0.569,-0.676 0.569,-1.136a0.953,0.953 0,0 0,-0.404 -0.806C12.766,8.118 12.384,8 12,8c-0.494,0 -0.814,0.121 -1.03,0.265zM13,17a1,1 0,1 1,-2 0,1 1,0 0,1 2,0z"/> - + android:fillColor="?attr/iconsColor" + android:pathData="M12,1C5.925,1 1,5.925 1,12s4.925,11 11,11 11,-4.925 11,-11S18.075,1 12,1zM2.5,12a9.5,9.5 0,1 1,19 0,9.5 9.5,0 0,1 -19,0z" + android:fillType="evenOdd"/> diff --git a/app/src/main/res/drawable/ic_submodule.xml b/app/src/main/res/drawable/ic_submodule.xml new file mode 100644 index 00000000..2f2eb4cd --- /dev/null +++ b/app/src/main/res/drawable/ic_submodule.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_symlink.xml b/app/src/main/res/drawable/ic_symlink.xml new file mode 100644 index 00000000..9a363867 --- /dev/null +++ b/app/src/main/res/drawable/ic_symlink.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_file_view.xml b/app/src/main/res/layout/activity_file_view.xml index 57e080b2..4b857eb3 100644 --- a/app/src/main/res/layout/activity_file_view.xml +++ b/app/src/main/res/layout/activity_file_view.xml @@ -1,5 +1,6 @@ + android:visibility="gone"> - + @@ -86,7 +88,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" - android:orientation="vertical"> + android:orientation="vertical" + android:visibility="gone"> - + android:visibility="gone" + tools:visibility="visible" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_files.xml b/app/src/main/res/layout/fragment_files.xml index ed740290..e3cc0fd1 100644 --- a/app/src/main/res/layout/fragment_files.xml +++ b/app/src/main/res/layout/fragment_files.xml @@ -46,10 +46,11 @@ diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 5be95c23..919ccfb2 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -21,80 +21,72 @@ android:contentDescription="@string/generalImgContentText" android:scaleType="centerCrop" /> - + android:gravity="center" + android:orientation="vertical" + android:padding="16dp"> + + + + + + + + + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical"> + android:src="@drawable/ic_language" + app:tint="@color/colorWhite" /> - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 353a9c7c..f8db344e 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -81,38 +81,6 @@ - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_files.xml b/app/src/main/res/layout/list_files.xml index 03839bb7..9599fed4 100644 --- a/app/src/main/res/layout/list_files.xml +++ b/app/src/main/res/layout/list_files.xml @@ -1,6 +1,7 @@ - + - - - - - + android:layout_marginBottom="10dp" + android:orientation="horizontal"> + + + + + + - - - - - - - - - - - - + + - + diff --git a/app/src/main/res/layout/list_pr.xml b/app/src/main/res/layout/list_pr.xml index a62e94a6..a7f49b8d 100644 --- a/app/src/main/res/layout/list_pr.xml +++ b/app/src/main/res/layout/list_pr.xml @@ -5,119 +5,82 @@ android:id="@+id/relativeLayoutFrame" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_margin="15dp" + android:orientation="vertical" android:background="?attr/primaryBackgroundColor"> - + - - - - - - - - - - - + android:layout_marginBottom="10dp" + android:orientation="horizontal"> - - - - - - - - - - - - - - - - - + android:gravity="top|center_vertical" + android:textAlignment="gravity" + android:textColor="?attr/primaryTextColor" + android:textSize="18sp" /> - + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml index 438a55d7..c607d16a 100644 --- a/app/src/main/res/layout/nav_header.xml +++ b/app/src/main/res/layout/nav_header.xml @@ -1,6 +1,7 @@ @@ -10,106 +11,79 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/generalImgContentText" - android:scaleType="centerCrop" /> + android:scaleType="centerCrop" + tools:srcCompat="@tools:sample/backgrounds/scenic" /> - + android:gravity="bottom" + android:orientation="vertical" + android:paddingStart="20dp" + android:paddingTop="20dp" + android:paddingEnd="20dp" + android:paddingBottom="20dp"> + android:layout_height="wrap_content" + android:orientation="horizontal"> - - - - - - - - - - - - - + + - - - - - - - - - - - - + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:reverseLayout="true" /> - + + + + + + + + + + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 84a97890..4028af15 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -479,7 +479,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -530,7 +530,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -562,6 +562,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -619,4 +621,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 44dad665..3677477e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -476,7 +476,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3c573b70..c3e6a14b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -476,7 +476,7 @@ Datei herunterladen Bitte warte, bis die Datei in den Speicher geladen wurde Datei erfolgreich gespeichert - Dieser Dateityp wird nicht vom Datei-Viewer unterstützt. Du kannst sie stattdessen übers Menü herunterladen? + Diese(r) Dateityp / Dateigröße wird vom Dateibetrachter nicht unterstützt. Du kannst es über das Menü herunterladen. Diese Datei löschen Diese Datei bearbeiten Lösche %1$s @@ -527,7 +527,7 @@ Entwurf wurde automatisch gespeichert. Themen, Schriften, Abzeichen PDF-Modus, Quellcode Theme - SSL-Zertifikate, Cache + Biometrische Authentifizierung, SSL-Zertifikate, Cache Sprachen Absturzberichte Wenn dir GitNex gefällt, hinterlassen Feedback @@ -559,6 +559,8 @@ Farbe auswählen Neue Nachrichten Du hast %d neue Nachrichten. + Benachrichtigungen + Das ist der Haupt-Nachrichten-Kanal von GitNex. Gelesen Ungelesen Repository Einstellungen @@ -615,5 +617,14 @@ Keine biometrischen Funktionen verfügbar Biometrischen Funktionen zur zeit nicht verfügbar Biometrische Daten in den Telefoneinstellungen registrieren - Login-ID \'%s\' in Zwischenablage kopiert + Login ID \'%s\' in Zwischenablage kopiert + + Download läuft + Lade %s herunter + Download erfolgreich + %s heruntergeladen + Download fehlgeschlagen + Konnte %s nicht downloaden + Download Manager + Zeigt den Fortschritt laufender Downloads an diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9d55389e..ec58669c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -476,7 +476,7 @@ Descargar este archivo Por favor espere a que el dispositivo cargue File saved successfully - Este tipo de archivo no es compatible con el visor de archivos. ¿Descargarlo en su lugar desde el menú de tres puntos? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index a224420f..dc713a4b 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -479,7 +479,7 @@ بارگیری این پرونده لطفا منتظر بمانید تا پرونده در حافظه بارگذاری شود پرونده با موفقیت ذخیره شد - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. حذف این پرونده ویرایش این پرونده حذف %1$s @@ -530,7 +530,7 @@ پیش نویس خودکار ذخیره شد. پوسته‌ها، فونت‌ها، نشانگرها حالت PDF، پوسته کد منبع - SSL certificates, cache + Biometric authentication, SSL certificates, cache زبان‌ها گزارش‌های خرابی اگر GitNex را می‌پسندید می‌توانید به آن امتیاز دهید @@ -562,6 +562,8 @@ انتخاب رنگ پیام‌های جدید You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. خوانده شده خوانده نشده تنظیمات مخزن @@ -619,4 +621,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index eb5586ae..d048fea1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -476,7 +476,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3f2de461..e1c761e4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -476,7 +476,7 @@ Télécharger ce fichier Chargement du fichier dans la mémoire Fichier sauvegardé avec succès - La visionneuse ne prend pas en charge ce type de fichier. Télécharger ce fichier ? + Le fichier incompatible ou trop grand pour la visionneuse. Vous pouvez toutefois le télécharger depuis le menu. Supprimer ce fichier Éditer ce fichier Supprimer %1$s @@ -527,7 +527,7 @@ Brouillon sauvegardé automatiquement Thèmes, polices, badges Mode PDF, couleurs du code source - Certificats SSL, cache + Biométrie, certificats SSL, cache Langues Rapports de plantage Vous aimez GitNex ? Mettez-lui un pouce ! @@ -559,6 +559,8 @@ Couleur Nouveaux messages Vous avez %d nouvelles notifications. + Notifications + Le canal de notifications principal de GitNex. Lu Non lu Paramètres du dépôt @@ -616,4 +618,13 @@ La biométrie n’est pas disponible pour le moment Paramétrer la biométrie dans les réglages de l\'appareil ID \'%s\' copié dans le presse-papier + + Téléchargement en cours + %s téléchargé + Téléchargement terminé + %s téléchargé + Échec du téléchargement + Impossible de télécharger %s + Gestionnaire de téléchargement + Indique la progression des téléchargements diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d3410f56..be22cfcb 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -478,7 +478,7 @@ autorizzazione
Scarica Questo File Attendere che il file venga caricato in memoria File salvato con successo - Questo tipo di file non è supportato nel visualizzatore di file. Scaricarlo invece dal menu con i tre punti? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -529,7 +529,7 @@ autorizzazione
Draft was saved automatically. Themes, fonts, badges Modalità PDF, tema codice sorgente - SSL certificates, cache + Biometric authentication, SSL certificates, cache Lingue Rapporti crash If you like GitNex you can give it a thumbs up @@ -561,6 +561,8 @@ autorizzazione
Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Letto Non letto Repository Settings @@ -618,4 +620,13 @@ autorizzazione
Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 089ece76..03dbc7b9 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -476,7 +476,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 248a2ef2..1cb1ba72 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -476,7 +476,7 @@ Pobierz ten plik Poczekaj aż plik załaduje się do pamięci File saved successfully - Ten typ pliku nie jest obsługiwany w przeglądarce plików. Pobrać go z trzech kropkowanych menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 60ecdb9c..0aa71eb1 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -476,7 +476,7 @@ Baixar este arquivo Aguarde que o arquivo seja carregado para a memória Arquivo salvo com êxito - Esse tipo de arquivo não é suportado no visualizador de arquivos. Baixe-o no menu de três pontos? + This file type/size is not supported in file viewer. You can download it from the menu. Excluir Este Arquivo Editar Este Arquivo Excluir %1$s @@ -527,7 +527,7 @@ O rascunho foi salvo automaticamente. Themes, fonts, badges Modo PDF, tema do código fonte - SSL certificates, cache + Biometric authentication, SSL certificates, cache Idiomas Relatórios de erros Se você gosta do GitNex você pode dar um joinha @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Lida Não Lida Configurações do repositório @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3c780a9c..fc331922 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -476,7 +476,7 @@ Скачать этот файл Пожалуйста дождитесь загрузки файла Файл успешно сохранён - Этот тип файла не поддерживается в средстве просмотра файлов. Загрузить его? + This file type/size is not supported in file viewer. You can download it from the menu. Удалить этот файл Редактировать этот файл Удалить %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF режим, исходный код темы - SSL certificates, cache + Biometric authentication, SSL certificates, cache Языки Отчёты об ошибках If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Прочитано Непрочитано Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 98b4d0a0..2bccf393 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -476,7 +476,7 @@ Преузми овај фајл Сачекај да се фајл учита у меморију File saved successfully - Прегледач фајлова не подржава овај тип. Да ли желиш да преузмеш фајл? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b5d85acf..68cb0dd0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -476,7 +476,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f522a316..8b1c8fe7 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -476,7 +476,7 @@ Завантажити цей файл Зачекайте, поки файл завантажиться Файл успішно збережений - Цей тип файлу не підтримується у переглядачі файлів. Завантажити його з меню? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -527,7 +527,7 @@ Draft was saved automatically. Themes, fonts, badges Режим PDF-версії, тема відображення коду - SSL certificates, cache + Biometric authentication, SSL certificates, cache Мови Звіти про падіння If you like GitNex you can give it a thumbs up @@ -559,6 +559,8 @@ Choose Color New messages You\'ve got %d new notifications. + Notifications + This is the main notification channel of GitNex. Read Unread Repository Settings @@ -616,4 +618,13 @@ Biometric features are currently unavailable Enroll biometric from phone settings Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9ccca3ee..6b7d3df3 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -476,7 +476,7 @@ 下载此文件 请等待文件加载到内存 文件保存成功 - 这个文件类型在文件查看器中不支持。从三个虚线菜单下载吗? + This file type/size is not supported in file viewer. You can download it from the menu. 删除此文件 编辑此文件 删除 %1$s @@ -527,7 +527,7 @@ 草稿已自动保存。 主题、字体、徽章 PDF 模式,源代码主题 - SSL 证书、缓存 + Biometric authentication, SSL certificates, cache 语言 崩溃报告 如果你喜欢GitNex,你可以给它点赞 @@ -559,6 +559,8 @@ 选择颜色 新消息 您有%d条新通知 + Notifications + This is the main notification channel of GitNex. 已读 未读 存储库设置 @@ -616,4 +618,13 @@ 生物特征功能当前不可用 从手机设置中注册生物识别 登录 ID \'%s\' 已复制到剪贴板 + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + Download manager + Indicates the progress of ongoing downloads diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c3b502c5..784ad7a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -578,7 +578,7 @@ Download This File Please wait for the file to load to memory File saved successfully - This file type is not supported in file viewer. Download it instead from the three dotted menu? + This file type/size is not supported in file viewer. You can download it from the menu. Delete This File Edit This File Delete %1$s @@ -639,7 +639,7 @@ Themes, fonts, badges PDF mode, source code theme - SSL certificates, cache + Biometric authentication, SSL certificates, cache Languages Crash reports If you like GitNex you can give it a thumbs up @@ -674,7 +674,8 @@ Choose Color New messages You\'ve got %d new notifications. - This is the main notification channel of GitNex. + Notifications + This is the main notification channel of GitNex. - %s (%s) Read @@ -744,4 +745,15 @@ Login ID \'%s\' copied to clipboard + + Download in progress + Downloading %s + Download successful + Downloaded %s + Download failed + Couldn\'t download %s + + Download manager + Indicates the progress of ongoing downloads + diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml index 760446c4..34727444 100644 --- a/app/src/main/res/xml/changelog.xml +++ b/app/src/main/res/xml/changelog.xml @@ -1,7 +1,7 @@ - + Under development diff --git a/build.gradle b/build.gradle index 6bdfda9e..cec44c6f 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:4.1.3' } } diff --git a/fastlane/metadata/android/en-US/changelogs/350.txt b/fastlane/metadata/android/en-US/changelogs/350.txt new file mode 100644 index 00000000..98c039b3 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/350.txt @@ -0,0 +1,13 @@ +3.5.0 + +New: Render emoji in issue/pr titles, commit message and markdown files +New: Cron tasks (Admin only) +New: Biometric support to unlock the app +New: Switch to saved instance when coming from email/links +New: Copy user login id to clipboard +New: Progress notification for downloading files +New: Rewrite file viewer in native code to move away from traditional js to load files faster + +Release Notes: https://codeberg.org/gitnex/GitNex/releases + +Release Blog: https://gitnex.codeberg.page/posts/350.html diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png index 0679ccab..e32ba5d1 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png index 441e52b9..99d4c851 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png index 0f14b5d2..03aa40b5 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/006.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/006.png index fc654b6d..be0b4e19 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/006.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/006.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/007.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/007.png index 5e9110d7..dc3e0c76 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/007.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/007.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/008.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/008.png index 58c4ed90..9d7e8abe 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/008.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/008.png differ