WHAT IS ICANN?
The global Internet community working together to promote the stability and integrity of the Internet.
A web domain name always ends with an extension of 2 or 3 characters. These characters can signify the country the website address is associated with or the type of organization; but this isn't always the case - more on that below.
The Internet Corporation for Assigned Names and Numbers (ICANN) is an internationally organized, non-profit corporation that has responsibility for Internet Protocol (IP) address space allocation, protocol identifier assignment, generic (gTLD) and country code (ccTLD) Top-Level Domain name system management, and root server system management functions. These services were originally performed under U.S. Government contract by the Internet Assigned Numbers Authority (IANA) and other entities. ICANN now performs the IANA function.
As a private-public partnership, ICANN is dedicated to preserving the operational stability of the Internet; to promoting competition; to achieving broad representation of global Internet communities; and to developing policy appropriate to its mission through bottom-up, consensus-based processes.
- What is ICANN's Role?
- ICANN is responsible for coordinating the management of the technical elements of the DNS to ensure universal resolvability so that all users of the Internet can find all valid addresses. It does this by overseeing the distribution of unique technical identifiers used in the Internet's operations, and delegation of Top-Level Domain names (such as .com, .info, etc.).
- Other issues of concern to Internet users, such as the rules for financial transactions, Internet content control, unsolicited commercial email (spam), and data protection are outside the range of ICANN's mission of technical coordination.
- How does ICANN work?
- Within ICANN's structure, governments and international treaty organizations work in partnership with businesses, organizations, and skilled individuals involved in building and sustaining the global Internet. Innovation and continuing growth of the Internet bring forth new challenges for maintaining stability. Working collectively, ICANN's participants address those issues that directly concern ICANN's mission of technical coordination. Consistent with the principle of maximum self-regulation in the high-tech economy, ICANN is perhaps the foremost example of collaboration by the various constituents of the Internet community.
- ICANN is governed by an internationally diverse Board of Directors overseeing the policy development process. ICANN's President directs an international staff, working from three continents, who ensure that ICANN meets its operational commitment to the Internet community.
- Designed to respond to the demands of rapidly changing technologies and economies, the flexible, readily implemented policy development process originates in the three Supporting Organizations. Advisory Committees from individual user organizations, and technical communities work with the Supporting Organizations to create appropriate and effective policies. Over eighty governments closely advise the Board of Directors via the Governmental Advisory Committee.
CONNECT WITH US


Generative Crochet
Generate. Experiment. Be surprised.
With this generator, you can discover virtually an infinite amount of patterns in this generator. Feel free to save or screenshot the patterns for later use, and make them in whatever technique you're comfortable (or not comfortable) in. Experiment, explore, and make to your heart's content.


If you new to SharePoint 2013 like me and get confused looking for “Sign in as a Different User” menu command, don’t worry it is not there by design but we can easily bring it back.
Do the following steps to get the link back:
Text="<%$Resources:wss,personalactions_loginasdifferentuser%>"
Description="<%$Resources:wss,personalactions_loginasdifferentuserdescription%>"
MenuGroupId="100"
Sequence="100"
UseShortId="true"
/>
Save and close the file. Now you should get the link “Sign in as Different User” back.


| Network Working Group | B. Carpenter |
| Internet-Draft | Univ. of Auckland |
| Intended status: Best Current Practice | F. Gont |
| Expires: December 1, 2020 | SI6 Networks |
| M. Richardson | |
| Sandelman Software Works | |
| May 30, 2020 |
Process for Working Group Adoption of Drafts
draft-carpenter-gendispatch-draft-adoption-00
IETF working groups often formally adopt drafts. This document specifies minimum requirements for this process, thereby extending RFC 2418. It also describes how an adopted draft may be withdrawn from the working group process.
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."
This Internet-Draft will expire on December 1, 2020.
Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
According to [RFC2418], the Internet-Drafts (I-D) mechanism is a "resource for posting and disseminating in-process copies of working group documents." However, most I-Ds start as individual contributions and only become working group documents by a WG decision generally referred to as "adoption." As noted in [RFC7221], this process was not previously documented as a formal step in the IETF WG process. This has sometimes led to confusion about the significance of such adoption and about how it fits into the IETF standards process. The present document is intended to define a few formal rules about adoption to reduce such confusion.
After a draft has been formally adopted by a WG, its original authors no longer have formal change control of the text. In addition to the normal consequence of posting a draft, i.e., that it becomes an IETF Contribution under [RFC5378], all future substantive changes to the draft require WG consensus and are no longer at the authors' sole discretion.
As a practical matter, the original authors usually continue to edit the document and make routine editorial decisions, but substantive changes must be referred to the WG and require WG rough consensus, consistently with [RFC2418]. It is also possible that new authors or editors will join the draft, or that previous authors may withdraw.
Adoption represents a commitment that the WG will spend time and effort on the draft, but it does not guarantee that the draft will reach WG consensus and be submitted to the IESG for publication as an RFC.
A WG Adoption Call of an I-D is not a required step of the IETF standards process. The WG chairs decide what documents belong in the WG, and can create new documents by fiat. A simple situation would be if a WG decides that an existing document should be split into two pieces: There is no reason to adopt each piece, that is needless bureaucracy. A WG that decides to create a design team to solve a problem has implicitely agreed to adopt the result. To not adopt the result is to say that the results of the WG mandated design team does not deserve first class agenda time. Such a design team would have been created, for instance, when a WG can not decide between two competing individual drafts and decides to merge them.
It is legitimate for a draft to be submitted to the IESG as described in [RFC2026] without a formal adoption by a WG.
If WG Chairs choose to consult the WG about adopting a document, this is the recommended process. The WG Chairs should also consider the additional guidelines in [RFC7221].
It sometimes happens that an adopted draft does not reach WG consensus to be submitted to the IESG for publication as an RFC due to lack of interest, lack of effort, or lack of consensus. In such a case, it may be desirable for the WG to formally withdraw the WG draft, such that it is explicitly removed from the WG's agenda and returned to the authors' control.
The withdrawal of WG document should be the result of an explicit decision by the relevant WG, and should follow the following recommendations.
This document makes no request of IANA.
This document should not affect the security of the Internet.
Useful comments were received from [TBD] …
| [RFC2026] | Bradner, S., "The Internet Standards Process -- Revision 3", BCP 9, RFC 2026, DOI 10.17487/RFC2026, October 1996. |
| [RFC2418] | Bradner, S., "IETF Working Group Guidelines and Procedures", BCP 25, RFC 2418, DOI 10.17487/RFC2418, September 1998. |
| [RFC5378] | Bradner, S. and J. Contreras, "Rights Contributors Provide to the IETF Trust", BCP 78, RFC 5378, DOI 10.17487/RFC5378, November 2008. |
| [RFC8179] | Bradner, S. and J. Contreras, "Intellectual Property Rights in IETF Technology", BCP 79, RFC 8179, DOI 10.17487/RFC8179, May 2017. |
| [RFC7221] | Farrel, A. and D. Crocker, "Handling of Internet-Drafts by IETF Working Groups", RFC 7221, DOI 10.17487/RFC7221, April 2014. |


|
The coconut is a common fruit found in tropical climates around the world. It originated from the coconut palm, a plant pertaining to the family of the palm ceas, which includes nearly a million varieties. Most people are familiar with 2 types of coconuts: the fresh green coconut for water and the brown ripe coconut for the meat. The first one has great quantity of water and a soft pulp, which is used for cooking to give light taste to drinks and sweets. This is in the earlier stages of development on the tree. As the coconut ripens on the tree it turns brown. It is literally dehydrating on the tree. This is usually seen in stores with the husk already removed and it looks about the same size as a grapefruit. Coconut water can be used by diabetics, since it does not contain sucrose (a type of sugar). It is prescribed by doctors around the world to help with minor and major ailments of all sorts. The health benefits are truly astounding. The ripe coconut or brown coconut has very little water, but it is rich in meat as a result. It is used as food and in foods, sweets, and desserts . Though the quantity of liquid is small in this sort, it can be consumed normally. The ripe coconut brings out the taste of the foods and can be substituted for nuts and almonds in many recipes, wi th an outstanding flavor. The coconut's husk in the brown ripe coconut is used in the manufacture of ropes, belts, carpets, and hats . It can be used in arts and crafts. It is necessary that it is dried up naturally in the sun or it will becomeshriveled . Under and after the fibrous husk is removed, the surface of the coconut is a very hard shell. The coconut shell is used to make earrings, necklaces and bracelets, cups, bowls, buttons, and even is used as a canvas for painting. Besides as an arts and craft function, the coconut's fibrous husk can be substituted as a plant fiber in the cultivation of ornamental plants like ferns, brom lias, orchids, sword of Saint Jorge, lilies, begonias, between others. The great advantage is that his cloth is rich in fibers and helps in the development of the plants, principally, of the epfitas. |
|
|
StandardExIm © 2014. all right reserved. Powered by:MadMec© |


Manage Orders
All Orders
Pending
Shipped
Delivered
All Orders
#
Customer
Issued On
Total Price
Order Status
Actions
Products
Products


Credits
A special thanks to these developers.
App Developers
-
++ Tweaks | UnlimApps
Instagram Rocket & Cercube | Majd Alfhaily
Phantom for Snapchat | CokePokes
Snapchat SCOthman | Othman Alomiry
PokeGo++ | PokeGo++
Bobby Movie & Bobby Music | BobbyMovie
CinemaBox PB | Playbox
Popcorn Time | Popcorn Time
EveryCord | EveryCord
F.lux | f.lux
iCleaner | Ivano Bilenchi
iDarkMode | iDarkMode
NFL GamePass Europe | Ghay & deltatre
iTD App | MasonD3V
App Providers
Jailbreak Developers
-
yalu102 | qwertyoruiopz
extra_recipe | Ian Beer


csonv.js
A tiny library to fetch relational CSV data at client-side just like JSON
Github repository
-
Download
ZIP
/
TAR
-
@archan937
-
@holder.nl
Click to fetch!
family.csv
books.csv
authors.csv


Changes I made to the default material theme
Hey there,
I’ve been looking for an easy to use theme for a personal site (which I will most likely not actively use) and I came across a jekyll theme called material. Jekyll is a pretty nice and easy to use static site generator. Another alternative to jekyll is hugo, which is also great. You can’t go wrong with either one.
So, the material theme is made by lukas-h and is hosted on github (https://github.com/lukas-h/material-theme). It is MIT licensed.
I’ve made some changes to it in order to use it as my own personal site. Here’s what I changed:
- Disabled google-analytics (I’m pretty averse to tracking and I effectively use browser add-ons in order to safely browse the internet. It would be pretty hypocrite of me to have GA on my own website);
- Changed to a dark/night mode (I usually prefer dark background in pretty much everything I do on the computer. Helps with vision and hides the annoying dead pixels);
- Increased paragraph’s width (I find the default width to be too narrow);
- Added support for mathjax (I come from a background where math is often used. Having support for latex is a must);
- Added an about page (The content of this page is, however, directly written in the .html file. I would like to have an about.md on the root directory instead, but I couldn’t make it work. If you know how, please let me know);
- Several changes to the Posts menu (removed the small paragraph below each post, brought the titles closer together, added “»»” for eye guidance, etc);
- Changed coloring of the buttons, when active and when inactive;
- Hid the bottom bar (I’d rather add those links in the about page);
And that is all I can remember. Please check out the original theme in https://github.com/lukas-h/material-theme. lukas-h did great here and he also has other nice themes. Check him out.
If you want to use a dark version of lukas’ material theme, with a few other changes, you can either edit lukas’ theme, as I did, or you can fork/clone my repo. Be aware, however, that I know nothing of html and css and so the code (my part) is probably a mess.


Module: ActiveRecord::Enum
| Included in: | Base |
| Defined in: |
Overview
Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example:
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.status = 1
conversation.status = "archived"
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
Scopes based on the allowed values of the enum field will be provided as well. With the above example:
Conversation.active
Conversation.archived
You can set the default value from the database declaration, like:
create_table :conversations do |t|
t.column :status, :integer, default: 0
end
Good practice is to let the first declared status be the default.
Finally, it's also possible to explicitly map the relation between attribute and database integer with a Hash:
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
Note that when an Array is used, the implicit mapping from the values to
database integers is derived from the order the values appear in the array.
In the example, :active
is mapped to 0
as
it's the first element, and :archived
is mapped to
1
. In general, the i
-th element is mapped to
i-1
in the database.
Therefore, once a value is added to the enum array, its position in the array must be maintained, and new values should only be added to the end of the array. To remove unused values, the explicit Hash syntax should be used.
In rare circumstances you might need to access the mapping directly. The mappings are exposed through a class method with the pluralized attribute name:
Conversation.statuses # => { "active" => 0, "archived" => 1 }
Use that class method when you need to know the ordinal value of an enum:
Conversation.where("status <> ?", Conversation.statuses[:archived])
Where conditions on an enum attribute must use the ordinal value of an enum.
Instance Method Summary collapse
Instance Method Details
#enum(definitions)
[ GitHub ]81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
# File 'activerecord/lib/active_record/enum.rb', line 81 def enum(definitions) klass = self definitions.each do |name, values| # statuses = { } enum_values = ActiveSupport::HashWithIndifferentAccess.new name = name.to_sym # def self.statuses statuses end detect_enum_conflict!(name, name.to_s.pluralize, true) klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values } _enum_methods_module.module_eval do # def status=(value) self[:status] = statuses[value] end klass.send(:detect_enum_conflict!, name, "#{name}=") define_method("#{name}=") { |value| if enum_values.has_key?(value) || value.blank? self[name] = enum_values[value] elsif enum_values.has_value?(value) # Assigning a value directly is not a end-user feature, hence it's not documented. # This is used internally to make building objects from the generated scopes work # as expected, i.e. Conversation.archived.build.archived? should be true. self[name] = value else raise ArgumentError, "'#{value}' is not a valid #{name}" end } # def status() statuses.key self[:status] end klass.send(:detect_enum_conflict!, name, name) define_method(name) { enum_values.key self[name] } # def status_before_type_cast() statuses.key self[:status] end klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast") define_method("#{name}_before_type_cast") { enum_values.key self[name] } pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index pairs.each do |value, i| enum_values[value] = i # def active?() status == 0 end klass.send(:detect_enum_conflict!, name, "#{value}?") define_method("#{value}?") { self[name] == i } # def active!() update! status: :active end klass.send(:detect_enum_conflict!, name, "#{value}!") define_method("#{value}!") { update! name => value } # scope :active, -> { where status: 0 } klass.send(:detect_enum_conflict!, name, value, true) klass.scope value, -> { klass.where name => i } end end defined_enums[name.to_s] = enum_values end end


`The Siege' - A Senseless Attack
You won't find a more determined and passionate FBI agent than Hubbard (Denzel Washington), who now has the very difficult task of confronting a new wave of terrorism that is spreading across New York City. But what makes his job virtually impossible in stemming the tide is that the perpetrators, in this case Arab fundamentalists, are all suicide bombers that do not hesitate to take their own lives as long as it means the utter destruction of some building or the meaningless death of innocent civilians. What's worse, these terrorist cells work independently of one another, which means that there is no way to bring down the operation in one fell swoop. The first act of aggression is truly terrifying and leaves a lump in your throat as you watch the scene unfurl. Hubbard will summon all of his resources to combat the situation.
The fact that the suspected terrorists are Arabs brings Agent Elise Kraft (Annette Benning) onto the investigation. She has special knowledge of the Arab sector, but also seems to have an ulterior motive for being there. Continually trying to circumvent Hubbard at every turn, she wants to help the determined agent, but has her own agenda to follow.
The first 40 minutes is actually quite intense, and really sets the stage for possibly a great and thrilling movie. But knowing that Hubbard would be fighting several terrorist cells, I could see that this film would suffer from a glaring weakness. And it did. Great movies always have a great villain that masterminds the evil scheme. Remember `Speed', a movie about a mad bomber and a bus (played with malicious intent by Dennis Hopper)? But here, the fundamentalists are all suicide bombers all too ready to take their own lives. Once we learn who they are, they are soon dead, either by bombing themselves or shot down by the local police. The all-important ‘hate-the-villain' factor is completely missing.
Terrorism and violence escalates. The city streets are becoming deserted. Hubbard still can not locate the remaining terrorists cells. A state of martial law is thus declared and the film completely shifts gears, now emphsizing a message over the manhunt. Suspicious of all Arabs, they are herded into interrogation camps where they are harassed and humiliated. It seemed like a strange turn of events for the audience member. I can't imagine that this movie ever intended to be a statement on our constitutional rights. If it was, it was upstaged by a marvelously intense beginning.
I give credit to Washington who infused energy into his role and Benning, who showed sass and smarts as well. Bruce Willis, however, had a thankless role as the General that oversaw the city during its state of martial law. His role asked him to do nothing but verbally punch out military rhetoric and patriotic jingoism. But the real problem with this movie is that it hits the ground running, but ultimately gets lost because the story abruptly shifts gears and because the terrorists want to die. `The Siege' starts with a blitzkrieg of suspense, but its power wanes considerably over the final 90 minutes.
Grade: C
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


I am an experienced Java Developer with Dev-ops skill set and have worked on various API migration and Middleware Migration.
Migration of Bankslice Apis to a complete new platform where group of apis are migrated to cloud by enhancing the functionality and Built robust pipline for the platform deployment.
DGW (Direct Gateway) is a middleware service which acts as a gateway between the frontend services and backend database. We have migrated DGW to AWS cloud environment for flexibility, reliable, scalable, secure and cost effective.
 In the winter, I am an avid skiier and novice ice climber. During the warmer months here in Colorado, I enjoy mountain biking, free climbing, and kayaking.
When forced indoors, I follow a number of sci-fi and fantasy genre movies and television shows, I am an aspiring chef, and I spend a large amount of my free time exploring the latest technolgy advancements in the front-end web development world.


Problem: Finding lowest common ancestor (and possibly k-th ancestor)
The problem is dead simple: you are given a tree with \(n\) vertices \((n \le 2*10^5)\), among them one distinguished vertex - root - and \(q\) queries in form \(s,a,b\) \((q \le 2*10^5)\). The problems:
- If \(s=\)'l', find \(lca(a,b)\).
- (Optional) If \(s=\)'k', find \(b-th\) ancestor of \(a\) - that is, such an ancestor \(x\) of \(a\), that \(dep(a)-dep(x) = b\).
Notice, that solving those problems can give valuable informations about any tree without distinguished root - for example, distance between two vertices \((a,b)\) in a tree is equal to \(dep(a)+dep(b) - 2*dep(lca(a,b))\).
Solution I: Binary lifting - \(O(n\log(n) + q\log(n))\) for finding lca and k-th ancestor
Preprocessing phase
What if one would keep information about 1st, 2nd, 4th, 8th, ...\(2^k\)-th ancestor of each vertex? Let \(par_k(x)\) denote he \(k\)-th ancestor of \(x\). Then:
$$ par_{2k}(x) = par_k(par_k(x)) $$
This allows to get information on each \(2^k\)-th ancestor of any vertex in \(O(n\log(n))\).
So, the preprocessing will go as follows:
- For each vertex \(x\) find out \(par_1(x)\) - using dfs.
- For each subsequent \(k\) for each vertex \(x\) find out \(par_{2^{k+1}}(x) = par_{2^k}(par_{2^k}(x))\) for all such \(k \le m\), that \(2^m \le n\) - so, \(m = \lceil \log_2(n) \rceil\).
Querying phase
There are two problems to solve:
- \(k\)-th ancestor - note, that \(par_k(x) = par_{k-2^l}(par_{2^l}(x))\) - this allows a solution based on whether a bit of \(k\) is set or not.
- \(lca(a,b)\) - effectively, it will boil down to a binary search - like approach for \(k'\)-th ancestor, where \(k' = lca(a,b)\). How to check, whether \(k'\) is an ancestor of both \(a\) and \(b\)? If candidates will be chosen among ancestors of \(a\), then the only question is: whether a vertex is an ancestor of \(b\). Note, that if \(x\) is a descendant of \(y\), then:
- \(pre(y) \le pre(x)\) - because dfs entered \(y\) before \(x\).
- \(pre(y) + son(y) \gt pre(x)\) - because all descendants of \(y\) have preorder numbers in \(\{pre(y), pre(y)+1 \dots pre(y)+son(y)-1\}\).
This allows constructing the following algorithm: search for ancestors of \(a\), from \(par_{2^m}(a)\) to \(par_1(a)\); if \(par_x(a)\) is not an ancestor of \(b\), set \(a\) to \(par_x(a)\) and continue from \(x\). At the end of the process, return \(par_1(a)\). The only corner case in need of attention: if \(a\) is an ancestor of \(b\) or \(b\) is an ancestor of \(a\), then, at the beginning of the algorithm, \(a\) or \(b\) should be returned.
{{ algo.neo_double_algorithm({'id':2, 'header_text':'Lowest common ancestor preprocessing', 'input_format':'
Input format: \(n\) is number of vertices, \(a_i, b_i\) denotes edge between \(a\) and \(b\). Vertexes need to be labeled from 1-n, there are \(n-1\) edges which need to denote a tree. \(q\) is the amount of queries.
\(n\)
\(a_1, b_1\)
\(\dots\)
\(a_{n-1}, b_{n-1}\)
',
'start_value':'8
1 2
1 3
3 4
4 5
3 6
3 7
7 8
', 'kvargos':{'input_type':'textarea'}
}, {'id':3, 'header_text':'Queries', 'input_format':'
Input format: Queries in a format: \(c, x, y\): if \(c=\)\'k\', then query will return \(y\)-th ancestor of \(x\). If \(c=\)\'l\', then query will return \(lca(x, y)\). Constraint: \(x, y \le n\).
\(c, x, y\)
'
, 'start_value':'l 8 5', 'kvargos':{} }, 'Lowest Common Ancestor with binary lifting') }}
Solution II: RMQ-based solution - \(O(n\log(n) + q)\) (depending on RMQ solution) for finding lca
Let the sequence \(path\) denote the order of processing vertices in the dfs. Each time the vertex is entered during the dfs (by moving upwards or downwards towards it), the vertex is added to a sequence \(path\). Furthermore, let \(time(x)\) denote first occurence of a vertex \(x\) in a sequence \(path\). Let's say, we are searching for \(lca(a,b)\), and \(time(a) \le time(b)\). In this representation:
- There is an index \(i\), where \(time(a) \le i \le time(b)\) and \(path_i = lca(a,b)\) - because dfs after processing vertex \(a\) had to go upwards to \(lca(a, b)\) before going downwards to find \(b\).
- The minimal depth of any vertex in sequence \(path\) between \(time(a)\) and \(time(b)\) is \(depth(lca(a,b))\) - because \(lca(a,b)\) cannot be exited (removed from dfs stack) during the dfs before reaching \(b\).
Thus, in order to find \(lca(a,b)\) you can find a vertex present in a sequence \(path\) between indexes \(time(a)\) and \(time(b)\) with minimal depth. This can be solved using RMQ - either with a segment tree, or a sparse table, or whatever.
The exact mechanics of range minimum query using the sparse table
Let's say we've got an array \(array\) and our aim is to be able to answer a question in form \(f\_min(l,r) = \min\{array_l, array_{l+1}, \dots, array_r\}\).
Our first aim is to construct a table \(sparse\) allowing us to answer queries in form \(f\_min(l, l+2^k-1)\). Let \(sparse_{k,l} = f\_min(l, l+2^k-1)\).
To start, \(sparse_{0,x} = array_{x}\).
Then, \(sparse_{k,x} = \min\{array_x, array_{x+1}, \dots, array_{x+2^k-1}\} = \min\{\min\{array_x, array_{x+1}, \dots, array_{x+2^{k-1}-1}\}, \min\{array_{x+2^{k-1}}, array_{x+2^{k-1}+1}, \dots, array_{x+2^k-1}\}\} = \min\{sparse_{k-1,x}, sparse_{k-1, x+2^{k-1}}\}\). So, if \(sparse_{k-1,h}\) is known for any \(h\), then \(sparse_{k, x}\) is easily attainable.
Note, that \(sparse_{k-1, x+2^k}\) may not exist (when \(x+2^k\) is bigger than the length of \(array\)) - in this case \(sparse_{k, x} = sparse_{k-1, x}\).
Now, in order to answer query in a form \(f\_min(l,r)\) we can find two (not necessarily distinct) intervals \(< l, l+2^k-1 >\) and \(<r-2^k+1, r>\), such that:
$$
\begin{cases}
l+2^k-1 \le r & \text{(so that no element from beyond the interval is included)}\\
l+2^k-1+1 \ge r-2^k+1 & \text{(so that all elements in the interval are included)}\\
\end{cases}
$$
Which leads to:
$$
\begin{cases}
2^k \le r-l+1 \\
2^{k+1} \ge r-l+1 \\
\end{cases}
$$
And so, \(k=\lfloor log_2(r-l+1) \rfloor\) always satisfies those demands.
Finally \(f\_min(l, r) = \min\{sparse_{k, l}, sparse_{k, r-2^k+1}\}\) with \(k=\lfloor log_2(r-l+1) \rfloor\).
To find \(\lfloor log_2(r-l+1) \rfloor\), a math library can be employed, or properties:
$$
\lfloor \log_2(1) \rfloor = 0 \\
\lfloor \log_2(x) \rfloor = \lfloor log_2(x > > 1) \rfloor + 1 \text{ for \(x \ge 2\)}
$$
can be used to create an array and use it, moving the complexity to \(O(n \log n + q)\).
Additional considerations regarding the use of range minimum query for lowest common ancestor problem
In order to use range minimum queries, the vertices need to be compared by their depths rather than by their index.
One can - instead of solving range minimum query on a path - solve queries on a preorder array. If dfs accessed vertex \(b\) after accessing vertex \(a\), then either \(lca(a, b)=a\), or \(lca(a,b)\) was accessed before accessing \(a\) (so \(pre(lca(a,b)) < pre(a)\)) and the second vertex on a path from \(lca(a,b)\) to \(b\) was accessed after accessing \(a\) - because dfs had to move upwards from \(a\), then downwards from \(lca(a,b)\) towards \(b\). Thus the parent of the vertex with lowest depth among vertices with preorder in range \(< pre(a);pre(b) >\) is \(lca(a,b)\) if \(a\) is not an ancestor of \(b\).
{{ algo.neo_double_algorithm({'id':4, 'header_text':'Lowest common ancestor preprocessing', 'input_format':'
Input format: \(n\) is number of vertices, \(a_i, b_i\) denotes edge between \(a\) and \(b\). Vertexes need to be labeled from 1-n, there are \(n-1\) edges which need to denote a tree. \(q\) is the amount of queries.
\(n\)
\(a_1, b_1\)
\(\dots\)
\(a_{n-1}, b_{n-1}\)
',
'start_value':'8
1 2
1 3
3 4
4 5
3 6
3 7
7 8
', 'kvargos':{'input_type':'textarea'}
}, {'id':5, 'header_text':'Queries', 'input_format':'
Input format: Queries in a format: \(x, y\): The query will return \(lca(x, y)\). Constraint: \(x, y \le n\).
\(x, y\)
'
, 'start_value':'8 5', 'kvargos':{} }, 'Lowest Common Ancestor with RMQ') }}


Please fill out the form in order to join! Thanks!
* Join our Site *
First Name:
Last Name:
User Name:
Email Address:
Re-enter Email Address:
Number of times you cook/bake in a week:
Date of Birth:


UPDATE: Kasus Covid-19 Indonesia Capai 1.837.126, Bertambah 5.353
Read NewsDashboard
Date and Time
19.00
Tuesday, 01 June 2021
Weather
27°C
Cloudly Sunny
Emergency Call
110
Police
Violence Case
Recent
Last Call
| No ID | Status | Phone | Location | Message | |
|---|---|---|---|---|---|
| ZP-1234 | 0812345xxxx | Bandung | gatau apaan | ||
| ZP-5678 | 0123456xxx | Zimbabwe | apaan yak |
Recent Updates
jack Menqu
October 3rd, 2018
School Website - Authentication Module.
It is a long established fact that a reader will be distracted by the readable content of a page.
Project Status
| # | Name | Due Date | Progress |
|---|---|---|---|
| 1 | Muhammad Fauzi | May 15, 2021 |
|
| 2 | Satria Rana | Jul 01, 2015 |
|
| 3 | Mahadara Aurora | Apr 12, 2015 |
|
| 4 | Bintang Kurniawan | May 15, 2015 |
|
| 5 | Iga Narendra | May 03, 2015 |
|


Forest Anderson
About me
Experience
Projects
Contact
FOREST ANDERSON
1337 Hacker
About me
I want to get a job at Evilcorp, here is my portfolio!
Experience
TODO
Projects
TODO
Contact
<EMAIL_ADDRESS>github.com/AngelOnFira
linkedin.com/in/forest-anderson


Tables
Markdown table documentation. View this page's source to inspect the markdown required to display each table.
Table Styling
Tables have basic bootstrap styling by default. To add additional style classes to the table element, use the notation {: class="table-striped table-bordered"}
on the line directly following the last row of the table. Class names should be space-separated. For full documentation, see Bootstrap Tables.
Supported Bootstrap Table Styles
table-striped
- add zebra-striping to any table row within the<tbody>
table-dark
- invert the colors with light text on dark backgroundstable-bordered
- for borders on all sides of the table and cellstable-hover
- to enable a hover state on table rows within a<tbody>
table-sm
- to make tables more compact by cutting cell padding in half
Basic table
Basic table with header and column alignment.
| Left Aligned | Centered | Right Aligned |
|---|---|---|
| Left 1 |
Center 1 |
Right 1 |
| Left 2 |
Center 2 |
Right 2 |
| Left 3 |
Center 3 |
Right 3 |
Colspan Examples
Create colspans by using empty cells to the right of the content to be spanned. Must be ||
without spaces. Using | |
will create an empty cell. Column alignment is determined by the first/leftmost spanning column.
| Column 1 | Column 2 | Column 3 | Column 4 | Column 5 |
|---|---|---|---|---|
| R1-C1 |
R1-C2 |
R1-C3 |
R1-C4 |
R1-C5 |
| R2-C1 |
R2-C2 (colspan=2) |
R2-C4 |
R2-C5 |
|
| R2-C1 |
R2-C2 (colspan=3) |
R2-C5 |
||
| R3-C1 |
R3-C2 (no colspan) |
R3-C4 |
R3-C5 |
|
| R4-C1 |
R4-C3 (no colspan, C2 is a space) |
R4-C4 |
R4-C5 |
|
| R5-C1 (colspan) |
R5-C3 |
R5-C4 |
R5-C5 |
Inline rendering
Inline markdown may be rendered inside of a table. Block elements, such as lists, are not supported.
| Feature | Syntax | Display |
|---|---|---|
| Inline code |
`var x = 2` |
var x = 2 |
| Link |
[Genesys](https://genesys.com) |
Genesys |
| Strikethrough |
~~Some Text~~ |
|
| Italic |
_Do you know who I am?_ |
Do you know who I am? |
| Bold |
**I AM THE JUGGERNAUT!** |
I AM THE JUGGERNAUT! |
| Image |
![Genesys](/img/genesys-logo-50.png) |
Multi-line table content
End a table line with \
instead of |
to make the next line append to the previous row's cells instead of creating a new row. A <br />
element will be added after the content of each cell on each line.
| Column 1 | Column 2 | Column 3 | Column 4 |
|---|---|---|---|
| R1-C1 |
R1-C2 |
R1-C3 |
R1-C4 |
| R2-C1 more R2-C1 |
R2-C2 more R2-C2 more R2-C2 again |
R3-C3 more R2-C3 |
R2-C4 More R2-C4 more R2-C4 again |
| R3-C1 more R3-C1 MORE R3-C1 |
R3-C2 More R3-C2 MORE R3-C2 First R3-C3 MORE R3-C3 |
R3-C4 MORE R3-C4 |


Hayes' last-second layup lifts Merrimack over Mass.-Lowell
by EndPlayLOWELL, Mass. (AP) - Juvaris Hayes scored on a layup with 1 second remaining to lift Merrimack to a 60-58 win over UMass Lowell in the River Hawk Invitational on Saturday.
Hayes had 10 points, eight assists and six rebounds for Merrimack (2-2). Jordan Minor added eight rebounds. Ziggy Reid had 11 points off the bench.
Christian Lutete had 24 points and 12 rebounds for the River Hawks (2-4). Obadiah Noel added six rebounds.
The River Hawk Invitational concludes Sunday when Merrimack takes on Jacksonville and UMass Lowell faces Dartmouth.
___
For more AP college basketball coverage: https://apnews.com/Collegebasketball and http://twitter.com/AP_Top25
___
Elements of this story were generated by Automated Insights, http://www.automatedinsights.com/ap, using data from STATS LLC, https://www.stats.com


Our Observatory(Fengqi Observatory) located in Hangzhou No.14 High School, Xiacheng Dist., Hangzhou, Zhejiang Province, China (30°15'59.9"N ,120°09'16.0"E,Alt=35m).
我们的天文台（凤起天文台）位于浙江省杭州第十四中学凤起校区，地理坐标：东经120°09’16.0″，北纬30°15’59.9″，海拔35米。
Fengqi Observatory was built in late 20th century, and the whole observatory was completely upgraded in 2013.
凤起天文台始建于20世纪末，在2013年对天文台进行了整体改造升级。
Here are photos which record its changes.
这些照片记录了天文台的历史变迁。
Fengqi Observatory now fixedly equips the Celetron C14 (which is the largest Schmidt-Cassegrain telescope in Hangzhou) and Paramount MX.
凤起天文台现在使用的是杭州市口径最大的施卡望远镜之一——星特朗C14望远镜，和它的赤道仪——派拉蒙MX。
There are different cameras, including QHY 9SM, QHY 8PRO, ZWO ASI174MM, etc., which can support multiple tasks for teaching, observation and astrophotography.
这里有不同种类的相机，包括QHY 9SM，QHY 8PRO，ZWO ASI174MM等，它们可以支持教学，观察和天文摄影的多个任务。
We are now undergoing network transformation and will be able to provide remote observatory services later.
我们正在对天文台进行网络化改造，之后将可以提供远程天文台服务。
We independently designed and manufactured the AstroSolar adapter of Celetron C14 which may be the largest adapter for Schmidt-Cassegrain telescope in Zhejiang Province. It enables the Celetron C14 to make high-quality solar observations during the day.
我们自主设计制作了（可能是）浙江省口径最大的施卡望远镜巴德膜，使得C14望远镜可以在白天进行高质量的太阳观测。
It can achieve a series of tasks, such as detailed shooting and tracking of sunspots, and support students to study solar activity.
实现对太阳黑子的细节拍摄、变化追踪等系列任务，为我们研究太阳活动提供支持。
There are also many mobile devices, including Skywatcher BKP250/1250, Skywatcher NEQ6, Skywatcher BKP150/750, etc.
我们还有很多可移动的设备，包括信达10寸反射望远镜、NEQ6赤道仪等。
We use them for outdoor observation activities.
这些设备一般被用于外出观测活动。


@** * Copyright (C) 2014 Johannes Unterstein<EMAIL_ADDRESS>unterstein.info * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *@ @(name: String, inputType: String, value: Option[String], error: Boolean, i18n: String = "", i18nPlaceHolder: String = "")(implicit request: play.api.mvc.Request[Any], lang: Lang)


By: billyzhang2010 原文发布于：2009-08-05 22:02:02
Virginia Woolf is remembered as both a feminist and a modernist whose novels often ignored traditional plots to follow the inner lives and musings of her characters.
Achievement as Novelist
Two of Virginia Woolf's novels in particular, Mrs. Dalloway (1925) and To the Lighthouse (1927), follow successfully the latter approach. The first novels covers a day in the life of Mrs. Dalloway in postwar London; it achieves its vision of reality through the reception by Mrs. Dalloway's mind of what Virginia Woolf called those 'myriad impressions - trivial, fantastic, evanescent, or engraved with the sharpness of steel." To the Lighthouseis, in a sense, a family portrait and history rendered in subjective depth through selected points in time. Part I deals with the time between six o'clock in the evening and dinner. Primarily through the consciousness of Mrs. Ramsay, it presents the clash of the male and female sensibilities in the family; Mrs. Ramsay functions as a means of equipoise and reconciliation. Part II: Time Passes, is a moving evocation of loss during the interval between Mrs. Ramsay's death and the family's revisit to the house. Part III moves toward completion of this intricate and subjective portrait through the adding of a last detail to a painting by an artist guest, Lily Briscoe, and through the final completion of a plan, rejected by the father in Part I, for him and the children to sail out to the lighthouse. The novel is impressionistic, subjectively perceptive, and poignant.


Man in his 30s killed after car struck a wall in Tipperary
by Fiona KellyA man has been killed and two others injured in a road traffic collision in Tipperary overnight.
Shortly after 3:00am gardai were called to the scene of a single car collision on the N24 at Killaloan on the outskirts of Clonmel.
The driver of the car, a man in his 30s, was removed to South Tipperary General Hospital where he has since been pronounced dead.
Two passengers in the car, a man and woman both in their 20s, were also taken to hospital. Their injuries are not thought to be life threatening.
The road is currently closed to traffic and local diversions are in place. Garda forensic collision investigators and crime scene examiners have been requested to examine the crash site scene. A report will be forwarded to the local Coroner.
Speaking at Clonmel Garda Station Superintendent William Leahy said: "From our initial enquiries it’s understood the car, a 05WH registered silver Volkswagen Polo, left the road and struck a wall while travelling from Clonmel in the direction of Carrick-on-Suir."
Gardai are appealing for witnesses and in particular anyone who travelling on this stretch of road between 2.45am and 3.15am this morning to contact them at Clonmel Garda Station on<PHONE_NUMBER> or the Garda Confidential Line 1800 666 111.


A practical introduction to app development on Android
Course source materials at on github.
Taught by Audrey Troutt / @auditty
You should be..
HINT: Press Down Arrow ↓ to navigate to the sub-sections below.
If you have one, create one for free.
We are going to use this for logging in to the apps we will be creating.
If you don't have it already, download and install Android Studio.
This is the editor we will use to write our apps.
Once Android Studio is installed, go to the Android SDK Manager inside Android Studio (Tools -> Android -> SDK Manager) and check two things:
Download the code for this class:
If you have an an Android phone enable developer options so that you can install and run your own apps.
An Android phone is not required for this course.
If you just can't wait to get started, here's some resources to start learning now:
HINT: This is the end of the section, press Right Arrow → to navigate to the next section.
HINT: Press ESC to enter the slide overview.
In this section you will learn how to create or import a new project into Android Studio, and build and run it on your device or emulator.
There's a lot to see and do in Android Studio. Let's take a brief tour of the parts we will use in this course.
Set the name and package for your app. Choose a project location or use the suggested directory.
Select Phone and Tablet and press Next
For now, select Empty Activity to start (it's the simplest) and press Next
Leave the default name and press Finish. Your app will be generated!
You should see this error-free code after Gradle, our build tool, finishes syncing.
In the top menu choose "Run 'app'"
Android Studio is going to ask you to "Select Deployment Target" — this means choose the device you would like to run on
PRO TIP: Check the box that says "Use same device for future launches."
You should see this on your device!
Import and run our class app for the first time.
If you haven't already,
download the source code for the first app (zip).
Alternatively, download the repo using git:
https://github.com/atroutt/android-beginners.git
Launch Android studio
Choose to open an existing Android Studio Project (File -> New -> Import Project)
Choose the AndroidBeginnersFirstApp folder on your computer.
Before you can run your first app, you need to make sure you have all the dependencies downloaded. To do that select "Sync Project with Gradle Files" from the top menu bar. This may take a minute.
In the top menu choose "Run 'app'"
Android Studio is going to ask you to "Select Deployment Target" — this means choose the device you would like to run on
PRO TIP: Check the box that says "Use same device for future launches."
If you can see this, then you are all set up!
Go ahead and snap a selfie!
Now that you have been introduced to Android Studio, let's dissect an app!
This is an app, also known as an Application
Every app has one Application class
An app usually has many screens.
These are called Activities.
Here's a screen from our app. It is defined by an Activity class you'll see later.
An Activity usually has many views
An Activity usually has many views
What does all this mean?
package com.audreytroutt.androidbeginners.firstapp;
import android.app.Application;
public class MyFirstApplication extends Application {
int toastLength = Toast.LENGTH_LONG;
@Override
public void onCreate() {
super.onCreate();
showToast("I have been created!");
}
public void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
What does all this mean?
package com.audreytroutt.androidbeginners.firstapp;
import android.app.Application; // import
public class MyFirstApplication extends Application { // class
int toastLength = Toast.LENGTH_LONG; // field
@Override
public void onCreate() { // method
super.onCreate();
showToast("I have been created!");
}
public void showToast(String message) { // method
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Anything after // is a comment in Java.
// This is a comment
Every line of code must end with a semi-colon;
int numberOfErrors = 0;
Most things in Java are object.
A class is the definition of an object in Java
// this is a class
public class MyFirstApplication extends Application {
// your fields and methods go in here
}
All of your code goes inside of classes.
A method defines the behavior of your class.
Everything your app does will be defined inside of methods inside of classes.
public void showToast(String message) { // this is a method
Toast.makeText(this, message, toastLength).show();
}
A field holds some data for your object.
public class MyFirstApplication extends Application {
int toastLength; // this is a field
public void showToast(String message) {
Toast.makeText(this, message, toastLength).show();
}
}
PRO TIP: always put your fields at the top of your class, above any methods.
Up at the top above the class body there are two things you will see: the package and import declarations.
package com.audreytroutt.androidbeginners.firstapp;
import android.app.Application;
import android.widget.Toast;
import java.util.Date;
package is like a folder path to your class--it will be auto-generated for you.
import is used to include other code into your class.
Let's dissect an app!
This is an app, also known as an Application
Your application is always represented in your Application's Manifest, AndroidManifest.xml.
The Android Manifest is an XML file that declares things like the name of your Application, what Activities it includes, and which Activity should be displayed on launch.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.audreytroutt.androidbeginners.firstapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".MyFirstApplication">
<activity
android:name=".MainActivity"
android:label="Welcome to the First App!"
<EMAIL_ADDRESS><intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".PaintingListActivity"
android:label="Paintings (List)"
android:parentActivityName=".MainActivity">
</activity>
<activity
android:name=".PaintingDetailActivity"
android:parentActivityName=".MainActivity">
</activity>
</application>
</manifest>
An instance of your Application class is created when your app launches and sticks around until your app is stopped.
You are not required to create your own Application class implementation. There is a default that works well enough for simple use cases.
If you do create your own implementation it has to extend android.app.Application. It will look something like this:
package com.audreytroutt.androidbeginners.firstapp;
import android.app.Application;
public class MyFirstApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// At this point my first application was just created
}
}
As we learned earlier, an app can have many Activities.
Here's a screen from our app.
Activities are registered in your Application Manifest
Here you can see three Activities for my app.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.audreytroutt.androidbeginners.firstapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".MyFirstApplication">
<!-- Here's the first Activity -->
<activity
android:name=".MainActivity"
android:label="Welcome to the First App!"
<EMAIL_ADDRESS><intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".PaintingListActivity"
android:label="Paintings (List)"
android:parentActivityName=".MainActivity">
</activity>
<activity
android:name=".PaintingDetailActivity"
android:parentActivityName=".MainActivity">
</activity>
</application>
</manifest>
Let's take a look at our MainActivity.
HINT: The name MainActivity can be anything you want. It's only by convention that the name has "Activity" in it. The only requirement is that the class extends Activity.
Inside an Activity class you again see an onCreate method. This is often where you set up what is displayed on the screen.
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Hello, Test!", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// Handle the camera action
} else if (id == R.id.nav_list) {
Intent listIntent = new Intent(this, PaintingListActivity.class);
startActivity(listIntent);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
}
From developer.android.com/reference/android/app/Activity.html#ProcessLifecycle
There will also be methods for any of the actions that can be taken with the views (buttons, toggles, menus, etc.) on the screen.
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.nav_camera) {
} else if (id == R.id.nav_list) {
Intent listIntent = new Intent(this, PaintingListActivity.class);
startActivity(listIntent);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
Now we can talk about how you navigate between Activities: Intents!
Navigating between Activities is done with the use of Intents, like this:
Intent listIntent = new Intent(this, PaintingListActivity.class);
startActivity(listIntent);
An Intent is a message to the Android operating system that we'd like to start the Activity named in the Intent.
HINT: Intents can only be created with a "context", which for us means an Activity. The startActivity method is defined for us in the Activity base class that we extend.
Intents are useful for more than switching between screens. They are the way to kick off all sorts of actions from your app:
Reference: common intents
Here's an example of a method that creates an intent to send an email:
public void composeEmail(String[] addresses, String subject) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:")); // only email apps
intent.putExtra(Intent.EXTRA_EMAIL, addresses);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
Here's an example of a method that creates an intent to capture a photo with the camera app:
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
When you call startActivityForResult you have to pass a code that you can use to match the response with your request.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
// A picture was just taken, let's display that in our image view
editImage();
updateMainImageFromFile();
}
}
Head over to Android Studio for our second project.
For this project, your goal is to create intents so that each of the menu items in the drawer lead to the correct screens. Open MainActivity and find onNavigationItemSelected
HINT: You can either try to figure those out from the previous lessons, or from the Android docs, or continue down for some copy and paste code snippets.
nav_camera should fire a Capture Photo Intent
if (id == R.id.nav_camera) {
// TODO Project 2: create an intent for the MediaStore.ACTION_IMAGE_CAPTURE
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getAndroidBeginnerImageUri()); // set the image file name
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
nav_list should display the PaintingListActivity
} else if (id == R.id.nav_list) {
// TODO Project 2: create an intent for the PaintingListActivity
Intent listIntent = new Intent(this, PaintingListActivity.class);
startActivity(listIntent);
}
nav_grid should display the PaintingGridActivity. This is just like nav_list, but with a different class--see if you can figure this out!
nav_web should take you to this website
} else if (id == R.id.nav_web) {
// TODO Project 2: create an intent to open a url
Uri webpage = Uri.parse("http://audreytroutt.com/android-beginners/");
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
nav_share should fire a social share Intent
I've already implemented a shareAction() method because I need it for the Floating Action Button (FAB).
} else if (id == R.id.nav_share) {
// TODO Project 2: create an intent to social share about this app
shareAction();
}
nav_send should open an email Intent
} else if (id == R.id.nav_send) {
// TODO Project 2: create an intent to send an email
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:")); // only email apps should handle this
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "gdiandroidbeginners@mailinator.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Testing out my Email Intent -- Success!");
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
Most of the code that makes apps work isn't code you write yourself--instead we use libraries.
A library will provide an API for implementing the features you need.
API stands for Application Program Interface, and it means the classes and methods that you need to use to leverage all this code that other people wrote.
Dependencies are libraries that your project needs to work.
You can add libraries manually by putting a copy of the library (usually a .jar or .aar file) in your project and instructing the gradle build script to include this file when it builds your app.
Or, you can use a dependency management system, like gradle, to automatically download and include the required libraries into your app.
Gradle is both our build tool and our dependency managment system. The build.gradle script gives instructions for how to build the app and which dependencies to download and include.
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "com.audreytroutt.androidbeginners.firstapp"
minSdkVersion 16
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
// Support libraries
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.android.support:support-v4:24.2.1'
// picasso
compile 'com.squareup.picasso:picasso:2.5.2'
// TODO Project 3: add the firebase auth dependencies here
}
Once you've added a library to your project, you need to know how to use it. There are four kinds of documentation for most libraries:
Getting started instructions usually tell you how to get the library and maybe how to initialize the main class.
Formal API documentation will list every class, method, and field in the API. Often these are in javadoc format.
Feature usage guides give you step by step instructions for how to use a specific feature of the API.
Sample apps are helpful because you can see a working example of the API.
When we use an authentication service there are typically three steps:
Firebase is a service that can do authentication AND store data.
Rather than forcing your app users to remember a new username and password you can let them log in with Google instead.
You can authenticate with Google and then use the Google success token to authenticate with Firebase.
For this project, your goal is to add a login screen to our app.
We are going to use Google to log in so that we don't have to remember any usernames or passwords.
Let's add a login screen to our app to learn more about who is using the app.
Reference: Using Firebase Auth
Add the following dependencies to your app's build.gradle file inside the dependencies{...} section
dependencies {
// ...
// TODO Project 3: add the firebase auth dependencies here
compile 'com.google.firebase:firebase-core:9.8.0'
compile 'com.google.firebase:firebase-auth:9.8.0'
compile 'com.google.android.gms:play-services-auth:9.8.0'
}
HINT: There are two build.gradle files for your project. One is labled "Module" and the other "Project". The Module one is the one we want to edit.
Add the following line to your app's build.gradle file outside the dependencies{...} section at the bottom of the file.
// Add to the bottom of the file outside of dependencies
apply plugin: 'com.google.gms.google-services'
HINT: Always sync gradle after editing build.gradle. Android Studio will remind you.
 TODO Project 3: Remove this intent when you add the LoginActivity -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Check that your LoginActivity looks like this:
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Open activity_login.xml and replace it's contents with this layout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.audreytroutt.androidbeginners.firstapp.LoginActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/cell_bg"
android:contentDescription="@string/background_image_description"
android:scaleType="fitXY"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_gravity="center_horizontal">
<com.google.android.gms.common.SignInButton
android:id="@+id/sign_in_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/logged_in_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:textSize="40sp"/>
</LinearLayout>
</FrameLayout>
Findy your LoginActivity onCreate method and set up a reference to your sign in button.
public class LoginActivity extends AppCompatActivity {
private TextView mStatusTextView;
private SignInButton mSignInButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mSignInButton = (SignInButton) findViewById(R.id.sign_in_button);
mSignInButton.setSize(SignInButton.SIZE_WIDE);
mStatusTextView = (TextView) findViewById(R.id.logged_in_name);
}
}
public class LoginActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener {
// ...
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// ... other stuff you already set up
// Set up Google Auth and request basic user profile data and email
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.d(TAG, "onConnectionFailed:" + connectionResult.getErrorMessage());
}
}
Inside onCreate also set up Firebase Authentication.
// ...
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private static final String TAG = "LoginActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// ... other stuff you already set up
// Set up Firebase Auth
mAuth = FirebaseAuth.getInstance();
mAuthListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d(TAG, "onAuthStateChanged:signed_out");
}
}
};
}
We only want to listen for authentication while the login activity is visible.
Add these methods inside your LoginActivity, after onCreate.
@Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
@Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}
Make Your LoginActivity the listener for the sign in button and implement an onClick method.
// # 1 ADD IMPLEMENTS OnClickListener
public class LoginActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener, OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
// # 3 setOnClickListener
mSignInButton.setOnClickListener(this);
// ...
}
// # 2 ADD onClick
@Override
public void onClick(View v) {
if (R.id.sign_in_button == v.getId()) {
Toast.makeText(this, "Sign In Button was pressed", Toast.LENGTH_LONG).show();
}
}
}
Use an intent! This starts a Google Auth activity.
@Override
public void onClick(View v) {
if (R.id.sign_in_button == v.getId()) {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
}
}
When the Google auth is done it will call back to our LoginActivity onActivityResult method. If successful we want to tell Firebase who you are.
private static final int RC_SIGN_IN = 111;
// ...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 1. if the result is for our sign in request
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// 2. if Google Sign In was successful, get the Google account
GoogleSignInAccount account = result.getSignInAccount();
// 3. Display the welcome text on the LoginActivity status text view
String text = getResources().getString(R.string.welcome_on_login, account.getDisplayName());
mStatusTextView.setText(text);
// 4. Hide the sign in button so that you can't sign in again
mSignInButton.setVisibility(View.INVISIBLE);
// 5. Tell Firebase to login with this Google account (this method won't exist until you create it in the next step!)
firebaseAuthWithGoogle(account);
} else {
// 6. Google Sign In failed, update status message
mStatusTextView.setText("Login Failed");
}
}
}
Use Android Studio to automatically create the missing method, firebaseAuthWithGoogle. Add the following implementation:
private void firebaseAuthWithGoogle(final GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mAuth.signInWithCredential(credential).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
if (!task.isSuccessful()) {
// Firebase login failed even though Google login was successful
Log.w(TAG, "signInWithCredential", task.getException());
Toast.makeText(LoginActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show();
mSignInButton.setVisibility(View.VISIBLE);
} else {
// Firebase login successful
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(acct.getDisplayName())
.setPhotoUri(acct.getPhotoUrl())
.build();
user.updateProfile(profileUpdates).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
// We are all done authenticating with Firebase
// TODO show the MainActivity! Do you remember how to create an intent and call startActivity?
}
}
});
}
}
});
}
In order to use Google Login you need to add your debug certificate fingerprint to our Firebase account.
Follow these instructions to get your SHA-1 developer fingerprint. It will look something like this:
SHA1: DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09
Run the app and try logging in with Google!
In MainActivity find setUserInfoInDrawer and add the following implementation:
private void setUserInfoInDrawer() {
// TODO Project 3: Get user info and display it at the top of the drawer
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
View headerView = navigationView.getHeaderView(0);
TextView currentUserEmail = (TextView) headerView.findViewById(R.id.current_user_email);
currentUserEmail.setText(user.getEmail());
TextView currentUserName = (TextView) headerView.findViewById(R.id.current_user_name);
currentUserName.setText(user.getDisplayName());
ImageView currentUserImage = (ImageView) headerView.findViewById(R.id.current_user_photo);
Picasso.with(this).load(user.getPhotoUrl()).into(currentUserImage);
}
}
It's nice to be able to sign out too! We will need to set up a Google API Client in MainActivity.
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, GoogleApiClient.OnConnectionFailedListener {
// ...
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ... other stuff you already set up
// Set up Google Auth and request basic user profile data and email
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.d(TAG, "onConnectionFailed:" + connectionResult.getErrorMessage());
}
}
I've already added a Sign Out menu in the top right of the screen. Find onOptionsItemSelected in MainActivity to implement Sign Out:
if (id == R.id.action_sign_out) {
// TODO Project 3: implement sign out
Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
Intent loginScreenIntent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(loginScreenIntent);
}
}
});
return true;
}
We already learned how to wire up a photo intent in the second project. There's more fun to be had with images, like editing them!
On Android, images are loaded into memory as Bitmaps
Bitmap bmp = BitmapFactory.decodeFile(getAndroidBeginnerImageUri().getPath(), null);
A bitmap is a representation of the image as a grid of pixels.
You can edit your Bitmap by creating a Canvas and some Paint.
Just like good old MS Paint you can switch colors and draw different shapes, all with code!
Bitmap bitmap = Bitmap.createBitmap(300, 300, null);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawCircle(150, 150, 50, paint);
The pixels are numbered by their x and y coordinates on the grid. With 0,0 at the top left.
X increases as you move to the right and Y increases as you move to the bottom.
Most shapes are drawn from the top left, but when drawing text on a bitmap by default text is drawn from the bottom left of the text (not the top left!).
HINT: The bottom of the text is known as the baseline of the text. The tails of letters like y and g hang down below the baseline, so make room for them when placing your text.
For this project, your goal is to update the snapped photo to add the words "Android Developer" on top.
In MainActivity look for the method editImage and your TODO comment there
private void editImage() {
if (!isStoragePermissionGranted()) {
requestWriteExternalStoragePermission();
}
// Load the image into memory from the file
Bitmap bmp = BitmapFactory.decodeFile(getAndroidBeginnerImageUri().getPath(), null);
// Square up the image from the camera
int croppedImageSize = (int)Math.min(bmp.getWidth(), bmp.getHeight());
Bitmap cropped = centerCropBitmapToSquareSize(bmp, croppedImageSize);
// TODO Project 4: Draw text on the cropped image
// Finally, save the edited image back to the file
saveBitmapToFile(cropped);
}
Create a Canvas from your bitmap--this is how you can "paint" on your image. Create a Paint object too!
Canvas canvas = new Canvas(cropped);
Paint paint = new Paint();
Set the size and color of the text that we want to write on the image
// I want the text to be about 1/10 as tall as the image
final int textSize = croppedImageSize / 10;
paint.setTextSize(textSize);
paint.setColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimaryDark, null));
You will need to tell your Paint where to draw the text on the image in terms of X and Y position.
X is the horizontal position of the text, relative to the left side. Y is the vertical position of the text, measured as how far the BOTTOM of the text is from the top of the image.
// it works out to start the text about 1/10 of the way into the image from the left, which is the same as our text size.
final int textXPosition = textSize;
// I want the text to be a little above the bottom of the image
final int textYPosition = croppedImageSize - (textSize / 2);
Now that your paint and variables are configured you can draw the text to your canvas! Drawing on the canvas updates the image.
final String label = getString(R.string.android_developer_image_label);
canvas.drawText(label, textXPosition, textYPosition, paint);
HINT: if you want the text to stand out a little more, try adding a shadow layer to your Paint BEFORE you draw the text. Check out paint.setShadowLayer(...)
Make sure that the call to save the edited bitmap is AFTER you are done painting on your canvas so that your changes will be there the next time you run the app.
// Save the edited image back to the file
saveBitmapToFile(cropped);
The next time you snap an image you should see your text added on top. Share that with your friends!
There are infinite ways you can edit images. Check out the Canvas and Paint clases for ideas or try these:
Android tests are based off of JUnit. There are two kinds of tests: unit tests and instrumented tests.
Normally when you create a new project Android Studio will create the unit test and instrumented test directories for you, but I have deleted them!
Switch to Project view in the Project Tool on the left and create test/java
and androidTest/java
under app/src
Cmd+click (Mac) or Ctrl+click (Windows) on test/java and Select New... -> Java Class.
Create FirstTest in package com.audreytroutt.androidbeginners.firstapp
Paste in this implementation
package com.audreytroutt.androidbeginners.firstapp;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class FirstTest {
@Test
public void trueIsTrue() {
assertThat(true, is(true));
assertEquals(true, true);
assertTrue(true);
}
@Test
public void falseIsFalse() {
assertThat(false, is(false));
assertEquals(false, false);
assertFalse(false);
}
}
We already have the junit 4.12 dependency in our project
dependencies {
...
testCompile 'junit:junit:4.12'
...
}
Cmd+click (Mac) or Ctrl+click (Windows) on FirstTest and select Run 'FirstTest'
Down in the Run tool, you should see the results of your tests
Cmd+click (Mac) or Ctrl+click (Windows) on androidTest/java and Select New... -> Java Class and create MainActivityTest in package com.audreytroutt.androidbeginners.firstapp
We need to add several dependencies to build Integrated Tests
dependencies {
...
// for Instrumented Tests
androidTestCompile 'com.android.support:support-annotations:24.2.1'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2', {
exclude group: 'com.android.support', module: 'appcompat-v7'
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'recyclerview-v7'
exclude group: 'com.android.support', module: 'design'
})
...
}
Paste in this implementation
package com.audreytroutt.androidbeginners.firstapp;
import android.support.test.filters.LargeTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerActions.open;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
/**
* ActivityTestRule will create and launch of the activity for you and also expose
* the activity under test. To get a reference to the activity you can use
* the ActivityTestRule#getActivity() method.
*/
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
@Test
public void navigateToPaintingList() throws Exception {
onView(withId(R.id.drawer_layout)).perform(open());
onView(withText("Painting List")).perform(click());
onView(withId(R.id.painting_list_recycler_view)).check(matches(isDisplayed()));
}
@Test
public void navigateToPaintingGrid() throws Exception {
onView(withId(R.id.drawer_layout)).perform(open());
onView(withText("Painting Grid")).perform(click());
onView(withId(R.id.painting_grid_recycler_view)).check(matches(isDisplayed()));
}
}
Cmd+click (Mac) or Ctrl+click (Windows) on FirstTest and select Run 'FirstTest'
Down in the Run tool, you should see the results of your tests
Usually the first way you write your code isn't the best way to write it.
Refactoring means changing the way the code is written without changing the behavior of the code.
Here are some of the most common refactoring moves and how to do them in Android Studio:
Refactoring and testing go hand and hand--in order to feel confident that you can refactor without breaking things, you need tests you can run to prove that everything is still working as expected.
Create a new class DeveloperDaysCalculator and write test for it.
package com.audreytroutt.androidbeginners.firstapp;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class DeveloperDaysCalculator {
public static long daysIveBeenAnAndroidDeveloper(Date startDate, Date today) {
final long millisecondsAsAndroidDeveloper = today.getTime() - startDate.getTime();
long daysAsAndroidDeveloper = TimeUnit.MILLISECONDS.toDays(millisecondsAsAndroidDeveloper);
return daysAsAndroidDeveloper;
}
}
Create a new unit test class DeveloperDaysCalculatorTests and write tests!
package com.audreytroutt.androidbeginners.firstapp;
import org.junit.Test;
import java.util.Calendar;
import static org.junit.Assert.assertEquals;
public class DeveloperDaysCalculatorTests {
@Test
public void calculateDaysIveBeenAnAndroidDeveloper() {
Calendar today = Calendar.getInstance();
today.set(2016, Calendar.NOVEMBER, 13, 20, 01); // 11/14/2016
// 1. TODO make a startDate
// 2. TODO call DeveloperDaysCalculator.daysIveBeenAnAndroidDeveloper
// 3. TODO make a test assertion about what the days should be
}
}
We can refactor our app to take advantage of ButterKnife!
dependencies {
compile 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}
Please complete the class survey: https://goo.gl/forms/rtVmQfki5CURi1dg1
We want your feedback!
Join the Philly Android meetup. The next meeting is November 30th.
Find me on the PhillyDev #android-philly or GDIPhilly #android slack channels!